├── README.md
├── assets
└── logo.png
├── reality
├── config.go
├── config.pb.go
├── config.proto
└── reality.go
├── splithttp
├── client.go
├── config.go
├── config.pb.go
├── config.proto
├── config_test.go
├── connection.go
├── dialer.go
├── h1_conn.go
├── hub.go
├── mux.go
├── mux_test.go
├── splithttp.go
├── splithttp_test.go
├── upload_queue.go
└── upload_queue_test.go
└── vless
├── LICENCE
├── account.go
├── encoding
├── addons.go
├── encoding.go
├── encoding.pb.go
└── encoding.proto
├── outbound
└── outbound.go
├── proxy.go
├── vless.go
└── xudp
└── xudp.go
/README.md:
--------------------------------------------------------------------------------
1 | # Vproxy
2 |
3 |
4 |
5 | [Vproxy](https://vproxy.5vnetwork.com) 是一款免费的多平台代理客户端.
6 |
7 |
8 | ## 订阅说明
9 | Vproxy获取订阅的方式与其他代理客户端一样:从订阅链接Get到内容(如果内容是base64编码,先解码),对每一行进行解析,获取节点。 目前支持:hysteria2, ss, trojan, vless, vmess
10 |
11 | 内容的第一行可以是一句自定义的说明,比如“剩余流量:10GB,5月31日到期”。该内容将显示给用户。
12 |
13 | ## 深度链接说明
14 | Vproxy支持用深度链接快捷导入订阅到客户端。深度链接的scheme为"vproxy"。目前支持两种格式:
15 |
16 | 1. vproxy://add/sub://aHR0cHM6Ly9leGFtcGxlLmNvbS9hYmNk?remarks=%E6%9C%BA%E5%9C%BA%0A
17 |
18 | 下划线为base64编码的订阅地址,解码后为"https://example.com/abcd"
19 | "remarks":订阅的名称。
20 |
21 | 2. vproxy://install-config?url=https%3A%2F%2Fexample.com%2Fabcd&name=%E6%9C%BA%E5%9C%BA%0A
22 |
23 | "name":订阅的名称
24 |
25 | 两个链接都会为用户添加一个名称为“机场”的订阅
26 |
27 | ## License Compliance
28 |
29 | The code contains in "vless", "reality" and "splithttp" folder is modified from [Xray-core](https://github.com/XTLS/Xray-core). It
30 | is distributed under the same licence(Mozilla Public License 2.0) as the original project.
31 |
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/5VNetwork/vproxy/f57d10ce72cbf43ddbee1a5939afefebb1234310/assets/logo.png
--------------------------------------------------------------------------------
/reality/config.go:
--------------------------------------------------------------------------------
1 | package reality
2 |
3 | import (
4 | "io"
5 | "os"
6 | "time"
7 |
8 | "github.com/5vnetwork/x/common/net"
9 | "github.com/rs/zerolog/log"
10 | "github.com/xtls/reality"
11 | )
12 |
13 | func (c *RealityConfig) GetREALITYConfig() *reality.Config {
14 | var dialer net.Dialer
15 | config := &reality.Config{
16 | DialContext: dialer.DialContext,
17 |
18 | // Show: c.Show,
19 | // Type: c.Type,
20 | Dest: c.Dest,
21 | Xver: byte(c.Xver),
22 |
23 | PrivateKey: c.PrivateKey,
24 | MinClientVer: c.MinClientVer,
25 | MaxClientVer: c.MaxClientVer,
26 | MaxTimeDiff: time.Duration(c.MaxTimeDiff) * time.Millisecond,
27 |
28 | NextProtos: nil, // should be nil
29 | SessionTicketsDisabled: true,
30 |
31 | KeyLogWriter: KeyLogWriterFromConfig(c),
32 | }
33 | config.ServerNames = make(map[string]bool)
34 | for _, serverName := range c.ServerNames {
35 | config.ServerNames[serverName] = true
36 | }
37 | config.ShortIds = make(map[[8]byte]bool)
38 | for _, shortId := range c.ShortIds {
39 | config.ShortIds[*(*[8]byte)(shortId)] = true
40 | }
41 | return config
42 | }
43 |
44 | func KeyLogWriterFromConfig(c *RealityConfig) io.Writer {
45 | if len(c.MasterKeyLog) <= 0 || c.MasterKeyLog == "none" {
46 | return nil
47 | }
48 |
49 | writer, err := os.OpenFile(c.MasterKeyLog, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
50 | if err != nil {
51 | log.Err(err).Msgf("failed to open %s as master key log", c.MasterKeyLog)
52 | }
53 |
54 | return writer
55 | }
56 |
--------------------------------------------------------------------------------
/reality/config.pb.go:
--------------------------------------------------------------------------------
1 | package reality
2 |
3 | import (
4 | protoreflect "google.golang.org/protobuf/reflect/protoreflect"
5 | protoimpl "google.golang.org/protobuf/runtime/protoimpl"
6 | reflect "reflect"
7 | sync "sync"
8 | )
9 |
10 | const (
11 | // Verify that this generated code is sufficiently up-to-date.
12 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
13 | // Verify that runtime/protoimpl is sufficiently up-to-date.
14 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
15 | )
16 |
17 | type RealityConfig struct {
18 | state protoimpl.MessageState
19 | sizeCache protoimpl.SizeCache
20 | unknownFields protoimpl.UnknownFields
21 |
22 | // bool show = 1;
23 | // server only
24 | Dest string `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"`
25 | // string type = 3;
26 | // server only
27 | Xver uint64 `protobuf:"varint,4,opt,name=xver,proto3" json:"xver,omitempty"`
28 | // server only
29 | ServerNames []string `protobuf:"bytes,5,rep,name=server_names,json=serverNames,proto3" json:"server_names,omitempty"`
30 | // server only
31 | PrivateKey []byte `protobuf:"bytes,6,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"`
32 | // server only
33 | MinClientVer []byte `protobuf:"bytes,7,opt,name=min_client_ver,json=minClientVer,proto3" json:"min_client_ver,omitempty"`
34 | // server only
35 | MaxClientVer []byte `protobuf:"bytes,8,opt,name=max_client_ver,json=maxClientVer,proto3" json:"max_client_ver,omitempty"`
36 | // server only
37 | // miliseconds
38 | MaxTimeDiff uint64 `protobuf:"varint,9,opt,name=max_time_diff,json=maxTimeDiff,proto3" json:"max_time_diff,omitempty"`
39 | // server only
40 | ShortIds [][]byte `protobuf:"bytes,10,rep,name=short_ids,json=shortIds,proto3" json:"short_ids,omitempty"`
41 | Fingerprint string `protobuf:"bytes,21,opt,name=Fingerprint,proto3" json:"Fingerprint,omitempty"`
42 | ServerName string `protobuf:"bytes,22,opt,name=server_name,json=serverName,proto3" json:"server_name,omitempty"`
43 | PublicKey []byte `protobuf:"bytes,23,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
44 | // must be 8 bytes
45 | ShortId []byte `protobuf:"bytes,24,opt,name=short_id,json=shortId,proto3" json:"short_id,omitempty"`
46 | SpiderX string `protobuf:"bytes,25,opt,name=spider_x,json=spiderX,proto3" json:"spider_x,omitempty"`
47 | SpiderY []int64 `protobuf:"varint,26,rep,packed,name=spider_y,json=spiderY,proto3" json:"spider_y,omitempty"`
48 | MasterKeyLog string `protobuf:"bytes,27,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"`
49 | }
50 |
51 | func (x *RealityConfig) Reset() {
52 | *x = RealityConfig{}
53 | mi := &file_transport_security_reality_config_proto_msgTypes[0]
54 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
55 | ms.StoreMessageInfo(mi)
56 | }
57 |
58 | func (x *RealityConfig) String() string {
59 | return protoimpl.X.MessageStringOf(x)
60 | }
61 |
62 | func (*RealityConfig) ProtoMessage() {}
63 |
64 | func (x *RealityConfig) ProtoReflect() protoreflect.Message {
65 | mi := &file_transport_security_reality_config_proto_msgTypes[0]
66 | if x != nil {
67 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
68 | if ms.LoadMessageInfo() == nil {
69 | ms.StoreMessageInfo(mi)
70 | }
71 | return ms
72 | }
73 | return mi.MessageOf(x)
74 | }
75 |
76 | // Deprecated: Use RealityConfig.ProtoReflect.Descriptor instead.
77 | func (*RealityConfig) Descriptor() ([]byte, []int) {
78 | return file_transport_security_reality_config_proto_rawDescGZIP(), []int{0}
79 | }
80 |
81 | func (x *RealityConfig) GetDest() string {
82 | if x != nil {
83 | return x.Dest
84 | }
85 | return ""
86 | }
87 |
88 | func (x *RealityConfig) GetXver() uint64 {
89 | if x != nil {
90 | return x.Xver
91 | }
92 | return 0
93 | }
94 |
95 | func (x *RealityConfig) GetServerNames() []string {
96 | if x != nil {
97 | return x.ServerNames
98 | }
99 | return nil
100 | }
101 |
102 | func (x *RealityConfig) GetPrivateKey() []byte {
103 | if x != nil {
104 | return x.PrivateKey
105 | }
106 | return nil
107 | }
108 |
109 | func (x *RealityConfig) GetMinClientVer() []byte {
110 | if x != nil {
111 | return x.MinClientVer
112 | }
113 | return nil
114 | }
115 |
116 | func (x *RealityConfig) GetMaxClientVer() []byte {
117 | if x != nil {
118 | return x.MaxClientVer
119 | }
120 | return nil
121 | }
122 |
123 | func (x *RealityConfig) GetMaxTimeDiff() uint64 {
124 | if x != nil {
125 | return x.MaxTimeDiff
126 | }
127 | return 0
128 | }
129 |
130 | func (x *RealityConfig) GetShortIds() [][]byte {
131 | if x != nil {
132 | return x.ShortIds
133 | }
134 | return nil
135 | }
136 |
137 | func (x *RealityConfig) GetFingerprint() string {
138 | if x != nil {
139 | return x.Fingerprint
140 | }
141 | return ""
142 | }
143 |
144 | func (x *RealityConfig) GetServerName() string {
145 | if x != nil {
146 | return x.ServerName
147 | }
148 | return ""
149 | }
150 |
151 | func (x *RealityConfig) GetPublicKey() []byte {
152 | if x != nil {
153 | return x.PublicKey
154 | }
155 | return nil
156 | }
157 |
158 | func (x *RealityConfig) GetShortId() []byte {
159 | if x != nil {
160 | return x.ShortId
161 | }
162 | return nil
163 | }
164 |
165 | func (x *RealityConfig) GetSpiderX() string {
166 | if x != nil {
167 | return x.SpiderX
168 | }
169 | return ""
170 | }
171 |
172 | func (x *RealityConfig) GetSpiderY() []int64 {
173 | if x != nil {
174 | return x.SpiderY
175 | }
176 | return nil
177 | }
178 |
179 | func (x *RealityConfig) GetMasterKeyLog() string {
180 | if x != nil {
181 | return x.MasterKeyLog
182 | }
183 | return ""
184 | }
185 |
186 | var File_transport_security_reality_config_proto protoreflect.FileDescriptor
187 |
188 | var file_transport_security_reality_config_proto_rawDesc = []byte{
189 | 0x0a, 0x27, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x73, 0x65, 0x63, 0x75,
190 | 0x72, 0x69, 0x74, 0x79, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2f, 0x63, 0x6f, 0x6e,
191 | 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x78, 0x2e, 0x74, 0x72, 0x61,
192 | 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2e,
193 | 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0xe1, 0x03, 0x0a, 0x0d, 0x52, 0x65, 0x61, 0x6c,
194 | 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73,
195 | 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a,
196 | 0x04, 0x78, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65,
197 | 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
198 | 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4e,
199 | 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f,
200 | 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61,
201 | 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6c, 0x69,
202 | 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6d,
203 | 0x69, 0x6e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x6d,
204 | 0x61, 0x78, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x18, 0x08, 0x20,
205 | 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x56, 0x65,
206 | 0x72, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x69,
207 | 0x66, 0x66, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x54, 0x69, 0x6d,
208 | 0x65, 0x44, 0x69, 0x66, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x5f, 0x69,
209 | 0x64, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x49,
210 | 0x64, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e,
211 | 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70,
212 | 0x72, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6e,
213 | 0x61, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65,
214 | 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f,
215 | 0x6b, 0x65, 0x79, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69,
216 | 0x63, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x5f, 0x69, 0x64,
217 | 0x18, 0x18, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x49, 0x64, 0x12,
218 | 0x19, 0x0a, 0x08, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x78, 0x18, 0x19, 0x20, 0x01, 0x28,
219 | 0x09, 0x52, 0x07, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x58, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70,
220 | 0x69, 0x64, 0x65, 0x72, 0x5f, 0x79, 0x18, 0x1a, 0x20, 0x03, 0x28, 0x03, 0x52, 0x07, 0x73, 0x70,
221 | 0x69, 0x64, 0x65, 0x72, 0x59, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f,
222 | 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d,
223 | 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x42, 0x33, 0x5a, 0x31, 0x67,
224 | 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x35, 0x76, 0x6e, 0x65, 0x74, 0x77,
225 | 0x6f, 0x72, 0x6b, 0x2f, 0x78, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
226 | 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79,
227 | 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
228 | }
229 |
230 | var (
231 | file_transport_security_reality_config_proto_rawDescOnce sync.Once
232 | file_transport_security_reality_config_proto_rawDescData = file_transport_security_reality_config_proto_rawDesc
233 | )
234 |
235 | func file_transport_security_reality_config_proto_rawDescGZIP() []byte {
236 | file_transport_security_reality_config_proto_rawDescOnce.Do(func() {
237 | file_transport_security_reality_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_security_reality_config_proto_rawDescData)
238 | })
239 | return file_transport_security_reality_config_proto_rawDescData
240 | }
241 |
242 | var file_transport_security_reality_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
243 | var file_transport_security_reality_config_proto_goTypes = []any{
244 | (*RealityConfig)(nil), // 0: x.transport.security.reality.RealityConfig
245 | }
246 | var file_transport_security_reality_config_proto_depIdxs = []int32{
247 | 0, // [0:0] is the sub-list for method output_type
248 | 0, // [0:0] is the sub-list for method input_type
249 | 0, // [0:0] is the sub-list for extension type_name
250 | 0, // [0:0] is the sub-list for extension extendee
251 | 0, // [0:0] is the sub-list for field type_name
252 | }
253 |
254 | func init() { file_transport_security_reality_config_proto_init() }
255 | func file_transport_security_reality_config_proto_init() {
256 | if File_transport_security_reality_config_proto != nil {
257 | return
258 | }
259 | type x struct{}
260 | out := protoimpl.TypeBuilder{
261 | File: protoimpl.DescBuilder{
262 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
263 | RawDescriptor: file_transport_security_reality_config_proto_rawDesc,
264 | NumEnums: 0,
265 | NumMessages: 1,
266 | NumExtensions: 0,
267 | NumServices: 0,
268 | },
269 | GoTypes: file_transport_security_reality_config_proto_goTypes,
270 | DependencyIndexes: file_transport_security_reality_config_proto_depIdxs,
271 | MessageInfos: file_transport_security_reality_config_proto_msgTypes,
272 | }.Build()
273 | File_transport_security_reality_config_proto = out.File
274 | file_transport_security_reality_config_proto_rawDesc = nil
275 | file_transport_security_reality_config_proto_goTypes = nil
276 | file_transport_security_reality_config_proto_depIdxs = nil
277 | }
278 |
--------------------------------------------------------------------------------
/reality/config.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package x.transport.security.reality;
4 | option go_package = "github.com/5vnetwork/x/transport/security/reality";
5 |
6 | message RealityConfig {
7 | // server only
8 | // bool show = 1;
9 | string dest = 2;
10 | // string type = 3;
11 | uint64 xver = 4;
12 | repeated string server_names = 5;
13 | bytes private_key = 6;
14 | bytes min_client_ver = 7;
15 | bytes max_client_ver = 8;
16 | // miliseconds
17 | uint64 max_time_diff = 9;
18 | repeated bytes short_ids = 10;
19 |
20 | string Fingerprint = 21;
21 | string server_name = 22;
22 | bytes public_key = 23;
23 | // must be 8 bytes
24 | bytes short_id = 24;
25 | string spider_x = 25;
26 | repeated int64 spider_y = 26;
27 | string master_key_log = 27;
28 | }
29 |
--------------------------------------------------------------------------------
/reality/reality.go:
--------------------------------------------------------------------------------
1 | package reality
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "crypto/aes"
7 | "crypto/cipher"
8 | "crypto/ecdh"
9 | "crypto/ed25519"
10 | "crypto/hmac"
11 | "crypto/rand"
12 | "crypto/sha256"
13 | "crypto/sha512"
14 | gotls "crypto/tls"
15 | "crypto/x509"
16 | "encoding/binary"
17 | "io"
18 | "math/big"
19 | "net/http"
20 | "reflect"
21 | "regexp"
22 | "strings"
23 | sync "sync"
24 | "time"
25 | "unsafe"
26 |
27 | "github.com/5vnetwork/x/common/crypto"
28 | "github.com/5vnetwork/x/common/errors"
29 | "github.com/5vnetwork/x/common/net"
30 | "github.com/5vnetwork/x/transport/security"
31 | "github.com/5vnetwork/x/transport/security/tls"
32 |
33 | utls "github.com/refraction-networking/utls"
34 | "github.com/rs/zerolog/log"
35 |
36 | "golang.org/x/crypto/hkdf"
37 | "golang.org/x/net/http2"
38 | )
39 |
40 | type Engine struct {
41 | Config *RealityConfig
42 | }
43 |
44 | func (c *Engine) GetTLSConfig(_ ...security.Option) *gotls.Config {
45 | panic("implement me")
46 | }
47 |
48 | func (c *Engine) GetClientConn(conn net.Conn, opts ...security.Option) (net.Conn, error) {
49 | var dst *net.Destination
50 | for _, o := range opts {
51 | switch v := o.(type) {
52 | case security.OptionWithDestination:
53 | dst = &v.Dest
54 | }
55 | }
56 | if dst == nil {
57 | return nil, errors.New("REALITY: no destination provided")
58 | }
59 | return UClient(conn, c.Config, context.Background(), *dst)
60 | }
61 |
62 | func (c *Engine) Listener(l net.Listener) net.Listener {
63 | panic("implement me")
64 | }
65 |
66 | var (
67 | Version_x byte = 25
68 | Version_y byte = 5
69 | Version_z byte = 16
70 | )
71 |
72 | // type Conn struct {
73 | // *reality.Conn
74 | // }
75 |
76 | // func (c *Conn) HandshakeAddress() net.Address {
77 | // if err := c.Handshake(); err != nil {
78 | // return nil
79 | // }
80 | // state := c.ConnectionState()
81 | // if state.ServerName == "" {
82 | // return nil
83 | // }
84 | // return net.ParseAddress(state.ServerName)
85 | // }
86 |
87 | // func Server(c net.Conn, config *reality.Config) (net.Conn, error) {
88 | // realityConn, err := reality.Server(context.Background(), c, config)
89 | // return &Conn{Conn: realityConn}, err
90 | // }
91 |
92 | type UConn struct {
93 | *utls.UConn
94 | ServerName string
95 | // a shared secret that is used to encrypt
96 | AuthKey []byte
97 | Verified bool
98 | }
99 |
100 | func (c *UConn) HandshakeAddress() net.Address {
101 | if err := c.Handshake(); err != nil {
102 | return nil
103 | }
104 | state := c.ConnectionState()
105 | if state.ServerName == "" {
106 | return nil
107 | }
108 | return net.ParseAddress(state.ServerName)
109 | }
110 |
111 | func (c *UConn) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
112 | p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates")
113 | certs := *(*([]*x509.Certificate))(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn)) + p.Offset))
114 | if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok {
115 | h := hmac.New(sha512.New, c.AuthKey)
116 | h.Write(pub)
117 | if bytes.Equal(h.Sum(nil), certs[0].Signature) {
118 | c.Verified = true
119 | return nil
120 | }
121 | }
122 | // at this point, it is confirmed that certs are not issued by a reality server.
123 | // try to verify original certificate (e.g. from a website)
124 | log.Warn().Msg("get a certificate from a non-reality server")
125 | opts := x509.VerifyOptions{
126 | DNSName: c.ServerName,
127 | Intermediates: x509.NewCertPool(),
128 | }
129 | for _, cert := range certs[1:] {
130 | opts.Intermediates.AddCert(cert)
131 | }
132 | if _, err := certs[0].Verify(opts); err != nil {
133 | return err
134 | }
135 | return nil
136 | }
137 |
138 | func UClient(c net.Conn, config *RealityConfig, ctx context.Context, dest net.Destination) (net.Conn, error) {
139 |
140 | localAddr := c.LocalAddr().String()
141 | uConn := &UConn{}
142 | utlsConfig := &utls.Config{
143 | VerifyPeerCertificate: uConn.VerifyPeerCertificate,
144 | ServerName: config.ServerName,
145 | InsecureSkipVerify: true,
146 | SessionTicketsDisabled: true,
147 | KeyLogWriter: KeyLogWriterFromConfig(config),
148 | }
149 | if utlsConfig.ServerName == "" {
150 | utlsConfig.ServerName = dest.Address.String()
151 | }
152 | uConn.ServerName = utlsConfig.ServerName
153 | fingerprint, err := tls.GetFingerprint(config.Fingerprint)
154 | if err != nil {
155 | return nil, errors.New("REALITY: failed to get fingerprint")
156 | }
157 | uConn.UConn = utls.UClient(c, utlsConfig, *fingerprint)
158 | {
159 | uConn.BuildHandshakeState()
160 | hello := uConn.HandshakeState.Hello
161 | hello.SessionId = make([]byte, 32)
162 |
163 | // why is this needed? hello.Raw is used as additional data might be the reason
164 | copy(hello.Raw[39:], hello.SessionId) // the fixed location of `Session ID`
165 |
166 | // copy auth info. 16 byte
167 | hello.SessionId[0] = Version_x
168 | hello.SessionId[1] = Version_y
169 | hello.SessionId[2] = Version_z
170 | hello.SessionId[3] = 0 // reserved
171 | binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))
172 | copy(hello.SessionId[8:], config.ShortId)
173 |
174 | publicKey, err := ecdh.X25519().NewPublicKey(config.PublicKey)
175 | if err != nil {
176 | return nil, errors.New("REALITY: publicKey == nil")
177 | }
178 | if uConn.HandshakeState.State13.KeyShareKeys.Ecdhe == nil {
179 | return nil, errors.New("Current fingerprint ", uConn.ClientHelloID.Client, uConn.ClientHelloID.Version, " does not support TLS 1.3, REALITY handshake cannot establish.")
180 | }
181 | // EcdheKey is a private key. Server has this shared secret(uConn.AuthKey) too
182 | uConn.AuthKey, _ = uConn.HandshakeState.State13.KeyShareKeys.Ecdhe.ECDH(publicKey)
183 | if uConn.AuthKey == nil {
184 | return nil, errors.New("REALITY: SharedKey == nil")
185 | }
186 |
187 | // get aead
188 | if _, err := hkdf.New(sha256.New, uConn.AuthKey, hello.Random[:20], []byte("REALITY")).Read(uConn.AuthKey); err != nil {
189 | return nil, err
190 | }
191 | block, _ := aes.NewCipher(uConn.AuthKey)
192 | aead, _ := cipher.NewGCM(block)
193 | aead.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw)
194 | copy(hello.Raw[39:], hello.SessionId)
195 | }
196 | if err := uConn.HandshakeContext(ctx); err != nil {
197 | return nil, err
198 | }
199 | log.Ctx(ctx).Debug().Str("localAddr", localAddr).Bool("verified", uConn.Verified).Msg("REALITY")
200 |
201 | if !uConn.Verified {
202 | go func() {
203 | client := &http.Client{
204 | Transport: &http2.Transport{
205 | DialTLSContext: func(ctx context.Context, network, addr string, cfg *gotls.Config) (net.Conn, error) {
206 | return uConn, nil
207 | },
208 | },
209 | }
210 | prefix := []byte("https://" + uConn.ServerName)
211 | maps.Lock()
212 | if maps.maps == nil {
213 | maps.maps = make(map[string]map[string]struct{})
214 | }
215 | paths := maps.maps[uConn.ServerName]
216 | if paths == nil {
217 | paths = make(map[string]struct{})
218 | paths[config.SpiderX] = struct{}{}
219 | maps.maps[uConn.ServerName] = paths
220 | }
221 | firstURL := string(prefix) + getPathLocked(paths)
222 | maps.Unlock()
223 | get := func(first bool) {
224 | var (
225 | req *http.Request
226 | resp *http.Response
227 | err error
228 | body []byte
229 | )
230 | if first {
231 | req, _ = http.NewRequest("GET", firstURL, nil)
232 | } else {
233 | maps.Lock()
234 | req, _ = http.NewRequest("GET", string(prefix)+getPathLocked(paths), nil)
235 | maps.Unlock()
236 | }
237 | if req == nil {
238 | return
239 | }
240 | req.Header.Set("User-Agent", fingerprint.Client) // TODO: User-Agent map
241 | times := 1
242 | if !first {
243 | times = int(crypto.RandBetween(config.SpiderY[4], config.SpiderY[5]))
244 | }
245 | for j := 0; j < times; j++ {
246 | if !first && j == 0 {
247 | req.Header.Set("Referer", firstURL)
248 | }
249 | req.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", int(crypto.RandBetween(config.SpiderY[0], config.SpiderY[1])))})
250 | if resp, err = client.Do(req); err != nil {
251 | break
252 | }
253 | defer resp.Body.Close()
254 | req.Header.Set("Referer", req.URL.String())
255 | if body, err = io.ReadAll(resp.Body); err != nil {
256 | break
257 | }
258 | maps.Lock()
259 | for _, m := range href.FindAllSubmatch(body, -1) {
260 | m[1] = bytes.TrimPrefix(m[1], prefix)
261 | if !bytes.Contains(m[1], dot) {
262 | paths[string(m[1])] = struct{}{}
263 | }
264 | }
265 | req.URL.Path = getPathLocked(paths)
266 | maps.Unlock()
267 | if !first {
268 | time.Sleep(time.Duration(crypto.RandBetween(config.SpiderY[6], config.SpiderY[7])) * time.Millisecond) // interval
269 | }
270 | }
271 | }
272 | get(true)
273 | concurrency := int(crypto.RandBetween(config.SpiderY[2], config.SpiderY[3]))
274 | for i := 0; i < concurrency; i++ {
275 | go get(false)
276 | }
277 | // Do not close the connection
278 | }()
279 | time.Sleep(time.Duration(crypto.RandBetween(config.SpiderY[8], config.SpiderY[9])) * time.Millisecond) // return
280 | return nil, errors.New("REALITY: processed invalid connection")
281 | }
282 | return uConn, nil
283 | }
284 |
285 | var (
286 | href = regexp.MustCompile(`href="([/h].*?)"`)
287 | dot = []byte(".")
288 | )
289 |
290 | var maps struct {
291 | sync.Mutex
292 | maps map[string]map[string]struct{}
293 | }
294 |
295 | func randBetween(left int64, right int64) int64 {
296 | if left == right {
297 | return left
298 | }
299 | bigInt, _ := rand.Int(rand.Reader, big.NewInt(right-left))
300 | return left + bigInt.Int64()
301 | }
302 |
303 | func getPathLocked(paths map[string]struct{}) string {
304 | stopAt := int(crypto.RandBetween(0, int64(len(paths)-1)))
305 | i := 0
306 | for s := range paths {
307 | if i == stopAt {
308 | return s
309 | }
310 | i++
311 | }
312 | return "/"
313 | }
314 |
--------------------------------------------------------------------------------
/splithttp/client.go:
--------------------------------------------------------------------------------
1 | package splithttp
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "fmt"
7 | "io"
8 | gonet "net"
9 | "net/http"
10 | "net/http/httptrace"
11 | "sync"
12 |
13 | "github.com/5vnetwork/x/common"
14 | "github.com/5vnetwork/x/common/errors"
15 | "github.com/5vnetwork/x/common/net"
16 | "github.com/5vnetwork/x/common/signal/done"
17 | "github.com/rs/zerolog/log"
18 | )
19 |
20 | // interface to abstract between use of browser dialer, vs net/http
21 | type DialerClient interface {
22 | IsClosed() bool
23 |
24 | // ctx, url, body, uploadOnly
25 | OpenStream(context.Context, string, io.Reader, bool) (io.ReadCloser, net.Addr, net.Addr, error)
26 |
27 | // ctx, url, body, contentLength
28 | PostPacket(context.Context, string, io.Reader, int64) error
29 | }
30 |
31 | // implements splithttp.DialerClient in terms of direct network connections
32 | type DefaultDialerClient struct {
33 | transportConfig *SplitHttpConfig
34 | client *http.Client
35 | closed bool
36 | httpVersion string
37 | // pool of net.Conn, created using dialUploadConn
38 | uploadRawPool *sync.Pool
39 | dialUploadConn func(ctxInner context.Context) (net.Conn, error)
40 | }
41 |
42 | func (c *DefaultDialerClient) IsClosed() bool {
43 | return c.closed
44 | }
45 |
46 | func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body io.Reader, uploadOnly bool) (wrc io.ReadCloser, remoteAddr, localAddr gonet.Addr, err error) {
47 | // this is done when the TCP/UDP connection to the server was established,
48 | // and we can unblock the Dial function and print correct net addresses in
49 | // logs
50 | gotConn := done.New()
51 | ctx = httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
52 | GotConn: func(connInfo httptrace.GotConnInfo) {
53 | remoteAddr = connInfo.Conn.RemoteAddr()
54 | localAddr = connInfo.Conn.LocalAddr()
55 | gotConn.Close()
56 | },
57 | })
58 |
59 | method := "GET" // stream-down
60 | if body != nil {
61 | method = "POST" // stream-up/one
62 | }
63 | req, _ := http.NewRequestWithContext(context.WithoutCancel(ctx), method, url, body)
64 | req.Header = c.transportConfig.GetRequestHeader(url)
65 | if method == "POST" && !c.transportConfig.NoGRPCHeader {
66 | req.Header.Set("Content-Type", "application/grpc")
67 | }
68 |
69 | wrc = &WaitReadCloser{Wait: make(chan struct{})}
70 | go func() {
71 | resp, err := c.client.Do(req)
72 | if err != nil {
73 | if !uploadOnly { // stream-down is enough
74 | c.closed = true
75 | log.Ctx(ctx).Error().Err(err).Msgf("failed to %s %s", method, url)
76 | }
77 | gotConn.Close()
78 | wrc.Close()
79 | return
80 | }
81 | if resp.StatusCode != 200 && !uploadOnly {
82 | log.Ctx(ctx).Info().Msgf("unexpected status %d", resp.StatusCode)
83 | }
84 | if resp.StatusCode != 200 || uploadOnly { // stream-up
85 | io.Copy(io.Discard, resp.Body)
86 | resp.Body.Close() // if it is called immediately, the upload will be interrupted also
87 | wrc.Close()
88 | return
89 | }
90 | wrc.(*WaitReadCloser).Set(resp.Body)
91 | }()
92 |
93 | <-gotConn.Wait()
94 | return
95 | }
96 |
97 | func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, body io.Reader, contentLength int64) error {
98 | req, err := http.NewRequestWithContext(context.WithoutCancel(ctx), "POST", url, body)
99 | if err != nil {
100 | return err
101 | }
102 | req.ContentLength = contentLength
103 | req.Header = c.transportConfig.GetRequestHeader(url)
104 |
105 | if c.httpVersion != "1.1" {
106 | resp, err := c.client.Do(req)
107 | if err != nil {
108 | c.closed = true
109 | return err
110 | }
111 |
112 | io.Copy(io.Discard, resp.Body)
113 | defer resp.Body.Close()
114 |
115 | if resp.StatusCode != 200 {
116 | return errors.New("bad status code:", resp.Status)
117 | }
118 | } else {
119 | // stringify the entire HTTP/1.1 request so it can be
120 | // safely retried. if instead req.Write is called multiple
121 | // times, the body is already drained after the first
122 | // request
123 | requestBuff := new(bytes.Buffer)
124 | common.Must(req.Write(requestBuff))
125 |
126 | var uploadConn any
127 | var h1UploadConn *H1Conn
128 |
129 | for {
130 | uploadConn = c.uploadRawPool.Get()
131 | newConnection := uploadConn == nil
132 | if newConnection {
133 | newConn, err := c.dialUploadConn(context.WithoutCancel(ctx))
134 | if err != nil {
135 | return err
136 | }
137 | h1UploadConn = NewH1Conn(newConn)
138 | uploadConn = h1UploadConn
139 | } else {
140 | h1UploadConn = uploadConn.(*H1Conn)
141 |
142 | // TODO: Replace 0 here with a config value later
143 | // Or add some other condition for optimization purposes
144 | if h1UploadConn.UnreadedResponsesCount > 0 {
145 | resp, err := http.ReadResponse(h1UploadConn.RespBufReader, req)
146 | if err != nil {
147 | c.closed = true
148 | return fmt.Errorf("error while reading response: %s", err.Error())
149 | }
150 | io.Copy(io.Discard, resp.Body)
151 | defer resp.Body.Close()
152 | if resp.StatusCode != 200 {
153 | return fmt.Errorf("got non-200 error response code: %d", resp.StatusCode)
154 | }
155 | }
156 | }
157 |
158 | _, err := h1UploadConn.Write(requestBuff.Bytes())
159 | // if the write failed, we try another connection from
160 | // the pool, until the write on a new connection fails.
161 | // failed writes to a pooled connection are normal when
162 | // the connection has been closed in the meantime.
163 | if err == nil {
164 | break
165 | } else if newConnection {
166 | return err
167 | }
168 | }
169 |
170 | c.uploadRawPool.Put(uploadConn)
171 | }
172 |
173 | return nil
174 | }
175 |
176 | type WaitReadCloser struct {
177 | Wait chan struct{}
178 | io.ReadCloser
179 | }
180 |
181 | func (w *WaitReadCloser) Set(rc io.ReadCloser) {
182 | w.ReadCloser = rc
183 | defer func() {
184 | if recover() != nil {
185 | rc.Close()
186 | }
187 | }()
188 | close(w.Wait)
189 | }
190 |
191 | func (w *WaitReadCloser) Read(b []byte) (int, error) {
192 | if w.ReadCloser == nil {
193 | if <-w.Wait; w.ReadCloser == nil {
194 | return 0, io.ErrClosedPipe
195 | }
196 | }
197 | return w.ReadCloser.Read(b)
198 | }
199 |
200 | func (w *WaitReadCloser) Close() error {
201 | if w.ReadCloser != nil {
202 | return w.ReadCloser.Close()
203 | }
204 | defer func() {
205 | if recover() != nil && w.ReadCloser != nil {
206 | w.ReadCloser.Close()
207 | }
208 | }()
209 | close(w.Wait)
210 | return nil
211 | }
212 |
--------------------------------------------------------------------------------
/splithttp/config.go:
--------------------------------------------------------------------------------
1 | package splithttp
2 |
3 | import (
4 | "net/http"
5 | "net/url"
6 | "strings"
7 |
8 | "github.com/5vnetwork/x/common/crypto"
9 | )
10 |
11 | func (c *SplitHttpConfig) GetNormalizedPath() string {
12 | pathAndQuery := strings.SplitN(c.Path, "?", 2)
13 | path := pathAndQuery[0]
14 |
15 | if path == "" || path[0] != '/' {
16 | path = "/" + path
17 | }
18 |
19 | if path[len(path)-1] != '/' {
20 | path = path + "/"
21 | }
22 |
23 | return path
24 | }
25 |
26 | func (c *SplitHttpConfig) GetNormalizedQuery() string {
27 | pathAndQuery := strings.SplitN(c.Path, "?", 2)
28 | query := ""
29 |
30 | if len(pathAndQuery) > 1 {
31 | query = pathAndQuery[1]
32 | }
33 |
34 | /*
35 | if query != "" {
36 | query += "&"
37 | }
38 | query += "x_version=" + core.Version()
39 | */
40 |
41 | return query
42 | }
43 |
44 | func (c *SplitHttpConfig) GetRequestHeader(rawURL string) http.Header {
45 | header := http.Header{}
46 | for k, v := range c.Headers {
47 | header.Add(k, v)
48 | }
49 |
50 | u, _ := url.Parse(rawURL)
51 | // https://www.rfc-editor.org/rfc/rfc7541.html#appendix-B
52 | // h2's HPACK Header Compression feature employs a huffman encoding using a static table.
53 | // 'X' is assigned an 8 bit code, so HPACK compression won't change actual padding length on the wire.
54 | // https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.2-2
55 | // h3's similar QPACK feature uses the same huffman table.
56 | u.RawQuery = "x_padding=" + strings.Repeat("X", int(c.GetNormalizedXPaddingBytes().rand()))
57 | header.Set("Referer", u.String())
58 |
59 | return header
60 | }
61 |
62 | func (c *SplitHttpConfig) WriteResponseHeader(writer http.ResponseWriter) {
63 | // CORS headers for the browser dialer
64 | writer.Header().Set("Access-Control-Allow-Origin", "*")
65 | writer.Header().Set("Access-Control-Allow-Methods", "GET, POST")
66 | // writer.Header().Set("X-Version", core.Version())
67 | writer.Header().Set("X-Padding", strings.Repeat("X", int(c.GetNormalizedXPaddingBytes().rand())))
68 | }
69 |
70 | func (c *SplitHttpConfig) GetNormalizedXPaddingBytes() RangeConfig {
71 | if c.XPaddingBytes == nil || c.XPaddingBytes.To == 0 {
72 | return RangeConfig{
73 | From: 100,
74 | To: 1000,
75 | }
76 | }
77 |
78 | return *c.XPaddingBytes
79 | }
80 |
81 | func (c *SplitHttpConfig) GetNormalizedScMaxEachPostBytes() RangeConfig {
82 | if c.ScMaxEachPostBytes == nil || c.ScMaxEachPostBytes.To == 0 {
83 | return RangeConfig{
84 | From: 1000000,
85 | To: 1000000,
86 | }
87 | }
88 |
89 | return *c.ScMaxEachPostBytes
90 | }
91 |
92 | func (c *SplitHttpConfig) GetNormalizedScMinPostsIntervalMs() RangeConfig {
93 | if c.ScMinPostsIntervalMs == nil || c.ScMinPostsIntervalMs.To == 0 {
94 | return RangeConfig{
95 | From: 30,
96 | To: 30,
97 | }
98 | }
99 |
100 | return *c.ScMinPostsIntervalMs
101 | }
102 |
103 | func (c *SplitHttpConfig) GetNormalizedScMaxBufferedPosts() int {
104 | if c.ScMaxBufferedPosts == 0 {
105 | return 30
106 | }
107 |
108 | return int(c.ScMaxBufferedPosts)
109 | }
110 |
111 | func (c *SplitHttpConfig) GetNormalizedScStreamUpServerSecs() RangeConfig {
112 | if c.ScStreamUpServerSecs == nil || c.ScStreamUpServerSecs.To == 0 {
113 | return RangeConfig{
114 | From: 20,
115 | To: 80,
116 | }
117 | }
118 |
119 | return *c.ScMinPostsIntervalMs
120 | }
121 |
122 | func (m *XmuxConfig) GetNormalizedMaxConcurrency() RangeConfig {
123 | if m.MaxConcurrency == nil {
124 | return RangeConfig{
125 | From: 0,
126 | To: 0,
127 | }
128 | }
129 |
130 | return *m.MaxConcurrency
131 | }
132 |
133 | func (m *XmuxConfig) GetNormalizedMaxConnections() RangeConfig {
134 | if m.MaxConnections == nil {
135 | return RangeConfig{
136 | From: 0,
137 | To: 0,
138 | }
139 | }
140 |
141 | return *m.MaxConnections
142 | }
143 |
144 | func (m *XmuxConfig) GetNormalizedCMaxReuseTimes() RangeConfig {
145 | if m.CMaxReuseTimes == nil {
146 | return RangeConfig{
147 | From: 0,
148 | To: 0,
149 | }
150 | }
151 |
152 | return *m.CMaxReuseTimes
153 | }
154 |
155 | func (m *XmuxConfig) GetNormalizedHMaxRequestTimes() RangeConfig {
156 | if m.HMaxRequestTimes == nil {
157 | return RangeConfig{
158 | From: 0,
159 | To: 0,
160 | }
161 | }
162 |
163 | return *m.HMaxRequestTimes
164 | }
165 |
166 | func (m *XmuxConfig) GetNormalizedHMaxReusableSecs() RangeConfig {
167 | if m.HMaxReusableSecs == nil {
168 | return RangeConfig{
169 | From: 0,
170 | To: 0,
171 | }
172 | }
173 |
174 | return *m.HMaxReusableSecs
175 | }
176 |
177 | func (c RangeConfig) rand() int32 {
178 | return int32(crypto.RandBetween(int64(c.From), int64(c.To)))
179 | }
180 |
--------------------------------------------------------------------------------
/splithttp/config.pb.go:
--------------------------------------------------------------------------------
1 | package splithttp
2 |
3 | import (
4 | reality "github.com/5vnetwork/x/transport/security/reality"
5 | tls "github.com/5vnetwork/x/transport/security/tls"
6 | protoreflect "google.golang.org/protobuf/reflect/protoreflect"
7 | protoimpl "google.golang.org/protobuf/runtime/protoimpl"
8 | reflect "reflect"
9 | sync "sync"
10 | )
11 |
12 | const (
13 | // Verify that this generated code is sufficiently up-to-date.
14 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
15 | // Verify that runtime/protoimpl is sufficiently up-to-date.
16 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
17 | )
18 |
19 | type RangeConfig struct {
20 | state protoimpl.MessageState
21 | sizeCache protoimpl.SizeCache
22 | unknownFields protoimpl.UnknownFields
23 |
24 | From int32 `protobuf:"varint,1,opt,name=from,proto3" json:"from,omitempty"`
25 | To int32 `protobuf:"varint,2,opt,name=to,proto3" json:"to,omitempty"`
26 | }
27 |
28 | func (x *RangeConfig) Reset() {
29 | *x = RangeConfig{}
30 | mi := &file_transport_protocols_splithttp_config_proto_msgTypes[0]
31 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
32 | ms.StoreMessageInfo(mi)
33 | }
34 |
35 | func (x *RangeConfig) String() string {
36 | return protoimpl.X.MessageStringOf(x)
37 | }
38 |
39 | func (*RangeConfig) ProtoMessage() {}
40 |
41 | func (x *RangeConfig) ProtoReflect() protoreflect.Message {
42 | mi := &file_transport_protocols_splithttp_config_proto_msgTypes[0]
43 | if x != nil {
44 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
45 | if ms.LoadMessageInfo() == nil {
46 | ms.StoreMessageInfo(mi)
47 | }
48 | return ms
49 | }
50 | return mi.MessageOf(x)
51 | }
52 |
53 | // Deprecated: Use RangeConfig.ProtoReflect.Descriptor instead.
54 | func (*RangeConfig) Descriptor() ([]byte, []int) {
55 | return file_transport_protocols_splithttp_config_proto_rawDescGZIP(), []int{0}
56 | }
57 |
58 | func (x *RangeConfig) GetFrom() int32 {
59 | if x != nil {
60 | return x.From
61 | }
62 | return 0
63 | }
64 |
65 | func (x *RangeConfig) GetTo() int32 {
66 | if x != nil {
67 | return x.To
68 | }
69 | return 0
70 | }
71 |
72 | type XmuxConfig struct {
73 | state protoimpl.MessageState
74 | sizeCache protoimpl.SizeCache
75 | unknownFields protoimpl.UnknownFields
76 |
77 | MaxConcurrency *RangeConfig `protobuf:"bytes,1,opt,name=maxConcurrency,proto3" json:"maxConcurrency,omitempty"`
78 | MaxConnections *RangeConfig `protobuf:"bytes,2,opt,name=maxConnections,proto3" json:"maxConnections,omitempty"`
79 | CMaxReuseTimes *RangeConfig `protobuf:"bytes,3,opt,name=cMaxReuseTimes,proto3" json:"cMaxReuseTimes,omitempty"`
80 | HMaxRequestTimes *RangeConfig `protobuf:"bytes,4,opt,name=hMaxRequestTimes,proto3" json:"hMaxRequestTimes,omitempty"`
81 | HMaxReusableSecs *RangeConfig `protobuf:"bytes,5,opt,name=hMaxReusableSecs,proto3" json:"hMaxReusableSecs,omitempty"`
82 | HKeepAlivePeriod int64 `protobuf:"varint,6,opt,name=hKeepAlivePeriod,proto3" json:"hKeepAlivePeriod,omitempty"`
83 | }
84 |
85 | func (x *XmuxConfig) Reset() {
86 | *x = XmuxConfig{}
87 | mi := &file_transport_protocols_splithttp_config_proto_msgTypes[1]
88 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
89 | ms.StoreMessageInfo(mi)
90 | }
91 |
92 | func (x *XmuxConfig) String() string {
93 | return protoimpl.X.MessageStringOf(x)
94 | }
95 |
96 | func (*XmuxConfig) ProtoMessage() {}
97 |
98 | func (x *XmuxConfig) ProtoReflect() protoreflect.Message {
99 | mi := &file_transport_protocols_splithttp_config_proto_msgTypes[1]
100 | if x != nil {
101 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
102 | if ms.LoadMessageInfo() == nil {
103 | ms.StoreMessageInfo(mi)
104 | }
105 | return ms
106 | }
107 | return mi.MessageOf(x)
108 | }
109 |
110 | // Deprecated: Use XmuxConfig.ProtoReflect.Descriptor instead.
111 | func (*XmuxConfig) Descriptor() ([]byte, []int) {
112 | return file_transport_protocols_splithttp_config_proto_rawDescGZIP(), []int{1}
113 | }
114 |
115 | func (x *XmuxConfig) GetMaxConcurrency() *RangeConfig {
116 | if x != nil {
117 | return x.MaxConcurrency
118 | }
119 | return nil
120 | }
121 |
122 | func (x *XmuxConfig) GetMaxConnections() *RangeConfig {
123 | if x != nil {
124 | return x.MaxConnections
125 | }
126 | return nil
127 | }
128 |
129 | func (x *XmuxConfig) GetCMaxReuseTimes() *RangeConfig {
130 | if x != nil {
131 | return x.CMaxReuseTimes
132 | }
133 | return nil
134 | }
135 |
136 | func (x *XmuxConfig) GetHMaxRequestTimes() *RangeConfig {
137 | if x != nil {
138 | return x.HMaxRequestTimes
139 | }
140 | return nil
141 | }
142 |
143 | func (x *XmuxConfig) GetHMaxReusableSecs() *RangeConfig {
144 | if x != nil {
145 | return x.HMaxReusableSecs
146 | }
147 | return nil
148 | }
149 |
150 | func (x *XmuxConfig) GetHKeepAlivePeriod() int64 {
151 | if x != nil {
152 | return x.HKeepAlivePeriod
153 | }
154 | return 0
155 | }
156 |
157 | type SplitHttpConfig struct {
158 | state protoimpl.MessageState
159 | sizeCache protoimpl.SizeCache
160 | unknownFields protoimpl.UnknownFields
161 |
162 | Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
163 | Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
164 | Mode string `protobuf:"bytes,3,opt,name=mode,proto3" json:"mode,omitempty"`
165 | Headers map[string]string `protobuf:"bytes,4,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
166 | XPaddingBytes *RangeConfig `protobuf:"bytes,5,opt,name=xPaddingBytes,proto3" json:"xPaddingBytes,omitempty"`
167 | NoGRPCHeader bool `protobuf:"varint,6,opt,name=noGRPCHeader,proto3" json:"noGRPCHeader,omitempty"`
168 | NoSSEHeader bool `protobuf:"varint,7,opt,name=noSSEHeader,proto3" json:"noSSEHeader,omitempty"`
169 | ScMaxEachPostBytes *RangeConfig `protobuf:"bytes,8,opt,name=scMaxEachPostBytes,proto3" json:"scMaxEachPostBytes,omitempty"`
170 | ScMinPostsIntervalMs *RangeConfig `protobuf:"bytes,9,opt,name=scMinPostsIntervalMs,proto3" json:"scMinPostsIntervalMs,omitempty"`
171 | ScMaxBufferedPosts int64 `protobuf:"varint,10,opt,name=scMaxBufferedPosts,proto3" json:"scMaxBufferedPosts,omitempty"`
172 | ScStreamUpServerSecs *RangeConfig `protobuf:"bytes,11,opt,name=scStreamUpServerSecs,proto3" json:"scStreamUpServerSecs,omitempty"`
173 | Xmux *XmuxConfig `protobuf:"bytes,12,opt,name=xmux,proto3" json:"xmux,omitempty"`
174 | DownloadSettings *DownConfig `protobuf:"bytes,13,opt,name=downloadSettings,proto3" json:"downloadSettings,omitempty"`
175 | }
176 |
177 | func (x *SplitHttpConfig) Reset() {
178 | *x = SplitHttpConfig{}
179 | mi := &file_transport_protocols_splithttp_config_proto_msgTypes[2]
180 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
181 | ms.StoreMessageInfo(mi)
182 | }
183 |
184 | func (x *SplitHttpConfig) String() string {
185 | return protoimpl.X.MessageStringOf(x)
186 | }
187 |
188 | func (*SplitHttpConfig) ProtoMessage() {}
189 |
190 | func (x *SplitHttpConfig) ProtoReflect() protoreflect.Message {
191 | mi := &file_transport_protocols_splithttp_config_proto_msgTypes[2]
192 | if x != nil {
193 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
194 | if ms.LoadMessageInfo() == nil {
195 | ms.StoreMessageInfo(mi)
196 | }
197 | return ms
198 | }
199 | return mi.MessageOf(x)
200 | }
201 |
202 | // Deprecated: Use SplitHttpConfig.ProtoReflect.Descriptor instead.
203 | func (*SplitHttpConfig) Descriptor() ([]byte, []int) {
204 | return file_transport_protocols_splithttp_config_proto_rawDescGZIP(), []int{2}
205 | }
206 |
207 | func (x *SplitHttpConfig) GetHost() string {
208 | if x != nil {
209 | return x.Host
210 | }
211 | return ""
212 | }
213 |
214 | func (x *SplitHttpConfig) GetPath() string {
215 | if x != nil {
216 | return x.Path
217 | }
218 | return ""
219 | }
220 |
221 | func (x *SplitHttpConfig) GetMode() string {
222 | if x != nil {
223 | return x.Mode
224 | }
225 | return ""
226 | }
227 |
228 | func (x *SplitHttpConfig) GetHeaders() map[string]string {
229 | if x != nil {
230 | return x.Headers
231 | }
232 | return nil
233 | }
234 |
235 | func (x *SplitHttpConfig) GetXPaddingBytes() *RangeConfig {
236 | if x != nil {
237 | return x.XPaddingBytes
238 | }
239 | return nil
240 | }
241 |
242 | func (x *SplitHttpConfig) GetNoGRPCHeader() bool {
243 | if x != nil {
244 | return x.NoGRPCHeader
245 | }
246 | return false
247 | }
248 |
249 | func (x *SplitHttpConfig) GetNoSSEHeader() bool {
250 | if x != nil {
251 | return x.NoSSEHeader
252 | }
253 | return false
254 | }
255 |
256 | func (x *SplitHttpConfig) GetScMaxEachPostBytes() *RangeConfig {
257 | if x != nil {
258 | return x.ScMaxEachPostBytes
259 | }
260 | return nil
261 | }
262 |
263 | func (x *SplitHttpConfig) GetScMinPostsIntervalMs() *RangeConfig {
264 | if x != nil {
265 | return x.ScMinPostsIntervalMs
266 | }
267 | return nil
268 | }
269 |
270 | func (x *SplitHttpConfig) GetScMaxBufferedPosts() int64 {
271 | if x != nil {
272 | return x.ScMaxBufferedPosts
273 | }
274 | return 0
275 | }
276 |
277 | func (x *SplitHttpConfig) GetScStreamUpServerSecs() *RangeConfig {
278 | if x != nil {
279 | return x.ScStreamUpServerSecs
280 | }
281 | return nil
282 | }
283 |
284 | func (x *SplitHttpConfig) GetXmux() *XmuxConfig {
285 | if x != nil {
286 | return x.Xmux
287 | }
288 | return nil
289 | }
290 |
291 | func (x *SplitHttpConfig) GetDownloadSettings() *DownConfig {
292 | if x != nil {
293 | return x.DownloadSettings
294 | }
295 | return nil
296 | }
297 |
298 | type DownConfig struct {
299 | state protoimpl.MessageState
300 | sizeCache protoimpl.SizeCache
301 | unknownFields protoimpl.UnknownFields
302 |
303 | Address string `protobuf:"bytes,8,opt,name=address,proto3" json:"address,omitempty"`
304 | Port uint32 `protobuf:"varint,9,opt,name=port,proto3" json:"port,omitempty"`
305 | XhttpConfig *SplitHttpConfig `protobuf:"bytes,2,opt,name=xhttp_config,json=xhttpConfig,proto3" json:"xhttp_config,omitempty"`
306 | // Types that are assignable to Security:
307 | //
308 | // *DownConfig_Tls
309 | // *DownConfig_Reality
310 | Security isDownConfig_Security `protobuf_oneof:"security"`
311 | }
312 |
313 | func (x *DownConfig) Reset() {
314 | *x = DownConfig{}
315 | mi := &file_transport_protocols_splithttp_config_proto_msgTypes[3]
316 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
317 | ms.StoreMessageInfo(mi)
318 | }
319 |
320 | func (x *DownConfig) String() string {
321 | return protoimpl.X.MessageStringOf(x)
322 | }
323 |
324 | func (*DownConfig) ProtoMessage() {}
325 |
326 | func (x *DownConfig) ProtoReflect() protoreflect.Message {
327 | mi := &file_transport_protocols_splithttp_config_proto_msgTypes[3]
328 | if x != nil {
329 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
330 | if ms.LoadMessageInfo() == nil {
331 | ms.StoreMessageInfo(mi)
332 | }
333 | return ms
334 | }
335 | return mi.MessageOf(x)
336 | }
337 |
338 | // Deprecated: Use DownConfig.ProtoReflect.Descriptor instead.
339 | func (*DownConfig) Descriptor() ([]byte, []int) {
340 | return file_transport_protocols_splithttp_config_proto_rawDescGZIP(), []int{3}
341 | }
342 |
343 | func (x *DownConfig) GetAddress() string {
344 | if x != nil {
345 | return x.Address
346 | }
347 | return ""
348 | }
349 |
350 | func (x *DownConfig) GetPort() uint32 {
351 | if x != nil {
352 | return x.Port
353 | }
354 | return 0
355 | }
356 |
357 | func (x *DownConfig) GetXhttpConfig() *SplitHttpConfig {
358 | if x != nil {
359 | return x.XhttpConfig
360 | }
361 | return nil
362 | }
363 |
364 | func (m *DownConfig) GetSecurity() isDownConfig_Security {
365 | if m != nil {
366 | return m.Security
367 | }
368 | return nil
369 | }
370 |
371 | func (x *DownConfig) GetTls() *tls.TlsConfig {
372 | if x, ok := x.GetSecurity().(*DownConfig_Tls); ok {
373 | return x.Tls
374 | }
375 | return nil
376 | }
377 |
378 | func (x *DownConfig) GetReality() *reality.RealityConfig {
379 | if x, ok := x.GetSecurity().(*DownConfig_Reality); ok {
380 | return x.Reality
381 | }
382 | return nil
383 | }
384 |
385 | type isDownConfig_Security interface {
386 | isDownConfig_Security()
387 | }
388 |
389 | type DownConfig_Tls struct {
390 | Tls *tls.TlsConfig `protobuf:"bytes,20,opt,name=tls,proto3,oneof"`
391 | }
392 |
393 | type DownConfig_Reality struct {
394 | Reality *reality.RealityConfig `protobuf:"bytes,21,opt,name=reality,proto3,oneof"`
395 | }
396 |
397 | func (*DownConfig_Tls) isDownConfig_Security() {}
398 |
399 | func (*DownConfig_Reality) isDownConfig_Security() {}
400 |
401 | var File_transport_protocols_splithttp_config_proto protoreflect.FileDescriptor
402 |
403 | var file_transport_protocols_splithttp_config_proto_rawDesc = []byte{
404 | 0x0a, 0x2a, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74,
405 | 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2f,
406 | 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x78, 0x2e,
407 | 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
408 | 0x6f, 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x1a, 0x14, 0x70,
409 | 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x74, 0x6c, 0x73, 0x2f, 0x74, 0x6c, 0x73, 0x2e, 0x70, 0x72,
410 | 0x6f, 0x74, 0x6f, 0x1a, 0x27, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x73,
411 | 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2f,
412 | 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x31, 0x0a, 0x0b,
413 | 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66,
414 | 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12,
415 | 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x74, 0x6f, 0x22,
416 | 0xee, 0x03, 0x0a, 0x0a, 0x58, 0x6d, 0x75, 0x78, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x54,
417 | 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79,
418 | 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
419 | 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2e, 0x73,
420 | 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f,
421 | 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72,
422 | 0x65, 0x6e, 0x63, 0x79, 0x12, 0x54, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
423 | 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78,
424 | 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
425 | 0x63, 0x6f, 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52,
426 | 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43,
427 | 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x54, 0x0a, 0x0e, 0x63, 0x4d,
428 | 0x61, 0x78, 0x52, 0x65, 0x75, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01,
429 | 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
430 | 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74,
431 | 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
432 | 0x52, 0x0e, 0x63, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x75, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73,
433 | 0x12, 0x58, 0x0a, 0x10, 0x68, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54,
434 | 0x69, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x2e, 0x74,
435 | 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
436 | 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e,
437 | 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x68, 0x4d, 0x61, 0x78, 0x52, 0x65,
438 | 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x10, 0x68, 0x4d,
439 | 0x61, 0x78, 0x52, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x73, 0x18, 0x05,
440 | 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
441 | 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c,
442 | 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66,
443 | 0x69, 0x67, 0x52, 0x10, 0x68, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65,
444 | 0x53, 0x65, 0x63, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x68, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69,
445 | 0x76, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10,
446 | 0x68, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64,
447 | 0x22, 0xe8, 0x06, 0x0a, 0x0f, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x48, 0x74, 0x74, 0x70, 0x43, 0x6f,
448 | 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01,
449 | 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68,
450 | 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04,
451 | 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65,
452 | 0x12, 0x57, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,
453 | 0x0b, 0x32, 0x3d, 0x2e, 0x78, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
454 | 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68,
455 | 0x74, 0x74, 0x70, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x48, 0x74, 0x74, 0x70, 0x43, 0x6f, 0x6e,
456 | 0x66, 0x69, 0x67, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
457 | 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x52, 0x0a, 0x0d, 0x78, 0x50, 0x61,
458 | 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
459 | 0x32, 0x2c, 0x2e, 0x78, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70,
460 | 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74,
461 | 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d,
462 | 0x78, 0x50, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22, 0x0a,
463 | 0x0c, 0x6e, 0x6f, 0x47, 0x52, 0x50, 0x43, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20,
464 | 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x6f, 0x47, 0x52, 0x50, 0x43, 0x48, 0x65, 0x61, 0x64, 0x65,
465 | 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x6e, 0x6f, 0x53, 0x53, 0x45, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
466 | 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f, 0x53, 0x53, 0x45, 0x48, 0x65, 0x61,
467 | 0x64, 0x65, 0x72, 0x12, 0x5c, 0x0a, 0x12, 0x73, 0x63, 0x4d, 0x61, 0x78, 0x45, 0x61, 0x63, 0x68,
468 | 0x50, 0x6f, 0x73, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32,
469 | 0x2c, 0x2e, 0x78, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72,
470 | 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74,
471 | 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x12, 0x73,
472 | 0x63, 0x4d, 0x61, 0x78, 0x45, 0x61, 0x63, 0x68, 0x50, 0x6f, 0x73, 0x74, 0x42, 0x79, 0x74, 0x65,
473 | 0x73, 0x12, 0x60, 0x0a, 0x14, 0x73, 0x63, 0x4d, 0x69, 0x6e, 0x50, 0x6f, 0x73, 0x74, 0x73, 0x49,
474 | 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32,
475 | 0x2c, 0x2e, 0x78, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72,
476 | 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74,
477 | 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x14, 0x73,
478 | 0x63, 0x4d, 0x69, 0x6e, 0x50, 0x6f, 0x73, 0x74, 0x73, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,
479 | 0x6c, 0x4d, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x73, 0x63, 0x4d, 0x61, 0x78, 0x42, 0x75, 0x66, 0x66,
480 | 0x65, 0x72, 0x65, 0x64, 0x50, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52,
481 | 0x12, 0x73, 0x63, 0x4d, 0x61, 0x78, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x65, 0x64, 0x50, 0x6f,
482 | 0x73, 0x74, 0x73, 0x12, 0x60, 0x0a, 0x14, 0x73, 0x63, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55,
483 | 0x70, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x63, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28,
484 | 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
485 | 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68,
486 | 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
487 | 0x14, 0x73, 0x63, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x70, 0x53, 0x65, 0x72, 0x76, 0x65,
488 | 0x72, 0x53, 0x65, 0x63, 0x73, 0x12, 0x3f, 0x0a, 0x04, 0x78, 0x6d, 0x75, 0x78, 0x18, 0x0c, 0x20,
489 | 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
490 | 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c, 0x69,
491 | 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x58, 0x6d, 0x75, 0x78, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
492 | 0x52, 0x04, 0x78, 0x6d, 0x75, 0x78, 0x12, 0x57, 0x0a, 0x10, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f,
493 | 0x61, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b,
494 | 0x32, 0x2b, 0x2e, 0x78, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70,
495 | 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74,
496 | 0x74, 0x70, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x64,
497 | 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x1a,
498 | 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
499 | 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
500 | 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
501 | 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8a, 0x02, 0x0a, 0x0a,
502 | 0x44, 0x6f, 0x77, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64,
503 | 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64,
504 | 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x09, 0x20, 0x01,
505 | 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x53, 0x0a, 0x0c, 0x78, 0x68, 0x74, 0x74,
506 | 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30,
507 | 0x2e, 0x78, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f,
508 | 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70,
509 | 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x48, 0x74, 0x74, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
510 | 0x52, 0x0b, 0x78, 0x68, 0x74, 0x74, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a,
511 | 0x03, 0x74, 0x6c, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x78, 0x2e, 0x74,
512 | 0x6c, 0x73, 0x2e, 0x54, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x03,
513 | 0x74, 0x6c, 0x73, 0x12, 0x47, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x15,
514 | 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
515 | 0x72, 0x74, 0x2e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2e, 0x72, 0x65, 0x61, 0x6c,
516 | 0x69, 0x74, 0x79, 0x2e, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69,
517 | 0x67, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x0a, 0x0a, 0x08,
518 | 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68,
519 | 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x35, 0x76, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
520 | 0x2f, 0x78, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x70, 0x72, 0x6f,
521 | 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70,
522 | 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
523 | }
524 |
525 | var (
526 | file_transport_protocols_splithttp_config_proto_rawDescOnce sync.Once
527 | file_transport_protocols_splithttp_config_proto_rawDescData = file_transport_protocols_splithttp_config_proto_rawDesc
528 | )
529 |
530 | func file_transport_protocols_splithttp_config_proto_rawDescGZIP() []byte {
531 | file_transport_protocols_splithttp_config_proto_rawDescOnce.Do(func() {
532 | file_transport_protocols_splithttp_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_protocols_splithttp_config_proto_rawDescData)
533 | })
534 | return file_transport_protocols_splithttp_config_proto_rawDescData
535 | }
536 |
537 | var file_transport_protocols_splithttp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
538 | var file_transport_protocols_splithttp_config_proto_goTypes = []any{
539 | (*RangeConfig)(nil), // 0: x.transport.protocols.splithttp.RangeConfig
540 | (*XmuxConfig)(nil), // 1: x.transport.protocols.splithttp.XmuxConfig
541 | (*SplitHttpConfig)(nil), // 2: x.transport.protocols.splithttp.SplitHttpConfig
542 | (*DownConfig)(nil), // 3: x.transport.protocols.splithttp.DownConfig
543 | nil, // 4: x.transport.protocols.splithttp.SplitHttpConfig.HeadersEntry
544 | (*tls.TlsConfig)(nil), // 5: x.tls.TlsConfig
545 | (*reality.RealityConfig)(nil), // 6: x.transport.security.reality.RealityConfig
546 | }
547 | var file_transport_protocols_splithttp_config_proto_depIdxs = []int32{
548 | 0, // 0: x.transport.protocols.splithttp.XmuxConfig.maxConcurrency:type_name -> x.transport.protocols.splithttp.RangeConfig
549 | 0, // 1: x.transport.protocols.splithttp.XmuxConfig.maxConnections:type_name -> x.transport.protocols.splithttp.RangeConfig
550 | 0, // 2: x.transport.protocols.splithttp.XmuxConfig.cMaxReuseTimes:type_name -> x.transport.protocols.splithttp.RangeConfig
551 | 0, // 3: x.transport.protocols.splithttp.XmuxConfig.hMaxRequestTimes:type_name -> x.transport.protocols.splithttp.RangeConfig
552 | 0, // 4: x.transport.protocols.splithttp.XmuxConfig.hMaxReusableSecs:type_name -> x.transport.protocols.splithttp.RangeConfig
553 | 4, // 5: x.transport.protocols.splithttp.SplitHttpConfig.headers:type_name -> x.transport.protocols.splithttp.SplitHttpConfig.HeadersEntry
554 | 0, // 6: x.transport.protocols.splithttp.SplitHttpConfig.xPaddingBytes:type_name -> x.transport.protocols.splithttp.RangeConfig
555 | 0, // 7: x.transport.protocols.splithttp.SplitHttpConfig.scMaxEachPostBytes:type_name -> x.transport.protocols.splithttp.RangeConfig
556 | 0, // 8: x.transport.protocols.splithttp.SplitHttpConfig.scMinPostsIntervalMs:type_name -> x.transport.protocols.splithttp.RangeConfig
557 | 0, // 9: x.transport.protocols.splithttp.SplitHttpConfig.scStreamUpServerSecs:type_name -> x.transport.protocols.splithttp.RangeConfig
558 | 1, // 10: x.transport.protocols.splithttp.SplitHttpConfig.xmux:type_name -> x.transport.protocols.splithttp.XmuxConfig
559 | 3, // 11: x.transport.protocols.splithttp.SplitHttpConfig.downloadSettings:type_name -> x.transport.protocols.splithttp.DownConfig
560 | 2, // 12: x.transport.protocols.splithttp.DownConfig.xhttp_config:type_name -> x.transport.protocols.splithttp.SplitHttpConfig
561 | 5, // 13: x.transport.protocols.splithttp.DownConfig.tls:type_name -> x.tls.TlsConfig
562 | 6, // 14: x.transport.protocols.splithttp.DownConfig.reality:type_name -> x.transport.security.reality.RealityConfig
563 | 15, // [15:15] is the sub-list for method output_type
564 | 15, // [15:15] is the sub-list for method input_type
565 | 15, // [15:15] is the sub-list for extension type_name
566 | 15, // [15:15] is the sub-list for extension extendee
567 | 0, // [0:15] is the sub-list for field type_name
568 | }
569 |
570 | func init() { file_transport_protocols_splithttp_config_proto_init() }
571 | func file_transport_protocols_splithttp_config_proto_init() {
572 | if File_transport_protocols_splithttp_config_proto != nil {
573 | return
574 | }
575 | file_transport_protocols_splithttp_config_proto_msgTypes[3].OneofWrappers = []any{
576 | (*DownConfig_Tls)(nil),
577 | (*DownConfig_Reality)(nil),
578 | }
579 | type x struct{}
580 | out := protoimpl.TypeBuilder{
581 | File: protoimpl.DescBuilder{
582 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
583 | RawDescriptor: file_transport_protocols_splithttp_config_proto_rawDesc,
584 | NumEnums: 0,
585 | NumMessages: 5,
586 | NumExtensions: 0,
587 | NumServices: 0,
588 | },
589 | GoTypes: file_transport_protocols_splithttp_config_proto_goTypes,
590 | DependencyIndexes: file_transport_protocols_splithttp_config_proto_depIdxs,
591 | MessageInfos: file_transport_protocols_splithttp_config_proto_msgTypes,
592 | }.Build()
593 | File_transport_protocols_splithttp_config_proto = out.File
594 | file_transport_protocols_splithttp_config_proto_rawDesc = nil
595 | file_transport_protocols_splithttp_config_proto_goTypes = nil
596 | file_transport_protocols_splithttp_config_proto_depIdxs = nil
597 | }
598 |
--------------------------------------------------------------------------------
/splithttp/config.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package x.transport.protocols.splithttp;
4 | option go_package = "github.com/5vnetwork/x/transport/protocols/splithttp";
5 |
6 | import "protos/tls/tls.proto";
7 | import "transport/security/reality/config.proto";
8 |
9 | message RangeConfig {
10 | int32 from = 1;
11 | int32 to = 2;
12 | }
13 |
14 | message XmuxConfig {
15 | RangeConfig maxConcurrency = 1;
16 | RangeConfig maxConnections = 2;
17 | RangeConfig cMaxReuseTimes = 3;
18 | RangeConfig hMaxRequestTimes = 4;
19 | RangeConfig hMaxReusableSecs = 5;
20 | int64 hKeepAlivePeriod = 6;
21 | }
22 |
23 | message SplitHttpConfig {
24 | string host = 1;
25 | string path = 2;
26 | string mode = 3;
27 | map headers = 4;
28 | RangeConfig xPaddingBytes = 5;
29 | bool noGRPCHeader = 6;
30 | bool noSSEHeader = 7;
31 | RangeConfig scMaxEachPostBytes = 8;
32 | RangeConfig scMinPostsIntervalMs = 9;
33 | int64 scMaxBufferedPosts = 10;
34 | RangeConfig scStreamUpServerSecs = 11;
35 | XmuxConfig xmux = 12;
36 | DownConfig downloadSettings = 13;
37 | }
38 |
39 | message DownConfig {
40 | string address = 8;
41 | uint32 port = 9;
42 | SplitHttpConfig xhttp_config = 2;
43 | oneof security {
44 | x.tls.TlsConfig tls = 20;
45 | x.transport.security.reality.RealityConfig reality = 21;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/splithttp/config_test.go:
--------------------------------------------------------------------------------
1 | package splithttp_test
2 |
3 | import (
4 | "testing"
5 |
6 | . "github.com/5vnetwork/x/transport/protocols/splithttp"
7 | )
8 |
9 | func Test_GetNormalizedPath(t *testing.T) {
10 | c := SplitHttpConfig{
11 | Path: "/?world",
12 | }
13 |
14 | path := c.GetNormalizedPath()
15 | if path != "/" {
16 | t.Error("Unexpected: ", path)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/splithttp/connection.go:
--------------------------------------------------------------------------------
1 | package splithttp
2 |
3 | import (
4 | "io"
5 | "net"
6 | "time"
7 | )
8 |
9 | type splitConn struct {
10 | writer io.WriteCloser
11 | reader io.ReadCloser
12 | remoteAddr net.Addr
13 | localAddr net.Addr
14 | onClose func()
15 | }
16 |
17 | func (c *splitConn) Write(b []byte) (int, error) {
18 | return c.writer.Write(b)
19 | }
20 |
21 | func (c *splitConn) Read(b []byte) (int, error) {
22 | return c.reader.Read(b)
23 | }
24 |
25 | func (c *splitConn) Close() error {
26 | if c.onClose != nil {
27 | c.onClose()
28 | }
29 |
30 | err := c.writer.Close()
31 | err2 := c.reader.Close()
32 | if err != nil {
33 | return err
34 | }
35 |
36 | if err2 != nil {
37 | return err
38 | }
39 |
40 | return nil
41 | }
42 |
43 | func (c *splitConn) LocalAddr() net.Addr {
44 | return c.localAddr
45 | }
46 |
47 | func (c *splitConn) RemoteAddr() net.Addr {
48 | return c.remoteAddr
49 | }
50 |
51 | func (c *splitConn) SetDeadline(t time.Time) error {
52 | // TODO cannot do anything useful
53 | return nil
54 | }
55 |
56 | func (c *splitConn) SetReadDeadline(t time.Time) error {
57 | // TODO cannot do anything useful
58 | return nil
59 | }
60 |
61 | func (c *splitConn) SetWriteDeadline(t time.Time) error {
62 | // TODO cannot do anything useful
63 | return nil
64 | }
65 |
--------------------------------------------------------------------------------
/splithttp/dialer.go:
--------------------------------------------------------------------------------
1 | package splithttp
2 |
3 | import (
4 | "context"
5 | gotls "crypto/tls"
6 | "fmt"
7 | "io"
8 | "math/rand"
9 | "net/http"
10 | "net/http/httptrace"
11 | "net/url"
12 | "strconv"
13 | "sync"
14 | "sync/atomic"
15 | "time"
16 |
17 | "github.com/5vnetwork/x/common/buf"
18 | "github.com/5vnetwork/x/common/net"
19 | "github.com/5vnetwork/x/common/pipe"
20 | "github.com/5vnetwork/x/common/signal/done"
21 | "github.com/5vnetwork/x/common/uuid"
22 | "github.com/5vnetwork/x/transport/dlhelper"
23 | "github.com/5vnetwork/x/transport/security"
24 | "github.com/5vnetwork/x/transport/security/reality"
25 | "github.com/5vnetwork/x/transport/security/tls"
26 | "github.com/quic-go/quic-go"
27 | "github.com/quic-go/quic-go/http3"
28 | "golang.org/x/net/http2"
29 | )
30 |
31 | type dialerConf struct {
32 | net.Destination
33 | config *SplitHttpConfig
34 | sc security.Engine
35 | socketConfig *dlhelper.SocketSetting
36 | }
37 |
38 | var (
39 | globalDialerMap map[dialerConf]*XmuxManager
40 | globalDialerAccess sync.Mutex
41 | )
42 |
43 | func getHTTPClient(ctx context.Context, dest net.Destination, config *SplitHttpConfig, sc security.Engine, socketConfig *dlhelper.SocketSetting) (DialerClient, *XmuxClient) {
44 | globalDialerAccess.Lock()
45 | defer globalDialerAccess.Unlock()
46 |
47 | if globalDialerMap == nil {
48 | globalDialerMap = make(map[dialerConf]*XmuxManager)
49 | }
50 |
51 | key := dialerConf{dest, config, sc, socketConfig}
52 |
53 | xmuxManager, found := globalDialerMap[key]
54 |
55 | if !found {
56 | transportConfig := config
57 | var xmuxConfig XmuxConfig
58 | if transportConfig.Xmux != nil {
59 | xmuxConfig = *transportConfig.Xmux
60 | }
61 |
62 | xmuxManager = NewXmuxManager(xmuxConfig, func() XmuxConn {
63 | return createHTTPClient(dest, config, sc, socketConfig)
64 | })
65 | globalDialerMap[key] = xmuxManager
66 | }
67 |
68 | xmuxClient := xmuxManager.GetXmuxClient(ctx)
69 | return xmuxClient.XmuxConn.(DialerClient), xmuxClient
70 | }
71 |
72 | func decideHTTPVersion(tlsConfig *tls.TlsConfig, realityConfig *reality.RealityConfig) string {
73 | if realityConfig != nil {
74 | return "2"
75 | }
76 | if tlsConfig == nil {
77 | return "1.1"
78 | }
79 | if len(tlsConfig.NextProtocol) != 1 {
80 | return "2"
81 | }
82 | if tlsConfig.NextProtocol[0] == "http/1.1" {
83 | return "1.1"
84 | }
85 | if tlsConfig.NextProtocol[0] == "h3" {
86 | return "3"
87 | }
88 | return "2"
89 | }
90 |
91 | // consistent with quic-go
92 | const QuicgoH3KeepAlivePeriod = 10 * time.Second
93 |
94 | // consistent with chrome
95 | const ChromeH2KeepAlivePeriod = 45 * time.Second
96 | const ConnIdleTimeout = 300 * time.Second
97 |
98 | type FakePacketConn struct {
99 | net.Conn
100 | }
101 |
102 | func (c *FakePacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
103 | n, err = c.Read(p)
104 | return n, c.RemoteAddr(), err
105 | }
106 |
107 | func (c *FakePacketConn) WriteTo(p []byte, _ net.Addr) (n int, err error) {
108 | return c.Write(p)
109 | }
110 |
111 | func (c *FakePacketConn) LocalAddr() net.Addr {
112 | return &net.TCPAddr{
113 | IP: net.IP{byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256)), byte(rand.Intn(256))},
114 | Port: rand.Intn(65536),
115 | }
116 | }
117 |
118 | func (c *FakePacketConn) SetReadBuffer(bytes int) error {
119 | // do nothing, this function is only there to suppress quic-go printing
120 | // random warnings about UDP buffers to stdout
121 | return nil
122 | }
123 |
124 | func createHTTPClient(dest net.Destination, config *SplitHttpConfig, sc security.Engine, socketConfig *dlhelper.SocketSetting) DialerClient {
125 | tlsConfig, realityConfig := getSecurityConfig(sc)
126 |
127 | httpVersion := decideHTTPVersion(tlsConfig, realityConfig)
128 | if httpVersion == "3" {
129 | dest.Network = net.Network_UDP // better to keep this line
130 | }
131 |
132 | var gotlsConfig *gotls.Config
133 |
134 | if tlsConfig != nil {
135 | gotlsConfig, _ = tlsConfig.GetTLSConfig(tls.WithDestination(dest))
136 | }
137 |
138 | transportConfig := config
139 |
140 | dialContext := func(ctxInner context.Context) (net.Conn, error) {
141 | conn, err := dlhelper.DialSystemConn(ctxInner, dest, socketConfig)
142 | if err != nil {
143 | return nil, err
144 | }
145 | if sc != nil {
146 | return sc.GetClientConn(conn)
147 | }
148 | return conn, nil
149 | }
150 |
151 | var keepAlivePeriod time.Duration
152 | if config.Xmux != nil {
153 | keepAlivePeriod = time.Duration(config.Xmux.HKeepAlivePeriod) * time.Second
154 | }
155 |
156 | var transport http.RoundTripper
157 |
158 | if httpVersion == "3" {
159 | if keepAlivePeriod == 0 {
160 | keepAlivePeriod = QuicgoH3KeepAlivePeriod
161 | }
162 | if keepAlivePeriod < 0 {
163 | keepAlivePeriod = 0
164 | }
165 | quicConfig := &quic.Config{
166 | MaxIdleTimeout: ConnIdleTimeout,
167 |
168 | // these two are defaults of quic-go/http3. the default of quic-go (no
169 | // http3) is different, so it is hardcoded here for clarity.
170 | // https://github.com/quic-go/quic-go/blob/b8ea5c798155950fb5bbfdd06cad1939c9355878/http3/client.go#L36-L39
171 | MaxIncomingStreams: -1,
172 | KeepAlivePeriod: keepAlivePeriod,
173 | }
174 | transport = &http3.Transport{
175 | QUICConfig: quicConfig,
176 | TLSClientConfig: gotlsConfig,
177 | Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
178 | conn, err := dlhelper.DialSystemConn(ctx, dest, socketConfig)
179 | if err != nil {
180 | return nil, err
181 | }
182 |
183 | var udpConn net.PacketConn
184 | var udpAddr *net.UDPAddr
185 |
186 | switch c := conn.(type) {
187 | case *net.UDPConn:
188 | udpConn = c
189 | udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String())
190 | if err != nil {
191 | return nil, err
192 | }
193 | default:
194 | udpConn = &FakePacketConn{Conn: c}
195 | udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String())
196 | if err != nil {
197 | return nil, err
198 | }
199 | }
200 |
201 | return quic.DialEarly(ctx, udpConn, udpAddr, tlsCfg, cfg)
202 | },
203 | }
204 | } else if httpVersion == "2" {
205 | if keepAlivePeriod == 0 {
206 | keepAlivePeriod = ChromeH2KeepAlivePeriod
207 | }
208 | if keepAlivePeriod < 0 {
209 | keepAlivePeriod = 0
210 | }
211 | transport = &http2.Transport{
212 | DialTLSContext: func(ctxInner context.Context, network string, addr string, cfg *gotls.Config) (net.Conn, error) {
213 | return dialContext(ctxInner)
214 | },
215 | IdleConnTimeout: ConnIdleTimeout,
216 | ReadIdleTimeout: keepAlivePeriod,
217 | }
218 | } else {
219 | httpDialContext := func(ctxInner context.Context, network string, addr string) (net.Conn, error) {
220 | return dialContext(ctxInner)
221 | }
222 |
223 | transport = &http.Transport{
224 | DialTLSContext: httpDialContext,
225 | DialContext: httpDialContext,
226 | IdleConnTimeout: ConnIdleTimeout,
227 | // chunked transfer download with KeepAlives is buggy with
228 | // http.Client and our custom dial context.
229 | DisableKeepAlives: true,
230 | }
231 | }
232 |
233 | client := &DefaultDialerClient{
234 | transportConfig: transportConfig,
235 | client: &http.Client{
236 | Transport: transport,
237 | },
238 | httpVersion: httpVersion,
239 | uploadRawPool: &sync.Pool{},
240 | dialUploadConn: dialContext,
241 | }
242 |
243 | return client
244 | }
245 |
246 | type XhttpDialer struct {
247 | config *SplitHttpConfig
248 | sc security.Engine
249 | socketConfig *dlhelper.SocketSetting
250 | downAddress string
251 | downPort int
252 | downConfig *SplitHttpConfig
253 | downSc security.Engine
254 | }
255 |
256 | func NewXhttpDialer(config *SplitHttpConfig, sc security.Engine, socketConfig *dlhelper.SocketSetting) (*XhttpDialer, error) {
257 | d := &XhttpDialer{
258 | config: config,
259 | sc: sc,
260 | socketConfig: socketConfig,
261 | }
262 | d.downConfig = config.GetDownloadSettings().GetXhttpConfig()
263 | var securityEngine security.Engine
264 | var err error
265 | switch sc := config.GetDownloadSettings().GetSecurity().(type) {
266 | case *DownConfig_Tls:
267 | securityEngine, err = tls.NewEngine(sc.Tls)
268 | if err != nil {
269 | return nil, fmt.Errorf("failed to create tls engine: %w", err)
270 | }
271 | case *DownConfig_Reality:
272 | securityEngine = &reality.Engine{
273 | Config: sc.Reality,
274 | }
275 | }
276 | d.downSc = securityEngine
277 | return d, nil
278 | }
279 |
280 | func getSecurityConfig(sc security.Engine) (tlsConfig *tls.TlsConfig, realityConfig *reality.RealityConfig) {
281 | switch sc := sc.(type) {
282 | case *tls.Engine:
283 | tlsConfig = sc.Config
284 | case *reality.Engine:
285 | realityConfig = sc.Config
286 | }
287 | return
288 | }
289 |
290 | func (x *XhttpDialer) Dial(ctx context.Context, dest net.Destination) (net.Conn, error) {
291 | tlsConfig, realityConfig := getSecurityConfig(x.sc)
292 |
293 | httpVersion := decideHTTPVersion(tlsConfig, realityConfig)
294 | if httpVersion == "3" {
295 | dest.Network = net.Network_UDP
296 | }
297 |
298 | transportConfiguration := x.config
299 | var requestURL url.URL
300 |
301 | if tlsConfig != nil || realityConfig != nil {
302 | requestURL.Scheme = "https"
303 | } else {
304 | requestURL.Scheme = "http"
305 | }
306 | requestURL.Host = transportConfiguration.Host
307 | if requestURL.Host == "" && tlsConfig != nil {
308 | requestURL.Host = tlsConfig.ServerName
309 | }
310 | if requestURL.Host == "" && realityConfig != nil {
311 | requestURL.Host = realityConfig.ServerName
312 | }
313 | if requestURL.Host == "" {
314 | requestURL.Host = dest.Address.String()
315 | }
316 |
317 | sessionIdUuid := uuid.New()
318 | requestURL.Path = transportConfiguration.GetNormalizedPath() + sessionIdUuid.String()
319 | requestURL.RawQuery = transportConfiguration.GetNormalizedQuery()
320 |
321 | httpClient, xmuxClient := getHTTPClient(ctx, dest, x.config, x.sc, x.socketConfig)
322 |
323 | mode := transportConfiguration.Mode
324 | if mode == "" || mode == "auto" {
325 | mode = "packet-up"
326 | if realityConfig != nil {
327 | mode = "stream-one"
328 | if transportConfiguration.DownloadSettings != nil {
329 | mode = "stream-up"
330 | }
331 | }
332 | }
333 |
334 | // errors.LogInfo(ctx, fmt.Sprintf("XHTTP is dialing to %s, mode %s, HTTP version %s, host %s", dest, mode, httpVersion, requestURL.Host))
335 |
336 | requestURL2 := requestURL
337 | httpClient2 := httpClient
338 | xmuxClient2 := xmuxClient
339 | if transportConfiguration.DownloadSettings != nil {
340 | globalDialerAccess.Lock()
341 | globalDialerAccess.Unlock()
342 | dest2 := net.Destination{
343 | Address: net.ParseAddress(x.downAddress),
344 | Port: net.Port(x.downPort),
345 | } // just panic
346 | tlsConfig2, realityConfig2 := getSecurityConfig(x.downSc)
347 | httpVersion2 := decideHTTPVersion(tlsConfig2, realityConfig2)
348 | if httpVersion2 == "3" {
349 | dest2.Network = net.Network_UDP
350 | }
351 | if tlsConfig2 != nil || realityConfig2 != nil {
352 | requestURL2.Scheme = "https"
353 | } else {
354 | requestURL2.Scheme = "http"
355 | }
356 | config2 := x.downConfig
357 | requestURL2.Host = config2.Host
358 | if requestURL2.Host == "" && tlsConfig2 != nil {
359 | requestURL2.Host = tlsConfig2.ServerName
360 | }
361 | if requestURL2.Host == "" && realityConfig2 != nil {
362 | requestURL2.Host = realityConfig2.ServerName
363 | }
364 | if requestURL2.Host == "" {
365 | requestURL2.Host = dest2.Address.String()
366 | }
367 | requestURL2.Path = config2.GetNormalizedPath() + sessionIdUuid.String()
368 | requestURL2.RawQuery = config2.GetNormalizedQuery()
369 | httpClient2, xmuxClient2 = getHTTPClient(ctx, dest2, x.downConfig, x.downSc, x.socketConfig)
370 | }
371 |
372 | if xmuxClient != nil {
373 | xmuxClient.OpenUsage.Add(1)
374 | }
375 | if xmuxClient2 != nil && xmuxClient2 != xmuxClient {
376 | xmuxClient2.OpenUsage.Add(1)
377 | }
378 | var closed atomic.Int32
379 |
380 | reader, writer := io.Pipe()
381 | conn := splitConn{
382 | writer: writer,
383 | onClose: func() {
384 | if closed.Add(1) > 1 {
385 | return
386 | }
387 | if xmuxClient != nil {
388 | xmuxClient.OpenUsage.Add(-1)
389 | }
390 | if xmuxClient2 != nil && xmuxClient2 != xmuxClient {
391 | xmuxClient2.OpenUsage.Add(-1)
392 | }
393 | },
394 | }
395 |
396 | var err error
397 | if mode == "stream-one" {
398 | requestURL.Path = transportConfiguration.GetNormalizedPath()
399 | if xmuxClient != nil {
400 | xmuxClient.LeftRequests.Add(-1)
401 | }
402 | conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient.OpenStream(ctx, requestURL.String(), reader, false)
403 | if err != nil { // browser dialer only
404 | return nil, err
405 | }
406 | return &conn, nil
407 | } else { // stream-down
408 | if xmuxClient2 != nil {
409 | xmuxClient2.LeftRequests.Add(-1)
410 | }
411 | conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient2.OpenStream(ctx, requestURL2.String(), nil, false)
412 | if err != nil { // browser dialer only
413 | return nil, err
414 | }
415 | }
416 | if mode == "stream-up" {
417 | if xmuxClient != nil {
418 | xmuxClient.LeftRequests.Add(-1)
419 | }
420 | _, _, _, err = httpClient.OpenStream(ctx, requestURL.String(), reader, true)
421 | if err != nil { // browser dialer only
422 | return nil, err
423 | }
424 | return &conn, nil
425 | }
426 |
427 | scMaxEachPostBytes := transportConfiguration.GetNormalizedScMaxEachPostBytes()
428 | scMinPostsIntervalMs := transportConfiguration.GetNormalizedScMinPostsIntervalMs()
429 |
430 | if scMaxEachPostBytes.From <= buf.Size {
431 | panic("`scMaxEachPostBytes` should be bigger than " + strconv.Itoa(buf.Size))
432 | }
433 |
434 | maxUploadSize := scMaxEachPostBytes.rand()
435 | // WithSizeLimit(0) will still allow single bytes to pass, and a lot of
436 | // code relies on this behavior. Subtract 1 so that together with
437 | // uploadWriter wrapper, exact size limits can be enforced
438 | // uploadPipeReader, uploadPipeWriter := pipe.New(pipe.WithSizeLimit(maxUploadSize - 1))
439 | pp := pipe.NewPipe(maxUploadSize-buf.Size, false)
440 |
441 | conn.writer = uploadWriter{
442 | pp,
443 | maxUploadSize,
444 | }
445 |
446 | go func() {
447 | var seq int64
448 | var lastWrite time.Time
449 |
450 | for {
451 | wroteRequest := done.New()
452 |
453 | ctx := httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
454 | WroteRequest: func(httptrace.WroteRequestInfo) {
455 | wroteRequest.Close()
456 | },
457 | })
458 |
459 | // this intentionally makes a shallow-copy of the struct so we
460 | // can reassign Path (potentially concurrently)
461 | url := requestURL
462 | url.Path += "/" + strconv.FormatInt(seq, 10)
463 |
464 | seq += 1
465 |
466 | if scMinPostsIntervalMs.From > 0 {
467 | time.Sleep(time.Duration(scMinPostsIntervalMs.rand())*time.Millisecond - time.Since(lastWrite))
468 | }
469 |
470 | // by offloading the uploads into a buffered pipe, multiple conn.Write
471 | // calls get automatically batched together into larger POST requests.
472 | // without batching, bandwidth is extremely limited.
473 | chunk, err := pp.ReadMultiBuffer()
474 | if err != nil {
475 | break
476 | }
477 |
478 | lastWrite = time.Now()
479 |
480 | if xmuxClient != nil && (xmuxClient.LeftRequests.Add(-1) <= 0 ||
481 | (xmuxClient.UnreusableAt != time.Time{} && lastWrite.After(xmuxClient.UnreusableAt))) {
482 | httpClient, xmuxClient = getHTTPClient(ctx, dest, x.config, x.sc, x.socketConfig)
483 | }
484 |
485 | go func() {
486 | err := httpClient.PostPacket(
487 | ctx,
488 | url.String(),
489 | &buf.MultiBufferContainer{MultiBuffer: chunk},
490 | int64(chunk.Len()),
491 | )
492 | wroteRequest.Close()
493 | if err != nil {
494 | // errors.LogInfoInner(ctx, err, "failed to send upload")
495 | pp.Interrupt(err)
496 | }
497 | }()
498 |
499 | if _, ok := httpClient.(*DefaultDialerClient); ok {
500 | <-wroteRequest.Wait()
501 | }
502 | }
503 | }()
504 |
505 | return &conn, nil
506 | }
507 |
508 | // A wrapper around pipe that ensures the size limit is exactly honored.
509 | //
510 | // The MultiBuffer pipe accepts any single WriteMultiBuffer call even if that
511 | // single MultiBuffer exceeds the size limit, and then starts blocking on the
512 | // next WriteMultiBuffer call. This means that ReadMultiBuffer can return more
513 | // bytes than the size limit. We work around this by splitting a potentially
514 | // too large write up into multiple.
515 | type uploadWriter struct {
516 | *pipe.Pipe
517 | maxLen int32
518 | }
519 |
520 | func (w uploadWriter) Write(b []byte) (int, error) {
521 | /*
522 | capacity := int(w.maxLen - w.Len())
523 | if capacity > 0 && capacity < len(b) {
524 | b = b[:capacity]
525 | }
526 | */
527 |
528 | buffer := buf.New()
529 | n, err := buffer.Write(b)
530 | if err != nil {
531 | return 0, err
532 | }
533 |
534 | err = w.WriteMultiBuffer(buf.MultiBuffer{buffer})
535 | if err != nil {
536 | return 0, err
537 | }
538 | return n, nil
539 | }
540 |
--------------------------------------------------------------------------------
/splithttp/h1_conn.go:
--------------------------------------------------------------------------------
1 | package splithttp
2 |
3 | import (
4 | "bufio"
5 | "net"
6 | )
7 |
8 | type H1Conn struct {
9 | UnreadedResponsesCount int
10 | RespBufReader *bufio.Reader
11 | net.Conn
12 | }
13 |
14 | func NewH1Conn(conn net.Conn) *H1Conn {
15 | return &H1Conn{
16 | RespBufReader: bufio.NewReader(conn),
17 | Conn: conn,
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/splithttp/hub.go:
--------------------------------------------------------------------------------
1 | package splithttp
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | gotls "crypto/tls"
7 | "io"
8 | "net/http"
9 | "net/url"
10 | "strconv"
11 | "strings"
12 | "sync"
13 | "time"
14 |
15 | "github.com/5vnetwork/x/common/errors"
16 | "github.com/5vnetwork/x/common/net"
17 | http_proto "github.com/5vnetwork/x/common/protocol/http"
18 | "github.com/5vnetwork/x/common/signal/done"
19 | "github.com/5vnetwork/x/transport/dlhelper"
20 | "github.com/5vnetwork/x/transport/security"
21 | "github.com/5vnetwork/x/transport/security/tls"
22 | "github.com/quic-go/quic-go"
23 | "github.com/quic-go/quic-go/http3"
24 | goreality "github.com/xtls/reality"
25 | )
26 |
27 | type requestHandler struct {
28 | config *SplitHttpConfig
29 | host string
30 | path string
31 | ln *Listener
32 | sessionMu *sync.Mutex
33 | sessions sync.Map
34 | localAddr net.Addr
35 | }
36 |
37 | type httpSession struct {
38 | uploadQueue *uploadQueue
39 | // for as long as the GET request is not opened by the client, this will be
40 | // open ("undone"), and the session may be expired within a certain TTL.
41 | // after the client connects, this becomes "done" and the session lives as
42 | // long as the GET request.
43 | isFullyConnected *done.Instance
44 | }
45 |
46 | func (h *requestHandler) upsertSession(sessionId string) *httpSession {
47 | // fast path
48 | currentSessionAny, ok := h.sessions.Load(sessionId)
49 | if ok {
50 | return currentSessionAny.(*httpSession)
51 | }
52 |
53 | // slow path
54 | h.sessionMu.Lock()
55 | defer h.sessionMu.Unlock()
56 |
57 | currentSessionAny, ok = h.sessions.Load(sessionId)
58 | if ok {
59 | return currentSessionAny.(*httpSession)
60 | }
61 |
62 | s := &httpSession{
63 | uploadQueue: NewUploadQueue(h.ln.config.GetNormalizedScMaxBufferedPosts()),
64 | isFullyConnected: done.New(),
65 | }
66 |
67 | h.sessions.Store(sessionId, s)
68 |
69 | shouldReap := done.New()
70 | go func() {
71 | time.Sleep(30 * time.Second)
72 | shouldReap.Close()
73 | }()
74 | go func() {
75 | select {
76 | case <-shouldReap.Wait():
77 | h.sessions.Delete(sessionId)
78 | s.uploadQueue.Close()
79 | case <-s.isFullyConnected.Wait():
80 | }
81 | }()
82 |
83 | return s
84 | }
85 |
86 | func IsValidHTTPHost(request string, config string) bool {
87 | r := strings.ToLower(request)
88 | c := strings.ToLower(config)
89 | if strings.Contains(r, ":") {
90 | h, _, _ := net.SplitHostPort(r)
91 | return h == c
92 | }
93 | return r == c
94 | }
95 |
96 | func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
97 | if len(h.host) > 0 && !IsValidHTTPHost(request.Host, h.host) {
98 | writer.WriteHeader(http.StatusNotFound)
99 | return
100 | }
101 |
102 | if !strings.HasPrefix(request.URL.Path, h.path) {
103 | writer.WriteHeader(http.StatusNotFound)
104 | return
105 | }
106 |
107 | h.config.WriteResponseHeader(writer)
108 |
109 | /*
110 | clientVer := []int{0, 0, 0}
111 | x_version := strings.Split(request.URL.Query().Get("x_version"), ".")
112 | for j := 0; j < 3 && len(x_version) > j; j++ {
113 | clientVer[j], _ = strconv.Atoi(x_version[j])
114 | }
115 | */
116 |
117 | validRange := h.config.GetNormalizedXPaddingBytes()
118 | paddingLength := 0
119 |
120 | referrer := request.Header.Get("Referer")
121 | if referrer != "" {
122 | if referrerURL, err := url.Parse(referrer); err == nil {
123 | // Browser dialer cannot control the host part of referrer header, so only check the query
124 | paddingLength = len(referrerURL.Query().Get("x_padding"))
125 | }
126 | } else {
127 | paddingLength = len(request.URL.Query().Get("x_padding"))
128 | }
129 |
130 | if int32(paddingLength) < validRange.From || int32(paddingLength) > validRange.To {
131 | writer.WriteHeader(http.StatusBadRequest)
132 | return
133 | }
134 |
135 | sessionId := ""
136 | subpath := strings.Split(request.URL.Path[len(h.path):], "/")
137 | if len(subpath) > 0 {
138 | sessionId = subpath[0]
139 | }
140 |
141 | if sessionId == "" && h.config.Mode != "" && h.config.Mode != "auto" && h.config.Mode != "stream-one" && h.config.Mode != "stream-up" {
142 | writer.WriteHeader(http.StatusBadRequest)
143 | return
144 | }
145 |
146 | forwardedAddrs := http_proto.ParseXForwardedFor(request.Header)
147 | var remoteAddr net.Addr
148 | var err error
149 | remoteAddr, err = net.ResolveTCPAddr("tcp", request.RemoteAddr)
150 | if err != nil {
151 | remoteAddr = &net.TCPAddr{
152 | IP: []byte{0, 0, 0, 0},
153 | Port: 0,
154 | }
155 | }
156 | if request.ProtoMajor == 3 {
157 | remoteAddr = &net.UDPAddr{
158 | IP: remoteAddr.(*net.TCPAddr).IP,
159 | Port: remoteAddr.(*net.TCPAddr).Port,
160 | }
161 | }
162 | if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() {
163 | remoteAddr = &net.TCPAddr{
164 | IP: forwardedAddrs[0].IP(),
165 | Port: 0,
166 | }
167 | }
168 |
169 | var currentSession *httpSession
170 | if sessionId != "" {
171 | currentSession = h.upsertSession(sessionId)
172 | }
173 | scMaxEachPostBytes := int(h.ln.config.GetNormalizedScMaxEachPostBytes().To)
174 |
175 | if request.Method == "POST" && sessionId != "" { // stream-up, packet-up
176 | seq := ""
177 | if len(subpath) > 1 {
178 | seq = subpath[1]
179 | }
180 |
181 | if seq == "" {
182 | if h.config.Mode != "" && h.config.Mode != "auto" && h.config.Mode != "stream-up" {
183 | writer.WriteHeader(http.StatusBadRequest)
184 | return
185 | }
186 | httpSC := &httpServerConn{
187 | Instance: done.New(),
188 | Reader: request.Body,
189 | ResponseWriter: writer,
190 | }
191 | err = currentSession.uploadQueue.Push(Packet{
192 | Reader: httpSC,
193 | })
194 | if err != nil {
195 | writer.WriteHeader(http.StatusConflict)
196 | } else {
197 | writer.Header().Set("X-Accel-Buffering", "no")
198 | writer.Header().Set("Cache-Control", "no-store")
199 | writer.WriteHeader(http.StatusOK)
200 | scStreamUpServerSecs := h.config.GetNormalizedScStreamUpServerSecs()
201 | if referrer != "" && scStreamUpServerSecs.To > 0 {
202 | go func() {
203 | for {
204 | _, err := httpSC.Write(bytes.Repeat([]byte{'X'}, int(h.config.GetNormalizedXPaddingBytes().rand())))
205 | if err != nil {
206 | break
207 | }
208 | time.Sleep(time.Duration(scStreamUpServerSecs.rand()) * time.Second)
209 | }
210 | }()
211 | }
212 | select {
213 | case <-request.Context().Done():
214 | case <-httpSC.Wait():
215 | }
216 | }
217 | httpSC.Close()
218 | return
219 | }
220 |
221 | if h.config.Mode != "" && h.config.Mode != "auto" && h.config.Mode != "packet-up" {
222 | writer.WriteHeader(http.StatusBadRequest)
223 | return
224 | }
225 |
226 | payload, err := io.ReadAll(io.LimitReader(request.Body, int64(scMaxEachPostBytes)+1))
227 |
228 | if len(payload) > scMaxEachPostBytes {
229 | writer.WriteHeader(http.StatusRequestEntityTooLarge)
230 | return
231 | }
232 |
233 | if err != nil {
234 | writer.WriteHeader(http.StatusInternalServerError)
235 | return
236 | }
237 |
238 | seqInt, err := strconv.ParseUint(seq, 10, 64)
239 | if err != nil {
240 | writer.WriteHeader(http.StatusInternalServerError)
241 | return
242 | }
243 |
244 | err = currentSession.uploadQueue.Push(Packet{
245 | Payload: payload,
246 | Seq: seqInt,
247 | })
248 |
249 | if err != nil {
250 | writer.WriteHeader(http.StatusInternalServerError)
251 | return
252 | }
253 |
254 | writer.WriteHeader(http.StatusOK)
255 | } else if request.Method == "GET" || sessionId == "" { // stream-down, stream-one
256 | if sessionId != "" {
257 | // after GET is done, the connection is finished. disable automatic
258 | // session reaping, and handle it in defer
259 | currentSession.isFullyConnected.Close()
260 | defer h.sessions.Delete(sessionId)
261 | }
262 |
263 | // magic header instructs nginx + apache to not buffer response body
264 | writer.Header().Set("X-Accel-Buffering", "no")
265 | // A web-compliant header telling all middleboxes to disable caching.
266 | // Should be able to prevent overloading the cache, or stop CDNs from
267 | // teeing the response stream into their cache, causing slowdowns.
268 | writer.Header().Set("Cache-Control", "no-store")
269 |
270 | if !h.config.NoSSEHeader {
271 | // magic header to make the HTTP middle box consider this as SSE to disable buffer
272 | writer.Header().Set("Content-Type", "text/event-stream")
273 | }
274 |
275 | writer.WriteHeader(http.StatusOK)
276 | writer.(http.Flusher).Flush()
277 |
278 | httpSC := &httpServerConn{
279 | Instance: done.New(),
280 | Reader: request.Body,
281 | ResponseWriter: writer,
282 | }
283 | conn := splitConn{
284 | writer: httpSC,
285 | reader: httpSC,
286 | remoteAddr: remoteAddr,
287 | localAddr: h.localAddr,
288 | }
289 | if sessionId != "" { // if not stream-one
290 | conn.reader = currentSession.uploadQueue
291 | }
292 |
293 | h.ln.addConn(&conn)
294 |
295 | // "A ResponseWriter may not be used after [Handler.ServeHTTP] has returned."
296 | select {
297 | case <-request.Context().Done():
298 | case <-httpSC.Wait():
299 | }
300 |
301 | conn.Close()
302 | } else {
303 | writer.WriteHeader(http.StatusMethodNotAllowed)
304 | }
305 | }
306 |
307 | type httpServerConn struct {
308 | sync.Mutex
309 | *done.Instance
310 | io.Reader // no need to Close request.Body
311 | http.ResponseWriter
312 | }
313 |
314 | func (c *httpServerConn) Write(b []byte) (int, error) {
315 | c.Lock()
316 | defer c.Unlock()
317 | if c.Done() {
318 | return 0, io.ErrClosedPipe
319 | }
320 | n, err := c.ResponseWriter.Write(b)
321 | if err == nil {
322 | c.ResponseWriter.(http.Flusher).Flush()
323 | }
324 | return n, err
325 | }
326 |
327 | func (c *httpServerConn) Close() error {
328 | c.Lock()
329 | defer c.Unlock()
330 | return c.Instance.Close()
331 | }
332 |
333 | type Listener struct {
334 | sync.Mutex
335 | server http.Server
336 | h3server *http3.Server
337 | listener net.Listener
338 | h3listener *quic.EarlyListener
339 | config *SplitHttpConfig
340 | addConn func(net.Conn)
341 | isH3 bool
342 | }
343 |
344 | func ListenXH(ctx context.Context, address net.Address, port net.Port,
345 | config *SplitHttpConfig, sc security.Engine, socketSetting *dlhelper.SocketSetting, addConn func(net.Conn)) (*Listener, error) {
346 | l := &Listener{
347 | addConn: addConn,
348 | }
349 | l.config = config
350 | if l.config != nil {
351 | if socketSetting == nil {
352 | socketSetting = &dlhelper.SocketSetting{}
353 | }
354 | }
355 | handler := &requestHandler{
356 | config: l.config,
357 | host: l.config.Host,
358 | path: l.config.GetNormalizedPath(),
359 | ln: l,
360 | sessionMu: &sync.Mutex{},
361 | sessions: sync.Map{},
362 | }
363 | tlsConfig := &gotls.Config{}
364 | te, ok := sc.(*tls.Engine)
365 | if ok {
366 | tlsConfig = te.TlsConfig
367 | }
368 | l.isH3 = len(tlsConfig.NextProtos) == 1 && tlsConfig.NextProtos[0] == "h3"
369 |
370 | var err error
371 | if port == net.Port(0) { // unix
372 | l.listener, err = dlhelper.ListenSystem(ctx, &net.UnixAddr{
373 | Name: address.Domain(),
374 | Net: "unix",
375 | }, socketSetting)
376 | if err != nil {
377 | return nil, errors.New("failed to listen UNIX domain socket for XHTTP on ", address).Base(err)
378 | }
379 | } else if l.isH3 { // quic
380 | listenAddr := &net.UDPAddr{
381 | IP: address.IP(),
382 | Port: int(port),
383 | }
384 | Conn, err := dlhelper.ListenSystemPacket(context.Background(), "udp", listenAddr.String(), socketSetting)
385 | if err != nil {
386 | return nil, errors.New("failed to listen UDP for XHTTP/3 on ", address, ":", port).Base(err)
387 | }
388 | l.h3listener, err = quic.ListenEarly(Conn, tlsConfig, nil)
389 | if err != nil {
390 | return nil, errors.New("failed to listen QUIC for XHTTP/3 on ", address, ":", port).Base(err)
391 | }
392 |
393 | handler.localAddr = l.h3listener.Addr()
394 |
395 | l.h3server = &http3.Server{
396 | Handler: handler,
397 | }
398 | go func() {
399 | if err := l.h3server.ServeListener(l.h3listener); err != nil {
400 | // errors.LogErrorInner(ctx, err, "failed to serve HTTP/3 for XHTTP/3")
401 | }
402 | }()
403 | } else { // tcp
404 | l.listener, err = dlhelper.ListenSystem(ctx, &net.TCPAddr{
405 | IP: address.IP(),
406 | Port: int(port),
407 | }, socketSetting)
408 | if err != nil {
409 | return nil, errors.New("failed to listen TCP for XHTTP on ", address, ":", port).Base(err)
410 | }
411 | // errors.LogInfo(ctx, "listening TCP for XHTTP on ", address, ":", port)
412 | }
413 |
414 | // tcp/unix (h1/h2)
415 | if l.listener != nil {
416 | tlsConfig, realityConfig := getSecurityConfig(sc)
417 | if tlsConfig != nil {
418 | if tlsConfig, err := tlsConfig.GetTLSConfig(); err == nil && tlsConfig != nil {
419 | l.listener = gotls.NewListener(l.listener, tlsConfig)
420 | } else {
421 | return nil, errors.New("failed to get TLS config for XHTTP")
422 | }
423 | }
424 | if realityConfig != nil {
425 | l.listener = goreality.NewListener(l.listener, realityConfig.GetREALITYConfig())
426 | }
427 |
428 | handler.localAddr = l.listener.Addr()
429 |
430 | // server can handle both plaintext HTTP/1.1 and h2c
431 | protocols := new(http.Protocols)
432 | protocols.SetHTTP1(true)
433 | protocols.SetUnencryptedHTTP2(true)
434 | l.server = http.Server{
435 | Handler: handler,
436 | ReadHeaderTimeout: time.Second * 4,
437 | MaxHeaderBytes: 8192,
438 | Protocols: protocols,
439 | }
440 | go func() {
441 | if err := l.server.Serve(l.listener); err != nil {
442 | // errors.LogErrorInner(ctx, err, "failed to serve HTTP for XHTTP")
443 | }
444 | }()
445 | }
446 |
447 | return l, err
448 | }
449 |
450 | // Addr implements net.Listener.Addr().
451 | func (ln *Listener) Addr() net.Addr {
452 | if ln.h3listener != nil {
453 | return ln.h3listener.Addr()
454 | }
455 | if ln.listener != nil {
456 | return ln.listener.Addr()
457 | }
458 | return nil
459 | }
460 |
461 | // Close implements net.Listener.Close().
462 | func (ln *Listener) Close() error {
463 | if ln.h3server != nil {
464 | if err := ln.h3server.Close(); err != nil {
465 | return err
466 | }
467 | } else if ln.listener != nil {
468 | return ln.listener.Close()
469 | }
470 | return errors.New("listener does not have an HTTP/3 server or a net.listener")
471 | }
472 |
--------------------------------------------------------------------------------
/splithttp/mux.go:
--------------------------------------------------------------------------------
1 | package splithttp
2 |
3 | import (
4 | "context"
5 | "crypto/rand"
6 | "math"
7 | "math/big"
8 | "sync/atomic"
9 | "time"
10 | )
11 |
12 | type XmuxConn interface {
13 | IsClosed() bool
14 | }
15 |
16 | type XmuxClient struct {
17 | XmuxConn XmuxConn
18 | OpenUsage atomic.Int32
19 | leftUsage int32
20 | LeftRequests atomic.Int32
21 | UnreusableAt time.Time
22 | }
23 |
24 | type XmuxManager struct {
25 | xmuxConfig XmuxConfig
26 | concurrency int32
27 | connections int32
28 | newConnFunc func() XmuxConn
29 | xmuxClients []*XmuxClient
30 | }
31 |
32 | func NewXmuxManager(xmuxConfig XmuxConfig, newConnFunc func() XmuxConn) *XmuxManager {
33 | return &XmuxManager{
34 | xmuxConfig: xmuxConfig,
35 | concurrency: xmuxConfig.GetNormalizedMaxConcurrency().rand(),
36 | connections: xmuxConfig.GetNormalizedMaxConnections().rand(),
37 | newConnFunc: newConnFunc,
38 | xmuxClients: make([]*XmuxClient, 0),
39 | }
40 | }
41 |
42 | func (m *XmuxManager) newXmuxClient() *XmuxClient {
43 | xmuxClient := &XmuxClient{
44 | XmuxConn: m.newConnFunc(),
45 | leftUsage: -1,
46 | }
47 | if x := m.xmuxConfig.GetNormalizedCMaxReuseTimes().rand(); x > 0 {
48 | xmuxClient.leftUsage = x - 1
49 | }
50 | xmuxClient.LeftRequests.Store(math.MaxInt32)
51 | if x := m.xmuxConfig.GetNormalizedHMaxRequestTimes().rand(); x > 0 {
52 | xmuxClient.LeftRequests.Store(x)
53 | }
54 | if x := m.xmuxConfig.GetNormalizedHMaxReusableSecs().rand(); x > 0 {
55 | xmuxClient.UnreusableAt = time.Now().Add(time.Duration(x) * time.Second)
56 | }
57 | m.xmuxClients = append(m.xmuxClients, xmuxClient)
58 | return xmuxClient
59 | }
60 |
61 | func (m *XmuxManager) GetXmuxClient(ctx context.Context) *XmuxClient { // when locking
62 | for i := 0; i < len(m.xmuxClients); {
63 | xmuxClient := m.xmuxClients[i]
64 | if xmuxClient.XmuxConn.IsClosed() ||
65 | xmuxClient.leftUsage == 0 ||
66 | xmuxClient.LeftRequests.Load() <= 0 ||
67 | (xmuxClient.UnreusableAt != time.Time{} && time.Now().After(xmuxClient.UnreusableAt)) {
68 | m.xmuxClients = append(m.xmuxClients[:i], m.xmuxClients[i+1:]...)
69 | } else {
70 | i++
71 | }
72 | }
73 |
74 | if len(m.xmuxClients) == 0 {
75 | return m.newXmuxClient()
76 | }
77 |
78 | if m.connections > 0 && len(m.xmuxClients) < int(m.connections) {
79 | return m.newXmuxClient()
80 | }
81 |
82 | xmuxClients := make([]*XmuxClient, 0)
83 | if m.concurrency > 0 {
84 | for _, xmuxClient := range m.xmuxClients {
85 | if xmuxClient.OpenUsage.Load() < m.concurrency {
86 | xmuxClients = append(xmuxClients, xmuxClient)
87 | }
88 | }
89 | } else {
90 | xmuxClients = m.xmuxClients
91 | }
92 |
93 | if len(xmuxClients) == 0 {
94 | return m.newXmuxClient()
95 | }
96 |
97 | i, _ := rand.Int(rand.Reader, big.NewInt(int64(len(xmuxClients))))
98 | xmuxClient := xmuxClients[i.Int64()]
99 | if xmuxClient.leftUsage > 0 {
100 | xmuxClient.leftUsage -= 1
101 | }
102 | return xmuxClient
103 | }
104 |
--------------------------------------------------------------------------------
/splithttp/mux_test.go:
--------------------------------------------------------------------------------
1 | package splithttp_test
2 |
3 | import (
4 | "context"
5 | "testing"
6 |
7 | . "github.com/5vnetwork/x/transport/protocols/splithttp"
8 | )
9 |
10 | type fakeRoundTripper struct{}
11 |
12 | func (f *fakeRoundTripper) IsClosed() bool {
13 | return false
14 | }
15 |
16 | func TestMaxConnections(t *testing.T) {
17 | xmuxConfig := XmuxConfig{
18 | MaxConnections: &RangeConfig{From: 4, To: 4},
19 | }
20 |
21 | xmuxManager := NewXmuxManager(xmuxConfig, func() XmuxConn {
22 | return &fakeRoundTripper{}
23 | })
24 |
25 | xmuxClients := make(map[interface{}]struct{})
26 | for i := 0; i < 8; i++ {
27 | xmuxClients[xmuxManager.GetXmuxClient(context.Background())] = struct{}{}
28 | }
29 |
30 | if len(xmuxClients) != 4 {
31 | t.Error("did not get 4 distinct clients, got ", len(xmuxClients))
32 | }
33 | }
34 |
35 | func TestCMaxReuseTimes(t *testing.T) {
36 | xmuxConfig := XmuxConfig{
37 | CMaxReuseTimes: &RangeConfig{From: 2, To: 2},
38 | }
39 |
40 | xmuxManager := NewXmuxManager(xmuxConfig, func() XmuxConn {
41 | return &fakeRoundTripper{}
42 | })
43 |
44 | xmuxClients := make(map[interface{}]struct{})
45 | for i := 0; i < 64; i++ {
46 | xmuxClients[xmuxManager.GetXmuxClient(context.Background())] = struct{}{}
47 | }
48 |
49 | if len(xmuxClients) != 32 {
50 | t.Error("did not get 32 distinct clients, got ", len(xmuxClients))
51 | }
52 | }
53 |
54 | func TestMaxConcurrency(t *testing.T) {
55 | xmuxConfig := XmuxConfig{
56 | MaxConcurrency: &RangeConfig{From: 2, To: 2},
57 | }
58 |
59 | xmuxManager := NewXmuxManager(xmuxConfig, func() XmuxConn {
60 | return &fakeRoundTripper{}
61 | })
62 |
63 | xmuxClients := make(map[interface{}]struct{})
64 | for i := 0; i < 64; i++ {
65 | xmuxClient := xmuxManager.GetXmuxClient(context.Background())
66 | xmuxClient.OpenUsage.Add(1)
67 | xmuxClients[xmuxClient] = struct{}{}
68 | }
69 |
70 | if len(xmuxClients) != 32 {
71 | t.Error("did not get 32 distinct clients, got ", len(xmuxClients))
72 | }
73 | }
74 |
75 | func TestDefault(t *testing.T) {
76 | xmuxConfig := XmuxConfig{}
77 |
78 | xmuxManager := NewXmuxManager(xmuxConfig, func() XmuxConn {
79 | return &fakeRoundTripper{}
80 | })
81 |
82 | xmuxClients := make(map[interface{}]struct{})
83 | for i := 0; i < 64; i++ {
84 | xmuxClient := xmuxManager.GetXmuxClient(context.Background())
85 | xmuxClient.OpenUsage.Add(1)
86 | xmuxClients[xmuxClient] = struct{}{}
87 | }
88 |
89 | if len(xmuxClients) != 1 {
90 | t.Error("did not get 1 distinct clients, got ", len(xmuxClients))
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/splithttp/splithttp.go:
--------------------------------------------------------------------------------
1 | package splithttp
2 |
3 | const protocolName = "splithttp"
4 |
--------------------------------------------------------------------------------
/splithttp/splithttp_test.go:
--------------------------------------------------------------------------------
1 | package splithttp_test
2 |
3 | import (
4 | "context"
5 | "crypto/rand"
6 | "fmt"
7 | "io"
8 | "net/http"
9 | "runtime"
10 | "testing"
11 | "time"
12 |
13 | "github.com/5vnetwork/x/common"
14 | "github.com/5vnetwork/x/common/buf"
15 | "github.com/5vnetwork/x/common/net"
16 | "github.com/5vnetwork/x/common/protocol/tls/cert"
17 | "github.com/5vnetwork/x/test/servers/tcp"
18 | "github.com/5vnetwork/x/test/servers/udp"
19 | . "github.com/5vnetwork/x/transport/protocols/splithttp"
20 | "github.com/5vnetwork/x/transport/security/tls"
21 | "github.com/google/go-cmp/cmp"
22 | )
23 |
24 | func Test_ListenXHAndDial(t *testing.T) {
25 | listenPort := tcp.PickPort()
26 | listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, &SplitHttpConfig{
27 | Path: "/sh",
28 | }, nil, nil, func(conn net.Conn) {
29 | go func(c net.Conn) {
30 | defer c.Close()
31 |
32 | var b [1024]byte
33 | c.SetReadDeadline(time.Now().Add(2 * time.Second))
34 | _, err := c.Read(b[:])
35 | if err != nil {
36 | return
37 | }
38 |
39 | common.Must2(c.Write([]byte("Response")))
40 | }(conn)
41 | })
42 | common.Must(err)
43 | ctx := context.Background()
44 |
45 | dialer, err := NewXhttpDialer(&SplitHttpConfig{Path: "sh"}, nil, nil)
46 | common.Must(err)
47 |
48 | conn, err := dialer.Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort))
49 |
50 | common.Must(err)
51 | _, err = conn.Write([]byte("Test connection 1"))
52 | common.Must(err)
53 |
54 | var b [1024]byte
55 | fmt.Println("test2")
56 | n, _ := io.ReadFull(conn, b[:])
57 | fmt.Println("string is", n)
58 | if string(b[:n]) != "Response" {
59 | t.Error("response: ", string(b[:n]))
60 | }
61 |
62 | common.Must(conn.Close())
63 | conn, err = dialer.Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort))
64 |
65 | common.Must(err)
66 | _, err = conn.Write([]byte("Test connection 2"))
67 | common.Must(err)
68 | n, _ = io.ReadFull(conn, b[:])
69 | common.Must(err)
70 | if string(b[:n]) != "Response" {
71 | t.Error("response: ", string(b[:n]))
72 | }
73 | common.Must(conn.Close())
74 |
75 | common.Must(listen.Close())
76 | }
77 |
78 | func TestDialWithRemoteAddr(t *testing.T) {
79 | listenPort := tcp.PickPort()
80 | listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, &SplitHttpConfig{
81 | Path: "sh",
82 | }, nil, nil, func(conn net.Conn) {
83 | go func(c net.Conn) {
84 | defer c.Close()
85 |
86 | var b [1024]byte
87 | _, err := c.Read(b[:])
88 | // common.Must(err)
89 | if err != nil {
90 | return
91 | }
92 |
93 | _, err = c.Write([]byte(c.RemoteAddr().String()))
94 | common.Must(err)
95 | }(conn)
96 | })
97 | common.Must(err)
98 |
99 | dialer, err := NewXhttpDialer(&SplitHttpConfig{Path: "sh", Headers: map[string]string{"X-Forwarded-For": "1.1.1.1"}}, nil, nil)
100 | common.Must(err)
101 |
102 | conn, err := dialer.Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort))
103 |
104 | common.Must(err)
105 | _, err = conn.Write([]byte("Test connection 1"))
106 | common.Must(err)
107 |
108 | var b [1024]byte
109 | n, _ := io.ReadFull(conn, b[:])
110 | if string(b[:n]) != "1.1.1.1:0" {
111 | t.Error("response: ", string(b[:n]))
112 | }
113 |
114 | common.Must(listen.Close())
115 | }
116 |
117 | func Test_ListenXHAndDial_TLS(t *testing.T) {
118 | if runtime.GOARCH == "arm64" {
119 | return
120 | }
121 |
122 | listenPort := tcp.PickPort()
123 |
124 | start := time.Now()
125 |
126 | config := &SplitHttpConfig{
127 | Path: "shs",
128 | }
129 | tlsConfig := &tls.TlsConfig{
130 | AllowInsecure: true,
131 | Certificates: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))},
132 | }
133 | listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, config, &tls.Engine{Config: tlsConfig}, nil, func(conn net.Conn) {
134 | go func() {
135 | defer conn.Close()
136 |
137 | var b [1024]byte
138 | conn.SetReadDeadline(time.Now().Add(2 * time.Second))
139 | _, err := conn.Read(b[:])
140 | if err != nil {
141 | return
142 | }
143 |
144 | common.Must2(conn.Write([]byte("Response")))
145 | }()
146 | })
147 | common.Must(err)
148 | defer listen.Close()
149 |
150 | dialer, err := NewXhttpDialer(config, &tls.Engine{Config: tlsConfig}, nil)
151 | common.Must(err)
152 |
153 | conn, err := dialer.Dial(context.Background(), net.TCPDestination(net.DomainAddress("localhost"), listenPort))
154 | common.Must(err)
155 |
156 | _, err = conn.Write([]byte("Test connection 1"))
157 | common.Must(err)
158 |
159 | var b [1024]byte
160 | n, _ := io.ReadFull(conn, b[:])
161 | if string(b[:n]) != "Response" {
162 | t.Error("response: ", string(b[:n]))
163 | }
164 |
165 | end := time.Now()
166 | if !end.Before(start.Add(time.Second * 5)) {
167 | t.Error("end: ", end, " start: ", start)
168 | }
169 | }
170 |
171 | func Test_ListenXHAndDial_H2C(t *testing.T) {
172 | if runtime.GOARCH == "arm64" {
173 | return
174 | }
175 |
176 | listenPort := tcp.PickPort()
177 |
178 | config := &SplitHttpConfig{
179 | Path: "shs",
180 | }
181 | listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, config, nil, nil, func(conn net.Conn) {
182 | go func() {
183 | _ = conn.Close()
184 | }()
185 | })
186 | common.Must(err)
187 | defer listen.Close()
188 |
189 | protocols := new(http.Protocols)
190 | protocols.SetUnencryptedHTTP2(true)
191 | client := http.Client{
192 | Transport: &http.Transport{
193 | Protocols: protocols,
194 | },
195 | }
196 |
197 | resp, err := client.Get("http://" + net.LocalHostIP.String() + ":" + listenPort.String())
198 | common.Must(err)
199 |
200 | if resp.StatusCode != 404 {
201 | t.Error("Expected 404 but got:", resp.StatusCode)
202 | }
203 |
204 | if resp.ProtoMajor != 2 {
205 | t.Error("Expected h2 but got:", resp.ProtoMajor)
206 | }
207 | }
208 |
209 | func Test_ListenXHAndDial_QUIC(t *testing.T) {
210 | if runtime.GOARCH == "arm64" {
211 | return
212 | }
213 |
214 | listenPort := udp.PickPort()
215 |
216 | start := time.Now()
217 |
218 | config := &SplitHttpConfig{
219 | Path: "shs",
220 | }
221 | tlsConfig := &tls.TlsConfig{
222 | AllowInsecure: true,
223 | Certificates: []*tls.Certificate{tls.ParseCertificate(cert.MustGenerate(nil, cert.CommonName("localhost")))},
224 | NextProtocol: []string{"h3"},
225 | }
226 |
227 | serverClosed := false
228 | listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, config, &tls.Engine{Config: tlsConfig}, nil, func(conn net.Conn) {
229 | go func() {
230 | defer conn.Close()
231 |
232 | b := buf.New()
233 | defer b.Release()
234 |
235 | for {
236 | b.Clear()
237 | if _, err := b.ReadOnce(conn); err != nil {
238 | break
239 | }
240 | common.Must2(conn.Write(b.Bytes()))
241 | }
242 |
243 | serverClosed = true
244 | }()
245 | })
246 | common.Must(err)
247 | defer listen.Close()
248 |
249 | time.Sleep(time.Second)
250 |
251 | dialer, err := NewXhttpDialer(config, &tls.Engine{Config: tlsConfig}, nil)
252 | common.Must(err)
253 |
254 | conn, err := dialer.Dial(context.Background(), net.UDPDestination(net.DomainAddress("localhost"), listenPort))
255 | common.Must(err)
256 |
257 | const N = 1024
258 | b1 := make([]byte, N)
259 | common.Must2(rand.Read(b1))
260 | b2 := buf.New()
261 |
262 | common.Must2(conn.Write(b1))
263 |
264 | b2.Clear()
265 | common.Must2(b2.ReadFullFrom(conn, N))
266 | if r := cmp.Diff(b2.Bytes(), b1); r != "" {
267 | t.Error(r)
268 | }
269 |
270 | common.Must2(conn.Write(b1))
271 |
272 | b2.Clear()
273 | common.Must2(b2.ReadFullFrom(conn, N))
274 | if r := cmp.Diff(b2.Bytes(), b1); r != "" {
275 | t.Error(r)
276 | }
277 |
278 | conn.Close()
279 | time.Sleep(100 * time.Millisecond)
280 | if !serverClosed {
281 | t.Error("server did not get closed")
282 | }
283 |
284 | end := time.Now()
285 | if !end.Before(start.Add(time.Second * 5)) {
286 | t.Error("end: ", end, " start: ", start)
287 | }
288 | }
289 |
290 | func Test_ListenXHAndDial_Unix(t *testing.T) {
291 | tempDir := t.TempDir()
292 | tempSocket := tempDir + "/server.sock"
293 |
294 | listen, err := ListenXH(context.Background(), net.DomainAddress(tempSocket), 0, &SplitHttpConfig{
295 | Path: "/sh",
296 | }, nil, nil, func(conn net.Conn) {
297 | go func(c net.Conn) {
298 | defer c.Close()
299 |
300 | var b [1024]byte
301 | c.SetReadDeadline(time.Now().Add(2 * time.Second))
302 | _, err := c.Read(b[:])
303 | if err != nil {
304 | return
305 | }
306 |
307 | common.Must2(c.Write([]byte("Response")))
308 | }(conn)
309 | })
310 | common.Must(err)
311 | ctx := context.Background()
312 | config := &SplitHttpConfig{
313 | Host: "example.com",
314 | Path: "sh",
315 | }
316 | dialer, err := NewXhttpDialer(config, nil, nil)
317 | common.Must(err)
318 | conn, err := dialer.Dial(ctx, net.UnixDestination(net.DomainAddress(tempSocket)))
319 |
320 | common.Must(err)
321 | _, err = conn.Write([]byte("Test connection 1"))
322 | common.Must(err)
323 |
324 | var b [1024]byte
325 | fmt.Println("test2")
326 | n, _ := io.ReadFull(conn, b[:])
327 | fmt.Println("string is", n)
328 | if string(b[:n]) != "Response" {
329 | t.Error("response: ", string(b[:n]))
330 | }
331 |
332 | common.Must(conn.Close())
333 | conn, err = dialer.Dial(ctx, net.UnixDestination(net.DomainAddress(tempSocket)))
334 |
335 | common.Must(err)
336 | _, err = conn.Write([]byte("Test connection 2"))
337 | common.Must(err)
338 | n, _ = io.ReadFull(conn, b[:])
339 | common.Must(err)
340 | if string(b[:n]) != "Response" {
341 | t.Error("response: ", string(b[:n]))
342 | }
343 | common.Must(conn.Close())
344 |
345 | common.Must(listen.Close())
346 | }
347 |
348 | func Test_queryString(t *testing.T) {
349 | listenPort := tcp.PickPort()
350 | listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, &SplitHttpConfig{
351 | // this querystring does not have any effect, but sometimes people blindly copy it from websocket config. make sure the outbound doesn't break
352 | Path: "/sh?ed=2048",
353 | }, nil, nil, func(conn net.Conn) {
354 | go func(c net.Conn) {
355 | defer c.Close()
356 |
357 | var b [1024]byte
358 | c.SetReadDeadline(time.Now().Add(2 * time.Second))
359 | _, err := c.Read(b[:])
360 | if err != nil {
361 | return
362 | }
363 |
364 | common.Must2(c.Write([]byte("Response")))
365 | }(conn)
366 | })
367 | common.Must(err)
368 | ctx := context.Background()
369 |
370 | dialer, err := NewXhttpDialer(&SplitHttpConfig{Path: "sh?ed=2048"}, nil, nil)
371 | conn, err := dialer.Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort))
372 |
373 | common.Must(err)
374 | _, err = conn.Write([]byte("Test connection 1"))
375 | common.Must(err)
376 |
377 | var b [1024]byte
378 | fmt.Println("test2")
379 | n, _ := io.ReadFull(conn, b[:])
380 | fmt.Println("string is", n)
381 | if string(b[:n]) != "Response" {
382 | t.Error("response: ", string(b[:n]))
383 | }
384 |
385 | common.Must(conn.Close())
386 | common.Must(listen.Close())
387 | }
388 |
389 | func Test_maxUpload(t *testing.T) {
390 | listenPort := tcp.PickPort()
391 | config := &SplitHttpConfig{
392 | Path: "/sh",
393 | ScMaxEachPostBytes: &RangeConfig{
394 | From: 10000,
395 | To: 10000,
396 | },
397 | }
398 |
399 | var uploadSize int
400 | listen, err := ListenXH(context.Background(), net.LocalHostIP, listenPort, config, nil, nil, func(conn net.Conn) {
401 | go func(c net.Conn) {
402 | defer c.Close()
403 | var b [10240]byte
404 | c.SetReadDeadline(time.Now().Add(2 * time.Second))
405 | n, err := c.Read(b[:])
406 | if err != nil {
407 | return
408 | }
409 |
410 | uploadSize = n
411 |
412 | common.Must2(c.Write([]byte("Response")))
413 | }(conn)
414 | })
415 | common.Must(err)
416 | ctx := context.Background()
417 |
418 | dialer, err := NewXhttpDialer(&SplitHttpConfig{Path: "sh"}, nil, nil)
419 | conn, err := dialer.Dial(ctx, net.TCPDestination(net.DomainAddress("localhost"), listenPort))
420 |
421 | // send a slightly too large upload
422 | var upload [10001]byte
423 | _, err = conn.Write(upload[:])
424 | common.Must(err)
425 |
426 | var b [10240]byte
427 | n, _ := io.ReadFull(conn, b[:])
428 | fmt.Println("string is", n)
429 | if string(b[:n]) != "Response" {
430 | t.Error("response: ", string(b[:n]))
431 | }
432 | common.Must(conn.Close())
433 |
434 | if uploadSize > 10000 || uploadSize == 0 {
435 | t.Error("incorrect upload size: ", uploadSize)
436 | }
437 |
438 | common.Must(listen.Close())
439 | }
440 |
--------------------------------------------------------------------------------
/splithttp/upload_queue.go:
--------------------------------------------------------------------------------
1 | package splithttp
2 |
3 | // upload_queue is a specialized priorityqueue + channel to reorder generic
4 | // packets by a sequence number
5 |
6 | import (
7 | "container/heap"
8 | "io"
9 | "runtime"
10 | "sync"
11 |
12 | "github.com/5vnetwork/x/common/errors"
13 | )
14 |
15 | type Packet struct {
16 | Reader io.ReadCloser
17 | Payload []byte
18 | Seq uint64
19 | }
20 |
21 | type uploadQueue struct {
22 | reader io.ReadCloser
23 | nomore bool
24 | pushedPackets chan Packet
25 | writeCloseMutex sync.Mutex
26 | heap uploadHeap
27 | nextSeq uint64
28 | closed bool
29 | maxPackets int
30 | }
31 |
32 | func NewUploadQueue(maxPackets int) *uploadQueue {
33 | return &uploadQueue{
34 | pushedPackets: make(chan Packet, maxPackets),
35 | heap: uploadHeap{},
36 | nextSeq: 0,
37 | closed: false,
38 | maxPackets: maxPackets,
39 | }
40 | }
41 |
42 | func (h *uploadQueue) Push(p Packet) error {
43 | h.writeCloseMutex.Lock()
44 | defer h.writeCloseMutex.Unlock()
45 |
46 | if h.closed {
47 | return errors.New("packet queue closed")
48 | }
49 | if h.nomore {
50 | return errors.New("h.reader already exists")
51 | }
52 | if p.Reader != nil {
53 | h.nomore = true
54 | }
55 | h.pushedPackets <- p
56 | return nil
57 | }
58 |
59 | func (h *uploadQueue) Close() error {
60 | h.writeCloseMutex.Lock()
61 | defer h.writeCloseMutex.Unlock()
62 |
63 | if !h.closed {
64 | h.closed = true
65 | runtime.Gosched() // hope Read() gets the packet
66 | f:
67 | for {
68 | select {
69 | case p := <-h.pushedPackets:
70 | if p.Reader != nil {
71 | h.reader = p.Reader
72 | }
73 | default:
74 | break f
75 | }
76 | }
77 | close(h.pushedPackets)
78 | }
79 | if h.reader != nil {
80 | return h.reader.Close()
81 | }
82 | return nil
83 | }
84 |
85 | func (h *uploadQueue) Read(b []byte) (int, error) {
86 | if h.reader != nil {
87 | return h.reader.Read(b)
88 | }
89 |
90 | if h.closed {
91 | return 0, io.EOF
92 | }
93 |
94 | if len(h.heap) == 0 {
95 | packet, more := <-h.pushedPackets
96 | if !more {
97 | return 0, io.EOF
98 | }
99 | if packet.Reader != nil {
100 | h.reader = packet.Reader
101 | return h.reader.Read(b)
102 | }
103 | heap.Push(&h.heap, packet)
104 | }
105 |
106 | for len(h.heap) > 0 {
107 | packet := heap.Pop(&h.heap).(Packet)
108 | n := 0
109 |
110 | if packet.Seq == h.nextSeq {
111 | copy(b, packet.Payload)
112 | n = min(len(b), len(packet.Payload))
113 |
114 | if n < len(packet.Payload) {
115 | // partial read
116 | packet.Payload = packet.Payload[n:]
117 | heap.Push(&h.heap, packet)
118 | } else {
119 | h.nextSeq = packet.Seq + 1
120 | }
121 |
122 | return n, nil
123 | }
124 |
125 | // misordered packet
126 | if packet.Seq > h.nextSeq {
127 | if len(h.heap) > h.maxPackets {
128 | // the "reassembly buffer" is too large, and we want to
129 | // constrain memory usage somehow. let's tear down the
130 | // connection, and hope the application retries.
131 | return 0, errors.New("packet queue is too large")
132 | }
133 | heap.Push(&h.heap, packet)
134 | packet2, more := <-h.pushedPackets
135 | if !more {
136 | return 0, io.EOF
137 | }
138 | heap.Push(&h.heap, packet2)
139 | }
140 | }
141 |
142 | return 0, nil
143 | }
144 |
145 | // heap code directly taken from https://pkg.go.dev/container/heap
146 | type uploadHeap []Packet
147 |
148 | func (h uploadHeap) Len() int { return len(h) }
149 | func (h uploadHeap) Less(i, j int) bool { return h[i].Seq < h[j].Seq }
150 | func (h uploadHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
151 |
152 | func (h *uploadHeap) Push(x any) {
153 | // Push and Pop use pointer receivers because they modify the slice's length,
154 | // not just its contents.
155 | *h = append(*h, x.(Packet))
156 | }
157 |
158 | func (h *uploadHeap) Pop() any {
159 | old := *h
160 | n := len(old)
161 | x := old[n-1]
162 | *h = old[0 : n-1]
163 | return x
164 | }
165 |
--------------------------------------------------------------------------------
/splithttp/upload_queue_test.go:
--------------------------------------------------------------------------------
1 | package splithttp_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/5vnetwork/x/common"
7 | . "github.com/5vnetwork/x/transport/protocols/splithttp"
8 | )
9 |
10 | func Test_regression_readzero(t *testing.T) {
11 | q := NewUploadQueue(10)
12 | q.Push(Packet{
13 | Payload: []byte("x"),
14 | Seq: 0,
15 | })
16 | buf := make([]byte, 20)
17 | n, err := q.Read(buf)
18 | common.Must(err)
19 | if n != 1 {
20 | t.Error("n=", n)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/vless/LICENCE:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
--------------------------------------------------------------------------------
/vless/account.go:
--------------------------------------------------------------------------------
1 | package vless
2 |
3 | import (
4 | "github.com/5vnetwork/x/common/protocol"
5 | "github.com/5vnetwork/x/common/uuid"
6 | )
7 |
8 | // MemoryAccount is an in-memory form of VLess account.
9 | type MemoryAccount struct {
10 | Uid uuid.UUID
11 | // ID of the account.
12 | ID *protocol.ID
13 | // Flow of the account. May be "xtls-rprx-vision".
14 | Flow string
15 | // Encryption of the account. Used for client connections, and only accepts "none" for now.
16 | Encryption string
17 | }
18 |
19 | // Equals implements protocol.Account.Equals().
20 | func (a *MemoryAccount) Equals(account protocol.Account) bool {
21 | vlessAccount, ok := account.(*MemoryAccount)
22 | if !ok {
23 | return false
24 | }
25 | return a.ID.Equals(vlessAccount.ID)
26 | }
27 |
--------------------------------------------------------------------------------
/vless/encoding/addons.go:
--------------------------------------------------------------------------------
1 | package encoding
2 |
3 | import (
4 | "context"
5 | "io"
6 |
7 | "github.com/5vnetwork/x/common/buf"
8 | "github.com/5vnetwork/x/common/errors"
9 | "github.com/5vnetwork/x/common/protocol"
10 | "github.com/5vnetwork/x/proxy/vless"
11 |
12 | "google.golang.org/protobuf/proto"
13 | )
14 |
15 | func EncodeHeaderAddons(buffer *buf.Buffer, addons *Addons) error {
16 | switch addons.Flow {
17 | case vless.XRV:
18 | bytes, err := proto.Marshal(addons)
19 | if err != nil {
20 | return errors.New("failed to marshal addons protobuf value").Base(err)
21 | }
22 | if err := buffer.WriteByte(byte(len(bytes))); err != nil {
23 | return errors.New("failed to write addons protobuf length").Base(err)
24 | }
25 | if _, err := buffer.Write(bytes); err != nil {
26 | return errors.New("failed to write addons protobuf value").Base(err)
27 | }
28 | default:
29 | if err := buffer.WriteByte(0); err != nil {
30 | return errors.New("failed to write addons protobuf length").Base(err)
31 | }
32 | }
33 |
34 | return nil
35 | }
36 |
37 | func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*Addons, error) {
38 | addons := new(Addons)
39 | buffer.Clear()
40 | if _, err := buffer.ReadFullFrom(reader, 1); err != nil {
41 | return nil, errors.New("failed to read addons protobuf length").Base(err)
42 | }
43 |
44 | if length := int32(buffer.Byte(0)); length != 0 {
45 | buffer.Clear()
46 | if _, err := buffer.ReadFullFrom(reader, length); err != nil {
47 | return nil, errors.New("failed to read addons protobuf value").Base(err)
48 | }
49 |
50 | if err := proto.Unmarshal(buffer.Bytes(), addons); err != nil {
51 | return nil, errors.New("failed to unmarshal addons protobuf value").Base(err)
52 | }
53 |
54 | // Verification.
55 | switch addons.Flow {
56 | default:
57 | }
58 | }
59 |
60 | return addons, nil
61 | }
62 |
63 | // EncodeBodyAddons returns a Writer that auto-encrypt content written by caller.
64 | func EncodeBodyAddons(writer io.Writer, request *protocol.RequestHeader, requestAddons *Addons, state *vless.TrafficState, context context.Context) buf.Writer {
65 | if request.Command == protocol.RequestCommandUDP {
66 | return buf.NewMultiLengthPacketWriter(writer.(buf.Writer))
67 | }
68 | w := buf.NewWriter(writer)
69 | if requestAddons.Flow == vless.XRV {
70 | w = vless.NewVisionWriter(w, state, context)
71 | }
72 | return w
73 | }
74 |
75 | // DecodeBodyAddons returns a Reader from which caller can fetch decrypted body.
76 | func DecodeBodyAddons(reader io.Reader, request *protocol.RequestHeader, addons *Addons) buf.Reader {
77 | switch addons.Flow {
78 | default:
79 | if request.Command == protocol.RequestCommandUDP {
80 | return NewLengthPacketReader(reader)
81 | }
82 | }
83 | return buf.NewReader(reader)
84 | }
85 |
86 | func NewLengthPacketWriter(writer io.Writer) *LengthPacketWriter {
87 | return &LengthPacketWriter{
88 | Writer: writer,
89 | cache: make([]byte, 0, 65536),
90 | }
91 | }
92 |
93 | type LengthPacketWriter struct {
94 | io.Writer
95 | cache []byte
96 | }
97 |
98 | func (w *LengthPacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
99 | length := mb.Len() // none of mb is nil
100 | // fmt.Println("Write", length)
101 | if length == 0 {
102 | return nil
103 | }
104 | defer func() {
105 | w.cache = w.cache[:0]
106 | }()
107 | w.cache = append(w.cache, byte(length>>8), byte(length))
108 | for i, b := range mb {
109 | w.cache = append(w.cache, b.Bytes()...)
110 | b.Release()
111 | mb[i] = nil
112 | }
113 | if _, err := w.Write(w.cache); err != nil {
114 | return errors.New("failed to write a packet").Base(err)
115 | }
116 | return nil
117 | }
118 |
119 | func NewLengthPacketReader(reader io.Reader) *LengthPacketReader {
120 | return &LengthPacketReader{
121 | Reader: reader,
122 | cache: make([]byte, 2),
123 | }
124 | }
125 |
126 | type LengthPacketReader struct {
127 | io.Reader
128 | cache []byte
129 | }
130 |
131 | func (r *LengthPacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
132 | if _, err := io.ReadFull(r.Reader, r.cache); err != nil { // maybe EOF
133 | return nil, errors.New("failed to read packet length").Base(err)
134 | }
135 | length := int32(r.cache[0])<<8 | int32(r.cache[1])
136 | // fmt.Println("Read", length)
137 | mb := make(buf.MultiBuffer, 0, length/buf.Size+1)
138 | for length > 0 {
139 | size := length
140 | if size > buf.Size {
141 | size = buf.Size
142 | }
143 | length -= size
144 | b := buf.New()
145 | if _, err := b.ReadFullFrom(r.Reader, size); err != nil {
146 | return nil, errors.New("failed to read packet payload").Base(err)
147 | }
148 | mb = append(mb, b)
149 | }
150 | return mb, nil
151 | }
152 |
--------------------------------------------------------------------------------
/vless/encoding/encoding.go:
--------------------------------------------------------------------------------
1 | package encoding
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "io"
7 | "sync"
8 | "sync/atomic"
9 |
10 | "github.com/5vnetwork/x/common/buf"
11 | "github.com/5vnetwork/x/common/errors"
12 | "github.com/5vnetwork/x/common/net"
13 | "github.com/5vnetwork/x/common/protocol"
14 | "github.com/5vnetwork/x/common/serial/address_parser"
15 | "github.com/5vnetwork/x/common/signal"
16 | "github.com/5vnetwork/x/common/uuid"
17 | "github.com/5vnetwork/x/proxy/vless"
18 | )
19 |
20 | const (
21 | Version = byte(0)
22 | )
23 |
24 | var addrParser = address_parser.VAddressSerializer
25 |
26 | // EncodeRequestHeader writes encoded request header into the given writer.
27 | func EncodeRequestHeader(writer io.Writer, request *protocol.RequestHeader, requestAddons *Addons) error {
28 | buffer := buf.StackNew()
29 | defer buffer.Release()
30 |
31 | if err := buffer.WriteByte(request.Version); err != nil {
32 | return errors.New("failed to write request version").Base(err)
33 | }
34 |
35 | if _, err := buffer.Write(request.Account.(*vless.MemoryAccount).ID.Bytes()); err != nil {
36 | return errors.New("failed to write request user id").Base(err)
37 | }
38 |
39 | if err := EncodeHeaderAddons(&buffer, requestAddons); err != nil {
40 | return errors.New("failed to encode request header addons").Base(err)
41 | }
42 |
43 | if err := buffer.WriteByte(byte(request.Command)); err != nil {
44 | return errors.New("failed to write request command").Base(err)
45 | }
46 |
47 | if request.Command != protocol.RequestCommandMux {
48 | if err := addrParser.WriteAddressPort(&buffer, request.Address, request.Port); err != nil {
49 | return errors.New("failed to write request address and port").Base(err)
50 | }
51 | }
52 |
53 | if _, err := writer.Write(buffer.Bytes()); err != nil {
54 | return errors.New("failed to write request header").Base(err)
55 | }
56 |
57 | return nil
58 | }
59 |
60 | // DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream.
61 | func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validator *sync.Map) (*protocol.RequestHeader, *Addons, bool, error) {
62 | buffer := buf.StackNew()
63 | defer buffer.Release()
64 |
65 | request := new(protocol.RequestHeader)
66 |
67 | if isfb {
68 | request.Version = first.Byte(0)
69 | } else {
70 | if _, err := buffer.ReadFullFrom(reader, 1); err != nil {
71 | return nil, nil, false, errors.New("failed to read request version").Base(err)
72 | }
73 | request.Version = buffer.Byte(0)
74 | }
75 |
76 | switch request.Version {
77 | case 0:
78 | var id [16]byte
79 | if isfb {
80 | copy(id[:], first.BytesRange(1, 17))
81 | } else {
82 | buffer.Clear()
83 | if _, err := buffer.ReadFullFrom(reader, 16); err != nil {
84 | return nil, nil, false, errors.New("failed to read request user id").Base(err)
85 | }
86 | copy(id[:], buffer.Bytes())
87 | }
88 |
89 | account, ok := validator.Load(uuid.UUID(id))
90 | if !ok {
91 | return nil, nil, isfb, errors.New("invalid request user id")
92 | }
93 | request.User = account.(*vless.MemoryAccount).Uid
94 | request.Account = account
95 |
96 | if isfb {
97 | first.AdvanceStart(17)
98 | }
99 |
100 | requestAddons, err := DecodeHeaderAddons(&buffer, reader)
101 | if err != nil {
102 | return nil, nil, false, errors.New("failed to decode request header addons").Base(err)
103 | }
104 |
105 | buffer.Clear()
106 | if _, err := buffer.ReadFullFrom(reader, 1); err != nil {
107 | return nil, nil, false, errors.New("failed to read request command").Base(err)
108 | }
109 |
110 | request.Command = protocol.RequestCommand(buffer.Byte(0))
111 | switch request.Command {
112 | case protocol.RequestCommandMux:
113 | request.Address = net.DomainAddress("v1.mux.cool")
114 | request.Port = 0
115 | case protocol.RequestCommandTCP, protocol.RequestCommandUDP:
116 | if addr, port, err := addrParser.ReadAddressPort(&buffer, reader); err == nil {
117 | request.Address = addr
118 | request.Port = port
119 | }
120 | }
121 | if request.Address == nil {
122 | return nil, nil, false, errors.New("invalid request address")
123 | }
124 | return request, requestAddons, false, nil
125 | default:
126 | return nil, nil, isfb, errors.New("invalid request version")
127 | }
128 | }
129 |
130 | // EncodeResponseHeader writes encoded response header into the given writer.
131 | func EncodeResponseHeader(writer io.Writer, request *protocol.RequestHeader, responseAddons *Addons) error {
132 | buffer := buf.StackNew()
133 | defer buffer.Release()
134 |
135 | if err := buffer.WriteByte(request.Version); err != nil {
136 | return errors.New("failed to write response version").Base(err)
137 | }
138 |
139 | if err := EncodeHeaderAddons(&buffer, responseAddons); err != nil {
140 | return errors.New("failed to encode response header addons").Base(err)
141 | }
142 |
143 | if _, err := writer.Write(buffer.Bytes()); err != nil {
144 | return errors.New("failed to write response header").Base(err)
145 | }
146 |
147 | return nil
148 | }
149 |
150 | // DecodeResponseHeader decodes and returns (if successful) a ResponseHeader from an input stream.
151 | func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*Addons, error) {
152 | buffer := buf.StackNew()
153 | defer buffer.Release()
154 |
155 | if _, err := buffer.ReadFullFrom(reader, 1); err != nil {
156 | return nil, errors.New("failed to read response version").Base(err)
157 | }
158 |
159 | if buffer.Byte(0) != request.Version {
160 | return nil, errors.New("unexpected response version. Expecting ", int(request.Version), " but actually ", int(buffer.Byte(0)))
161 | }
162 |
163 | responseAddons, err := DecodeHeaderAddons(&buffer, reader)
164 | if err != nil {
165 | return nil, errors.New("failed to decode response header addons").Base(err)
166 | }
167 |
168 | return responseAddons, nil
169 | }
170 |
171 | // XtlsRead filter and read xtls protocol
172 | // read loop. reader is or includes a visionReader
173 | func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityChecker, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *vless.TrafficState, ob *vless.OutboundInfo, ctx context.Context) error {
174 | err := func() error {
175 | for {
176 | if trafficState.ReaderSwitchToDirectCopy {
177 | var writerConn net.Conn
178 | var inTimer *signal.ActivityChecker
179 | if inbound := vless.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil {
180 | writerConn = inbound.Conn
181 | inTimer = inbound.Timer
182 | if inbound.CanSpliceCopy == 2 {
183 | inbound.CanSpliceCopy = 1
184 | }
185 | if ob != nil && ob.CanSpliceCopy == 2 { // ob need to be passed in due to context can change
186 | ob.CanSpliceCopy = 1
187 | }
188 | }
189 | // log.Ctx(ctx).Debug().Msg("Switch to direct read")
190 | return vless.CopyRawConnIfExist(ctx, conn, writerConn, writer, timer, inTimer)
191 | }
192 | buffer, err := reader.ReadMultiBuffer()
193 | if !buffer.IsEmpty() {
194 | timer.Update()
195 | if trafficState.ReaderSwitchToDirectCopy {
196 | // XTLS Vision processes struct TLS Conn's input and rawInput
197 | if inputBuffer, err := buf.ReadFrom(input); err == nil {
198 | if !inputBuffer.IsEmpty() {
199 | buffer, _ = buf.MergeMulti(buffer, inputBuffer)
200 | }
201 | }
202 | if rawInputBuffer, err := buf.ReadFrom(rawInput); err == nil {
203 | if !rawInputBuffer.IsEmpty() {
204 | buffer, _ = buf.MergeMulti(buffer, rawInputBuffer)
205 | }
206 | }
207 | }
208 | if werr := writer.WriteMultiBuffer(buffer); werr != nil {
209 | return werr
210 | }
211 | }
212 | if err != nil {
213 | return err
214 | }
215 | }
216 | }()
217 | if err != nil && !errors.Is(err, io.EOF) {
218 | return err
219 | }
220 | return nil
221 | }
222 |
223 | // XtlsWrite filter and write xtls protocol.
224 | // actual write loop. writer is or includes a visionWriter
225 | func XtlsWrite(reader buf.Reader, writer buf.Writer, timer *signal.ActivityChecker, conn net.Conn, trafficState *vless.TrafficState, ob *vless.OutboundInfo, ctx context.Context) error {
226 | err := func() error {
227 | var ct *atomic.Uint64
228 | for {
229 | buffer, err := reader.ReadMultiBuffer()
230 | if trafficState.WriterSwitchToDirectCopy {
231 | if inbound := vless.InboundFromContext(ctx); inbound != nil {
232 | if inbound.CanSpliceCopy == 2 {
233 | inbound.CanSpliceCopy = 1
234 | }
235 | if ob != nil && ob.CanSpliceCopy == 2 {
236 | ob.CanSpliceCopy = 1
237 | }
238 | }
239 | rawConn, _, writerCounter := vless.UnwrapRawConn(conn)
240 | writer = buf.NewWriter(rawConn)
241 | ct = writerCounter
242 | // log.Ctx(ctx).Debug().Msg("Switch to direct write")
243 | trafficState.WriterSwitchToDirectCopy = false
244 | }
245 | if !buffer.IsEmpty() {
246 | if ct != nil {
247 | ct.Add(uint64(buffer.Len()))
248 | }
249 | timer.Update()
250 | if werr := writer.WriteMultiBuffer(buffer); werr != nil {
251 | return werr
252 | }
253 | }
254 | if err != nil {
255 | return err
256 | }
257 | }
258 | }()
259 | if err != nil && !errors.Is(err, io.EOF) {
260 | return err
261 | }
262 | return nil
263 | }
264 |
--------------------------------------------------------------------------------
/vless/encoding/encoding.pb.go:
--------------------------------------------------------------------------------
1 | package encoding
2 |
3 | import (
4 | protoreflect "google.golang.org/protobuf/reflect/protoreflect"
5 | protoimpl "google.golang.org/protobuf/runtime/protoimpl"
6 | reflect "reflect"
7 | sync "sync"
8 | )
9 |
10 | const (
11 | // Verify that this generated code is sufficiently up-to-date.
12 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
13 | // Verify that runtime/protoimpl is sufficiently up-to-date.
14 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
15 | )
16 |
17 | type Addons struct {
18 | state protoimpl.MessageState
19 | sizeCache protoimpl.SizeCache
20 | unknownFields protoimpl.UnknownFields
21 |
22 | Flow string `protobuf:"bytes,1,opt,name=Flow,proto3" json:"Flow,omitempty"`
23 | Seed []byte `protobuf:"bytes,2,opt,name=Seed,proto3" json:"Seed,omitempty"`
24 | }
25 |
26 | func (x *Addons) Reset() {
27 | *x = Addons{}
28 | if protoimpl.UnsafeEnabled {
29 | mi := &file_proxy_vless_encoding_encoding_proto_msgTypes[0]
30 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
31 | ms.StoreMessageInfo(mi)
32 | }
33 | }
34 |
35 | func (x *Addons) String() string {
36 | return protoimpl.X.MessageStringOf(x)
37 | }
38 |
39 | func (*Addons) ProtoMessage() {}
40 |
41 | func (x *Addons) ProtoReflect() protoreflect.Message {
42 | mi := &file_proxy_vless_encoding_encoding_proto_msgTypes[0]
43 | if protoimpl.UnsafeEnabled && x != nil {
44 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
45 | if ms.LoadMessageInfo() == nil {
46 | ms.StoreMessageInfo(mi)
47 | }
48 | return ms
49 | }
50 | return mi.MessageOf(x)
51 | }
52 |
53 | // Deprecated: Use Addons.ProtoReflect.Descriptor instead.
54 | func (*Addons) Descriptor() ([]byte, []int) {
55 | return file_proxy_vless_encoding_encoding_proto_rawDescGZIP(), []int{0}
56 | }
57 |
58 | func (x *Addons) GetFlow() string {
59 | if x != nil {
60 | return x.Flow
61 | }
62 | return ""
63 | }
64 |
65 | func (x *Addons) GetSeed() []byte {
66 | if x != nil {
67 | return x.Seed
68 | }
69 | return nil
70 | }
71 |
72 | var File_proxy_vless_encoding_encoding_proto protoreflect.FileDescriptor
73 |
74 | var file_proxy_vless_encoding_encoding_proto_rawDesc = []byte{
75 | 0x0a, 0x23, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x65, 0x6e,
76 | 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x2e,
77 | 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76,
78 | 0x6c, 0x65, 0x73, 0x73, 0x22, 0x30, 0x0a, 0x06, 0x41, 0x64, 0x64, 0x6f, 0x6e, 0x73, 0x12, 0x12,
79 | 0x0a, 0x04, 0x46, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x6c,
80 | 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x53, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
81 | 0x52, 0x04, 0x53, 0x65, 0x65, 0x64, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
82 | 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x35, 0x76, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x78,
83 | 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x65, 0x6e, 0x63,
84 | 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
85 | }
86 |
87 | var (
88 | file_proxy_vless_encoding_encoding_proto_rawDescOnce sync.Once
89 | file_proxy_vless_encoding_encoding_proto_rawDescData = file_proxy_vless_encoding_encoding_proto_rawDesc
90 | )
91 |
92 | func file_proxy_vless_encoding_encoding_proto_rawDescGZIP() []byte {
93 | file_proxy_vless_encoding_encoding_proto_rawDescOnce.Do(func() {
94 | file_proxy_vless_encoding_encoding_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_vless_encoding_encoding_proto_rawDescData)
95 | })
96 | return file_proxy_vless_encoding_encoding_proto_rawDescData
97 | }
98 |
99 | var file_proxy_vless_encoding_encoding_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
100 | var file_proxy_vless_encoding_encoding_proto_goTypes = []any{
101 | (*Addons)(nil), // 0: x.proxy.vless.Addons
102 | }
103 | var file_proxy_vless_encoding_encoding_proto_depIdxs = []int32{
104 | 0, // [0:0] is the sub-list for method output_type
105 | 0, // [0:0] is the sub-list for method input_type
106 | 0, // [0:0] is the sub-list for extension type_name
107 | 0, // [0:0] is the sub-list for extension extendee
108 | 0, // [0:0] is the sub-list for field type_name
109 | }
110 |
111 | func init() { file_proxy_vless_encoding_encoding_proto_init() }
112 | func file_proxy_vless_encoding_encoding_proto_init() {
113 | if File_proxy_vless_encoding_encoding_proto != nil {
114 | return
115 | }
116 | if !protoimpl.UnsafeEnabled {
117 | file_proxy_vless_encoding_encoding_proto_msgTypes[0].Exporter = func(v any, i int) any {
118 | switch v := v.(*Addons); i {
119 | case 0:
120 | return &v.state
121 | case 1:
122 | return &v.sizeCache
123 | case 2:
124 | return &v.unknownFields
125 | default:
126 | return nil
127 | }
128 | }
129 | }
130 | type x struct{}
131 | out := protoimpl.TypeBuilder{
132 | File: protoimpl.DescBuilder{
133 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
134 | RawDescriptor: file_proxy_vless_encoding_encoding_proto_rawDesc,
135 | NumEnums: 0,
136 | NumMessages: 1,
137 | NumExtensions: 0,
138 | NumServices: 0,
139 | },
140 | GoTypes: file_proxy_vless_encoding_encoding_proto_goTypes,
141 | DependencyIndexes: file_proxy_vless_encoding_encoding_proto_depIdxs,
142 | MessageInfos: file_proxy_vless_encoding_encoding_proto_msgTypes,
143 | }.Build()
144 | File_proxy_vless_encoding_encoding_proto = out.File
145 | file_proxy_vless_encoding_encoding_proto_rawDesc = nil
146 | file_proxy_vless_encoding_encoding_proto_goTypes = nil
147 | file_proxy_vless_encoding_encoding_proto_depIdxs = nil
148 | }
149 |
--------------------------------------------------------------------------------
/vless/encoding/encoding.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package x.proxy.vless;
4 | option go_package = "github.com/5vnetwork/x/proxy/vless/encoding";
5 |
6 | message Addons {
7 | string Flow = 1;
8 | bytes Seed = 2;
9 | }
--------------------------------------------------------------------------------
/vless/outbound/outbound.go:
--------------------------------------------------------------------------------
1 | package outbound
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | gotls "crypto/tls"
7 | "fmt"
8 | "io"
9 | "reflect"
10 | "time"
11 | "unsafe"
12 |
13 | "github.com/5vnetwork/x/common/buf"
14 | "github.com/5vnetwork/x/common/dispatcher"
15 | "github.com/5vnetwork/x/common/errors"
16 | "github.com/5vnetwork/x/common/mux"
17 | "github.com/5vnetwork/x/common/net"
18 | "github.com/5vnetwork/x/common/net/udp"
19 | "github.com/5vnetwork/x/common/protocol"
20 | "github.com/5vnetwork/x/common/session"
21 | "github.com/5vnetwork/x/common/signal"
22 | "github.com/5vnetwork/x/common/task"
23 | "github.com/5vnetwork/x/i"
24 | "github.com/5vnetwork/x/proxy/helper"
25 | "github.com/5vnetwork/x/proxy/vless"
26 | "github.com/5vnetwork/x/proxy/vless/encoding"
27 | "github.com/5vnetwork/x/proxy/vless/xudp"
28 |
29 | "github.com/5vnetwork/x/transport/security/tls"
30 |
31 | utls "github.com/refraction-networking/utls"
32 | "github.com/rs/zerolog/log"
33 | )
34 |
35 | // Handler is an outbound connection handler for VLess protocol.
36 | type Handler struct {
37 | serverPicker protocol.ServerPicker
38 | timeoutSetting i.TimeoutSetting
39 | }
40 |
41 | // New creates a new VLess outbound handler.
42 | func New() *Handler {
43 | handler := &Handler{}
44 | return handler
45 | }
46 |
47 | func (h *Handler) WithServerPicker(p protocol.ServerPicker) *Handler {
48 | h.serverPicker = p
49 | return h
50 | }
51 | func (h *Handler) WithTimeoutSetting(p i.TimeoutSetting) *Handler {
52 | h.timeoutSetting = p
53 | return h
54 | }
55 |
56 | func (h *Handler) HandleFlow(ctx context.Context, info *session.Info, rw buf.ReaderWriter, dialer i.Dialer) error {
57 | return h.handle(ctx, info, rw, dialer)
58 | }
59 |
60 | func (h *Handler) HandlePacketConn(ctx context.Context, info *session.Info, p udp.PacketConn, dialer i.Dialer) error {
61 | sp := h.serverPicker.PickServer()
62 | account := sp.GetProtocolSetting().(*vless.MemoryAccount)
63 | requestAddons := &encoding.Addons{
64 | Flow: account.Flow,
65 | }
66 |
67 | if requestAddons.Flow == (vless.XRV + "-udp443") {
68 | if info.Target.Port == 443 {
69 | return ErrRejectQuic
70 | }
71 | requestAddons.Flow = requestAddons.Flow[:16]
72 | }
73 |
74 | // xudp case, full cone NAT
75 | if requestAddons.Flow == vless.XRV ||
76 | (info.Target.Port != 53 && info.Target.Port != 443) {
77 | var conn net.Conn
78 | conn, err := dialer.Dial(ctx, sp.Destination())
79 | if err != nil {
80 | return fmt.Errorf("failed to find an available destination, %w", err)
81 | }
82 | defer conn.Close()
83 | log.Ctx(ctx).Debug().Str("laddr", conn.LocalAddr().String()).Msg("vless dial ok")
84 |
85 | request := &protocol.RequestHeader{
86 | Version: encoding.Version,
87 | Account: account,
88 | Command: protocol.RequestCommandMux,
89 | Address: net.DomainAddress("v1.mux.cool"),
90 | Port: net.Port(666),
91 | }
92 | trafficState := vless.NewTrafficState(account.ID.Bytes())
93 | postRequest := func() error {
94 | bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn))
95 | if err := encoding.EncodeRequestHeader(bufferWriter, request, requestAddons); err != nil {
96 | return errors.New("failed to encode request header").Base(err)
97 | }
98 | // default: serverWriter := bufferWriter
99 | serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, ctx)
100 | serverWriter = xudp.NewPacketWriter(serverWriter, info.Target, xudp.GetGlobalID(ctx))
101 | if requestAddons.Flow == vless.XRV {
102 | mb := make(buf.MultiBuffer, 1)
103 | log.Ctx(ctx).Debug().Msg("Insert padding with empty content to camouflage VLESS header")
104 | if err := serverWriter.WriteMultiBuffer(mb); err != nil {
105 | return err // ...
106 | }
107 | }
108 | // Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer
109 | if err := bufferWriter.SetBuffered(false); err != nil {
110 | return errors.New("failed to write A request payload").Base(err)
111 | }
112 | if requestAddons.Flow == vless.XRV {
113 | if tlsConn, ok := conn.(*tls.Conn); ok {
114 | if tlsConn.ConnectionState().Version != gotls.VersionTLS13 {
115 | return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version)
116 | }
117 | } else if utlsConn, ok := conn.(*tls.UConn); ok {
118 | if utlsConn.ConnectionState().Version != utls.VersionTLS13 {
119 | return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, utlsConn.ConnectionState().Version)
120 | }
121 | }
122 | }
123 | // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
124 | xudpWriter := serverWriter.(*xudp.PacketWriter)
125 | for {
126 | p, err := p.ReadPacket()
127 | if err != nil {
128 | if err == io.EOF {
129 | return nil
130 | }
131 | return fmt.Errorf("failed to read packet from packetConn: %w", err)
132 | }
133 | if err := xudpWriter.WritePacket(p); err != nil {
134 | return fmt.Errorf("failed to write packet to server: %w", err)
135 | }
136 | }
137 | }
138 | getResponse := func() error {
139 | responseAddons, err := encoding.DecodeResponseHeader(conn, request)
140 | if err != nil {
141 | return errors.New("failed to decode response header").Base(err)
142 | }
143 |
144 | // default: serverReader := buf.NewReader(conn)
145 | serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons)
146 | if requestAddons.Flow == vless.XRV {
147 | serverReader = vless.NewVisionReader(serverReader, trafficState, ctx)
148 | }
149 | if requestAddons.Flow == vless.XRV {
150 | serverReader = xudp.NewPacketReader(&buf.BufferedReader{Reader: serverReader})
151 | } else {
152 | serverReader = xudp.NewPacketReader(conn)
153 | }
154 | xudpReader := serverReader.(*xudp.PacketReader)
155 | for {
156 | pk, err := xudpReader.ReadPacket()
157 | if err != nil {
158 | if err == io.EOF {
159 | return nil
160 | }
161 | return fmt.Errorf("failed to read packet from server: %w", err)
162 | }
163 | if err := p.WritePacket(pk); err != nil {
164 | return fmt.Errorf("failed to write packet to packetConn: %w", err)
165 | }
166 | }
167 | }
168 | if err := task.Run(ctx, postRequest, getResponse); err != nil {
169 | return fmt.Errorf("connection ends: %w", err)
170 | }
171 | return nil
172 | } else {
173 | d := dispatcher.NewPacketDispatcher(ctx, info, &helper.Adapter{
174 | Dialer: dialer,
175 | ProxyClient: h,
176 | }, func(packet *udp.Packet) {
177 | p.WritePacket(packet)
178 | })
179 | defer d.Close()
180 | for {
181 | packet, err := p.ReadPacket()
182 | if err != nil {
183 | return err
184 | }
185 | d.DispatchPacket(packet.Target, packet.Payload)
186 | }
187 | }
188 | }
189 |
190 | var ErrRejectQuic = errors.New("XTLS rejected QUIC traffic")
191 |
192 | func (h *Handler) handle(ctx context.Context, info *session.Info, rw buf.ReaderWriter, dialer i.Dialer) error {
193 | ob := &vless.OutboundInfo{
194 | Target: info.Target,
195 | CanSpliceCopy: info.SpliceCopy.ToVlessNum(),
196 | }
197 | ctx = vless.WithOutbounds(ctx, []*vless.OutboundInfo{ob})
198 |
199 | ib := &vless.InboundInfo{
200 | CanSpliceCopy: info.SpliceCopy.ToVlessNum(),
201 | Conn: info.RawConn,
202 | UpCounter: info.UpCounter,
203 | DownCounter: info.DownCounter,
204 | }
205 | ctx = vless.WithInbound(ctx, ib)
206 |
207 | var conn net.Conn
208 | var account *vless.MemoryAccount
209 | sp := h.serverPicker.PickServer()
210 | account = sp.GetProtocolSetting().(*vless.MemoryAccount)
211 | conn, err := dialer.Dial(ctx, sp.Destination())
212 | if err != nil {
213 | return fmt.Errorf("failed to find an available destination, %w", err)
214 | }
215 | defer conn.Close()
216 |
217 | log.Ctx(ctx).Debug().Str("laddr", conn.LocalAddr().String()).Msg("vless dial ok")
218 |
219 | target := ob.Target
220 |
221 | command := protocol.RequestCommandTCP
222 | if target.Network == net.Network_UDP {
223 | command = protocol.RequestCommandUDP
224 | }
225 | if target.Address.Family().IsDomain() && target.Address.Domain() == mux.MuxCoolAddressDst.String() {
226 | command = protocol.RequestCommandMux
227 | }
228 |
229 | request := &protocol.RequestHeader{
230 | Version: encoding.Version,
231 | Account: account,
232 | Command: command,
233 | Address: target.Address,
234 | Port: target.Port,
235 | }
236 |
237 | requestAddons := &encoding.Addons{
238 | Flow: account.Flow,
239 | }
240 |
241 | var input *bytes.Reader
242 | var rawInput *bytes.Buffer
243 | allowUDP443 := false
244 | switch requestAddons.Flow {
245 | case vless.XRV + "-udp443":
246 | allowUDP443 = true
247 | requestAddons.Flow = requestAddons.Flow[:16]
248 | fallthrough
249 | case vless.XRV:
250 | ob.CanSpliceCopy = 2
251 | switch request.Command {
252 | case protocol.RequestCommandUDP:
253 | if !allowUDP443 && request.Port == 443 {
254 | return ErrRejectQuic
255 | }
256 | case protocol.RequestCommandMux:
257 | fallthrough // let server break Mux connections that contain TCP requests
258 | case protocol.RequestCommandTCP:
259 | var t reflect.Type
260 | var p uintptr
261 | if tlsConn, ok := conn.(*tls.Conn); ok {
262 | t = reflect.TypeOf(tlsConn.Conn).Elem()
263 | p = uintptr(unsafe.Pointer(tlsConn.Conn))
264 | } else if utlsConn, ok := conn.(*tls.UConn); ok {
265 | t = reflect.TypeOf(utlsConn.Conn).Elem()
266 | p = uintptr(unsafe.Pointer(utlsConn.Conn))
267 | } else {
268 | return errors.New("XTLS only supports TLS and REALITY directly for now.")
269 | }
270 | // else if realityConn, ok := conn.(*reality.UConn); ok {
271 | // t = reflect.TypeOf(realityConn.Conn).Elem()
272 | // p = uintptr(unsafe.Pointer(realityConn.Conn))
273 | // }
274 |
275 | i, _ := t.FieldByName("input")
276 | r, _ := t.FieldByName("rawInput")
277 | input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset))
278 | rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset))
279 | }
280 | default:
281 | ob.CanSpliceCopy = 3
282 | }
283 |
284 | // var newCtx context.Context
285 | // var newCancel context.CancelFunc
286 |
287 | ctx, cancelCause := context.WithCancelCause(ctx)
288 | timer := signal.NewActivityChecker(func() {
289 | cancelCause(errors.ErrIdle)
290 | // if newCancel != nil {
291 | // newCancel()
292 | // }
293 | }, h.timeoutSetting.TcpIdleTimeout())
294 |
295 | clientReader := rw // .(*pipe.Reader)
296 | clientWriter := rw // .(*pipe.Writer)
297 | trafficState := vless.NewTrafficState(account.ID.Bytes())
298 | if request.Command == protocol.RequestCommandUDP && (requestAddons.Flow == vless.XRV ||
299 | (request.Port != 53 && request.Port != 443)) {
300 | request.Command = protocol.RequestCommandMux
301 | request.Address = net.DomainAddress("v1.mux.cool")
302 | request.Port = net.Port(666)
303 | }
304 |
305 | postRequest := func() error {
306 | defer timer.SetTimeout(h.timeoutSetting.DownLinkOnlyTimeout())
307 |
308 | bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn))
309 | if err := encoding.EncodeRequestHeader(bufferWriter, request, requestAddons); err != nil {
310 | return errors.New("failed to encode request header").Base(err)
311 | }
312 |
313 | // default: serverWriter := bufferWriter
314 | serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons, trafficState, ctx)
315 | if request.Command == protocol.RequestCommandMux && request.Port == 666 {
316 | serverWriter = xudp.NewPacketWriter(serverWriter, target, [8]byte{})
317 | }
318 | timeoutReader, ok := clientReader.(buf.TimeoutReader)
319 | if ok {
320 | multiBuffer, err1 := timeoutReader.ReadMultiBufferTimeout(time.Millisecond * 500)
321 | if err1 == nil {
322 | if err := serverWriter.WriteMultiBuffer(multiBuffer); err != nil {
323 | return err // ...
324 | }
325 | } else if err1 != buf.ErrReadTimeout {
326 | return err1
327 | } else if requestAddons.Flow == vless.XRV {
328 | mb := make(buf.MultiBuffer, 1)
329 | log.Ctx(ctx).Debug().Msg("Insert padding with empty content to camouflage VLESS header")
330 | if err := serverWriter.WriteMultiBuffer(mb); err != nil {
331 | return err // ...
332 | }
333 | }
334 | }
335 | // Flush; bufferWriter.WriteMultiBuffer now is bufferWriter.writer.WriteMultiBuffer
336 | if err := bufferWriter.SetBuffered(false); err != nil {
337 | return errors.New("failed to write A request payload").Base(err)
338 | }
339 |
340 | var err error
341 | if requestAddons.Flow == vless.XRV {
342 | if tlsConn, ok := conn.(*tls.Conn); ok {
343 | if tlsConn.ConnectionState().Version != gotls.VersionTLS13 {
344 | return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version)
345 | }
346 | } else if utlsConn, ok := conn.(*tls.UConn); ok {
347 | if utlsConn.ConnectionState().Version != utls.VersionTLS13 {
348 | return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, utlsConn.ConnectionState().Version)
349 | }
350 | }
351 | ctx1 := vless.WithInbound(ctx, nil) // TODO enable splice
352 | err = encoding.XtlsWrite(clientReader, serverWriter, timer, conn, trafficState, ob, ctx1)
353 | } else {
354 | // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
355 | err = buf.Copy(clientReader, serverWriter, buf.UpdateActivityCopyOption(timer))
356 | }
357 | if err != nil {
358 | return errors.New("failed to transfer request payload").Base(err)
359 | }
360 |
361 | // Indicates the end of request payload.
362 | switch requestAddons.Flow {
363 | default:
364 | }
365 | return nil
366 | }
367 |
368 | getResponse := func() error {
369 | defer timer.SetTimeout(h.timeoutSetting.UpLinkOnlyTimeout())
370 |
371 | responseAddons, err := encoding.DecodeResponseHeader(conn, request)
372 | if err != nil {
373 | return errors.New("failed to decode response header").Base(err)
374 | }
375 |
376 | // default: serverReader := buf.NewReader(conn)
377 | serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons)
378 | if requestAddons.Flow == vless.XRV {
379 | serverReader = vless.NewVisionReader(serverReader, trafficState, ctx)
380 | }
381 | if request.Command == protocol.RequestCommandMux && request.Port == 666 {
382 | if requestAddons.Flow == vless.XRV {
383 | serverReader = xudp.NewPacketReader(&buf.BufferedReader{Reader: serverReader})
384 | } else {
385 | serverReader = xudp.NewPacketReader(conn)
386 | }
387 | }
388 |
389 | if requestAddons.Flow == vless.XRV {
390 | err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, input, rawInput, trafficState, ob, ctx)
391 | } else {
392 | // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
393 | err = buf.Copy(serverReader, clientWriter, buf.UpdateActivityCopyOption(timer))
394 | }
395 | if err != nil {
396 | return fmt.Errorf("failed to transfer response payload: %w", err)
397 | }
398 | clientWriter.CloseWrite()
399 | return nil
400 | }
401 |
402 | // if newCtx != nil {
403 | // ctx = newCtx
404 | // }
405 |
406 | if err := task.Run(ctx, postRequest, getResponse); err != nil {
407 | return fmt.Errorf("connection ends: %w", err)
408 | }
409 | return nil
410 | }
411 |
--------------------------------------------------------------------------------
/vless/proxy.go:
--------------------------------------------------------------------------------
1 | // Package proxy contains all proxies used by Xray.
2 | //
3 | // To implement an inbound or outbound proxy, one needs to do the following:
4 | // 1. Implement the interface(s) below.
5 | // 2. Register a config creator through creator.RegisterConfig.
6 | package vless
7 |
8 | import (
9 | "bytes"
10 | "context"
11 | "crypto/rand"
12 | gotls "crypto/tls"
13 | "fmt"
14 | "io"
15 | "math/big"
16 | "runtime"
17 | "strconv"
18 | "sync/atomic"
19 | "time"
20 |
21 | "github.com/5vnetwork/x/common/buf"
22 | "github.com/5vnetwork/x/common/errors"
23 | "github.com/5vnetwork/x/common/net"
24 | "github.com/5vnetwork/x/common/signal"
25 | "github.com/5vnetwork/x/transport/security/tls"
26 | "github.com/pires/go-proxyproto"
27 | )
28 |
29 | var (
30 | Tls13SupportedVersions = []byte{0x00, 0x2b, 0x00, 0x02, 0x03, 0x04}
31 | TlsClientHandShakeStart = []byte{0x16, 0x03}
32 | TlsServerHandShakeStart = []byte{0x16, 0x03, 0x03}
33 | TlsApplicationDataStart = []byte{0x17, 0x03, 0x03}
34 |
35 | Tls13CipherSuiteDic = map[uint16]string{
36 | 0x1301: "TLS_AES_128_GCM_SHA256",
37 | 0x1302: "TLS_AES_256_GCM_SHA384",
38 | 0x1303: "TLS_CHACHA20_POLY1305_SHA256",
39 | 0x1304: "TLS_AES_128_CCM_SHA256",
40 | 0x1305: "TLS_AES_128_CCM_8_SHA256",
41 | }
42 | )
43 |
44 | const (
45 | TlsHandshakeTypeClientHello byte = 0x01
46 | TlsHandshakeTypeServerHello byte = 0x02
47 |
48 | CommandPaddingContinue byte = 0x00
49 | CommandPaddingEnd byte = 0x01
50 | CommandPaddingDirect byte = 0x02
51 | )
52 |
53 | // TrafficState is used to track uplink and downlink of one connection
54 | // It is used by XTLS to determine if switch to raw copy mode, It is used by Vision to calculate padding
55 | type TrafficState struct {
56 | UserUUID []byte
57 | NumberOfPacketToFilter int
58 | EnableXtls bool
59 | IsTLS12orAbove bool
60 | IsTLS bool
61 | Cipher uint16
62 | RemainingServerHello int32
63 |
64 | // reader link state
65 | WithinPaddingBuffers bool
66 | ReaderSwitchToDirectCopy bool
67 | RemainingCommand int32
68 | RemainingContent int32
69 | RemainingPadding int32
70 | CurrentCommand int
71 |
72 | // write link state
73 | IsPadding bool
74 | WriterSwitchToDirectCopy bool
75 | }
76 |
77 | func NewTrafficState(userUUID []byte) *TrafficState {
78 | return &TrafficState{
79 | UserUUID: userUUID,
80 | NumberOfPacketToFilter: 8,
81 | EnableXtls: false,
82 | IsTLS12orAbove: false,
83 | IsTLS: false,
84 | Cipher: 0,
85 | RemainingServerHello: -1,
86 | WithinPaddingBuffers: true,
87 | ReaderSwitchToDirectCopy: false,
88 | RemainingCommand: -1,
89 | RemainingContent: -1,
90 | RemainingPadding: -1,
91 | CurrentCommand: 0,
92 | IsPadding: true,
93 | WriterSwitchToDirectCopy: false,
94 | }
95 | }
96 |
97 | // VisionReader is used to read xtls vision protocol
98 | // Note Vision probably only make sense as the inner most layer of reader, since it need assess traffic state from origin proxy traffic
99 | type VisionReader struct {
100 | buf.Reader
101 | trafficState *TrafficState
102 | ctx context.Context
103 | }
104 |
105 | func NewVisionReader(reader buf.Reader, state *TrafficState, context context.Context) *VisionReader {
106 | return &VisionReader{
107 | Reader: reader,
108 | trafficState: state,
109 | ctx: context,
110 | }
111 | }
112 |
113 | // The buffer read out does not contain a complete record and a incomplete record. It either contains a complete record or an incomplete record
114 | func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
115 | buffer, err := w.Reader.ReadMultiBuffer()
116 | if !buffer.IsEmpty() {
117 | if w.trafficState.WithinPaddingBuffers || w.trafficState.NumberOfPacketToFilter > 0 {
118 | mb2 := make(buf.MultiBuffer, 0, len(buffer))
119 | for _, b := range buffer {
120 | newbuffer := XtlsUnpadding(b, w.trafficState, w.ctx)
121 | if newbuffer.Len() > 0 {
122 | mb2 = append(mb2, newbuffer)
123 | }
124 | }
125 | buffer = mb2
126 | if w.trafficState.RemainingContent > 0 || w.trafficState.RemainingPadding > 0 || w.trafficState.CurrentCommand == 0 {
127 | w.trafficState.WithinPaddingBuffers = true
128 | // The following two cases: last block has been fully read
129 | } else if w.trafficState.CurrentCommand == 1 {
130 | w.trafficState.WithinPaddingBuffers = false
131 | } else if w.trafficState.CurrentCommand == 2 {
132 | w.trafficState.WithinPaddingBuffers = false
133 | w.trafficState.ReaderSwitchToDirectCopy = true
134 | } else {
135 | // log.Debug().Msg("XtlsRead unknown command")
136 | }
137 | }
138 | if w.trafficState.NumberOfPacketToFilter > 0 {
139 | XtlsFilterTls(buffer, w.trafficState, w.ctx)
140 | }
141 | }
142 | return buffer, err
143 | }
144 |
145 | // VisionWriter is used to write xtls vision protocol
146 | // Note Vision probably only make sense as the inner most layer of writer, since it need assess traffic state from origin proxy traffic
147 | type VisionWriter struct {
148 | buf.Writer
149 | trafficState *TrafficState
150 | ctx context.Context
151 | writeOnceUserUUID []byte
152 | }
153 |
154 | func NewVisionWriter(writer buf.Writer, state *TrafficState, context context.Context) *VisionWriter {
155 | w := make([]byte, len(state.UserUUID))
156 | copy(w, state.UserUUID)
157 | return &VisionWriter{
158 | Writer: writer,
159 | trafficState: state,
160 | ctx: context,
161 | writeOnceUserUUID: w,
162 | }
163 | }
164 |
165 | func (w *VisionWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
166 | if w.trafficState.NumberOfPacketToFilter > 0 {
167 | XtlsFilterTls(mb, w.trafficState, w.ctx)
168 | }
169 | if w.trafficState.IsPadding {
170 | if len(mb) == 1 && mb[0] == nil {
171 | mb[0] = XtlsPadding(nil, CommandPaddingContinue, &w.writeOnceUserUUID, true, w.ctx) // we do a long padding to hide vless header
172 | return w.Writer.WriteMultiBuffer(mb)
173 | }
174 | mb = ReshapeMultiBuffer(w.ctx, mb)
175 | longPadding := w.trafficState.IsTLS
176 | // a b a block. The last b might or might not be the last block
177 | for i, b := range mb {
178 | // if app data is found
179 | if w.trafficState.IsTLS && b.Len() >= 6 && bytes.Equal(TlsApplicationDataStart, b.BytesTo(3)) {
180 | if w.trafficState.EnableXtls {
181 | // This WriteMultiBuffer is the last call
182 | w.trafficState.WriterSwitchToDirectCopy = true
183 | }
184 | var command byte = CommandPaddingContinue
185 | if i == len(mb)-1 {
186 | command = CommandPaddingEnd
187 | if w.trafficState.EnableXtls {
188 | command = CommandPaddingDirect
189 | }
190 | }
191 | mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, true, w.ctx)
192 | w.trafficState.IsPadding = false // padding going to end
193 | longPadding = false
194 | continue
195 | } else if !w.trafficState.IsTLS12orAbove && w.trafficState.NumberOfPacketToFilter <= 1 { // For compatibility with earlier vision receiver, we finish padding 1 packet early
196 | w.trafficState.IsPadding = false
197 | mb[i] = XtlsPadding(b, CommandPaddingEnd, &w.writeOnceUserUUID, longPadding, w.ctx)
198 | break
199 | }
200 | var command byte = CommandPaddingContinue
201 | if i == len(mb)-1 && !w.trafficState.IsPadding {
202 | command = CommandPaddingEnd
203 | if w.trafficState.EnableXtls {
204 | command = CommandPaddingDirect
205 | }
206 | }
207 | mb[i] = XtlsPadding(b, command, &w.writeOnceUserUUID, longPadding, w.ctx)
208 | }
209 | }
210 | return w.Writer.WriteMultiBuffer(mb)
211 | }
212 |
213 | // ReshapeMultiBuffer prepare multi buffer for padding structure (max 21 bytes)
214 | func ReshapeMultiBuffer(ctx context.Context, buffer buf.MultiBuffer) buf.MultiBuffer {
215 | needReshape := 0
216 | for _, b := range buffer {
217 | if b.Len() >= buf.Size-21 {
218 | needReshape += 1
219 | }
220 | }
221 | if needReshape == 0 {
222 | return buffer
223 | }
224 | mb2 := make(buf.MultiBuffer, 0, len(buffer)+needReshape)
225 | toPrint := ""
226 | for i, buffer1 := range buffer {
227 | if buffer1.Len() >= buf.Size-21 {
228 | index := int32(bytes.LastIndex(buffer1.Bytes(), TlsApplicationDataStart))
229 | if index < 21 || index > buf.Size-21 {
230 | index = buf.Size / 2
231 | }
232 | buffer2 := buf.New()
233 | buffer2.Write(buffer1.BytesFrom(index))
234 | buffer1.Resize(0, index)
235 | mb2 = append(mb2, buffer1, buffer2)
236 | toPrint += " " + strconv.Itoa(int(buffer1.Len())) + " " + strconv.Itoa(int(buffer2.Len()))
237 | } else {
238 | mb2 = append(mb2, buffer1)
239 | toPrint += " " + strconv.Itoa(int(buffer1.Len()))
240 | }
241 | buffer[i] = nil
242 | }
243 | buffer = buffer[:0]
244 | return mb2
245 | }
246 |
247 | // XtlsPadding add padding to eliminate length signature during tls handshake
248 | func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, longPadding bool, ctx context.Context) *buf.Buffer {
249 | var contentLen int32 = 0
250 | var paddingLen int32 = 0
251 | if b != nil {
252 | contentLen = b.Len()
253 | }
254 | if contentLen < 900 && longPadding {
255 | l, err := rand.Int(rand.Reader, big.NewInt(500))
256 | if err != nil {
257 | // log.Debug().Msg("failed to generate padding")
258 | }
259 | paddingLen = int32(l.Int64()) + 900 - contentLen
260 | } else {
261 | l, err := rand.Int(rand.Reader, big.NewInt(256))
262 | if err != nil {
263 | // log.Debug().Msg("failed to generate padding")
264 | }
265 | paddingLen = int32(l.Int64())
266 | }
267 | if paddingLen > buf.Size-21-contentLen {
268 | paddingLen = buf.Size - 21 - contentLen
269 | }
270 | newbuffer := buf.New()
271 | if userUUID != nil {
272 | newbuffer.Write(*userUUID)
273 | *userUUID = nil
274 | }
275 | newbuffer.Write([]byte{command, byte(contentLen >> 8), byte(contentLen), byte(paddingLen >> 8), byte(paddingLen)})
276 | if b != nil {
277 | newbuffer.Write(b.Bytes())
278 | b.Release()
279 | b = nil
280 | }
281 | newbuffer.Extend(paddingLen)
282 | return newbuffer
283 | }
284 |
285 | // XtlsUnpadding remove padding and parse command
286 | func XtlsUnpadding(b *buf.Buffer, s *TrafficState, ctx context.Context) *buf.Buffer {
287 | if s.RemainingCommand == -1 && s.RemainingContent == -1 && s.RemainingPadding == -1 { // inital state
288 | if b.Len() >= 21 && bytes.Equal(s.UserUUID, b.BytesTo(16)) {
289 | b.AdvanceStart(16)
290 | s.RemainingCommand = 5
291 | } else {
292 | return b
293 | }
294 | }
295 | newbuffer := buf.New()
296 | for b.Len() > 0 {
297 | if s.RemainingCommand > 0 {
298 | data, err := b.ReadByte()
299 | if err != nil {
300 | return newbuffer
301 | }
302 | switch s.RemainingCommand {
303 | case 5:
304 | s.CurrentCommand = int(data)
305 | case 4:
306 | s.RemainingContent = int32(data) << 8
307 | case 3:
308 | s.RemainingContent = s.RemainingContent | int32(data)
309 | case 2:
310 | s.RemainingPadding = int32(data) << 8
311 | case 1:
312 | s.RemainingPadding = s.RemainingPadding | int32(data)
313 | }
314 | s.RemainingCommand--
315 | } else if s.RemainingContent > 0 {
316 | len := s.RemainingContent
317 | if b.Len() < len {
318 | len = b.Len()
319 | }
320 | data, err := b.ReadBytes(len)
321 | if err != nil {
322 | return newbuffer
323 | }
324 | newbuffer.Write(data)
325 | s.RemainingContent -= len
326 | } else { // remainingPadding > 0
327 | len := s.RemainingPadding
328 | if b.Len() < len {
329 | len = b.Len()
330 | }
331 | b.AdvanceStart(len)
332 | s.RemainingPadding -= len
333 | }
334 | if s.RemainingCommand <= 0 && s.RemainingContent <= 0 && s.RemainingPadding <= 0 { // this block done
335 | if s.CurrentCommand == 0 {
336 | s.RemainingCommand = 5
337 | } else {
338 | s.RemainingCommand = -1 // set to initial state
339 | s.RemainingContent = -1
340 | s.RemainingPadding = -1
341 | if b.Len() > 0 { // shouldn't happen
342 | newbuffer.Write(b.Bytes())
343 | }
344 | break
345 | }
346 | }
347 | }
348 | b.Release()
349 | b = nil
350 | return newbuffer
351 | }
352 |
353 | // XtlsFilterTls filter and recognize tls 1.3 and other Debug
354 | func XtlsFilterTls(buffer buf.MultiBuffer, trafficState *TrafficState, ctx context.Context) {
355 | for _, b := range buffer {
356 | if b == nil {
357 | continue
358 | }
359 | trafficState.NumberOfPacketToFilter--
360 | if b.Len() >= 6 {
361 | startsBytes := b.BytesTo(6)
362 | if bytes.Equal(TlsServerHandShakeStart, startsBytes[:3]) && startsBytes[5] == TlsHandshakeTypeServerHello {
363 | trafficState.RemainingServerHello = (int32(startsBytes[3])<<8 | int32(startsBytes[4])) + 5
364 | trafficState.IsTLS12orAbove = true
365 | trafficState.IsTLS = true
366 | if b.Len() >= 79 && trafficState.RemainingServerHello >= 79 {
367 | sessionIdLen := int32(b.Byte(43))
368 | cipherSuite := b.BytesRange(43+sessionIdLen+1, 43+sessionIdLen+3)
369 | trafficState.Cipher = uint16(cipherSuite[0])<<8 | uint16(cipherSuite[1])
370 | }
371 | } else if bytes.Equal(TlsClientHandShakeStart, startsBytes[:2]) && startsBytes[5] == TlsHandshakeTypeClientHello {
372 | trafficState.IsTLS = true
373 | }
374 | }
375 | if trafficState.RemainingServerHello > 0 {
376 | end := trafficState.RemainingServerHello
377 | if end > b.Len() {
378 | end = b.Len()
379 | }
380 | trafficState.RemainingServerHello -= b.Len()
381 | if bytes.Contains(b.BytesTo(end), Tls13SupportedVersions) {
382 | v, ok := Tls13CipherSuiteDic[trafficState.Cipher]
383 | if !ok {
384 | v = "Old cipher: " + strconv.FormatUint(uint64(trafficState.Cipher), 16)
385 | } else if v != "TLS_AES_128_CCM_8_SHA256" {
386 | trafficState.EnableXtls = true
387 | }
388 | trafficState.NumberOfPacketToFilter = 0
389 | return
390 | } else if trafficState.RemainingServerHello <= 0 {
391 | trafficState.NumberOfPacketToFilter = 0
392 | return
393 | }
394 | }
395 | }
396 | }
397 |
398 | // UnwrapRawConn support unwrap stats, tls, utls, reality and proxyproto conn and get raw tcp conn from it
399 | func UnwrapRawConn(conn net.Conn) (net.Conn, *atomic.Uint64, *atomic.Uint64) {
400 | var readCounter, writerCounter *atomic.Uint64
401 | if conn != nil {
402 | if xc, ok := conn.(*tls.Conn); ok {
403 | conn = xc.NetConn()
404 | } else if goTlsConn, ok := conn.(*gotls.Conn); ok {
405 | conn = goTlsConn.NetConn()
406 | } else if utlsConn, ok := conn.(*tls.UConn); ok {
407 | conn = utlsConn.NetConn()
408 | }
409 | // else if realityConn, ok := conn.(*reality.Conn); ok {
410 | // conn = realityConn.NetConn()
411 | // } else if realityUConn, ok := conn.(*reality.UConn); ok {
412 | // conn = realityUConn.NetConn()
413 | // }
414 | if pc, ok := conn.(*proxyproto.Conn); ok {
415 | conn = pc.Raw()
416 | // 8192 > 4096, there is no need to process pc's bufReader
417 | }
418 | }
419 | return conn, readCounter, writerCounter
420 | }
421 |
422 | // CopyRawConnIfExist use the most efficient copy method.
423 | // - If caller don't want to turn on splice, do not pass in both reader conn and writer conn
424 | // - writer are from *transport.Link
425 | func CopyRawConnIfExist(ctx context.Context, readerConn net.Conn, writerConn net.Conn, writer buf.Writer, timer *signal.ActivityChecker, inTimer *signal.ActivityChecker) error {
426 | readerConn, readCounter, _ := UnwrapRawConn(readerConn)
427 | writerConn, _, writeCounter := UnwrapRawConn(writerConn)
428 | reader := buf.NewReader(readerConn)
429 | if runtime.GOOS != "linux" && runtime.GOOS != "android" {
430 | return readV(ctx, reader, writer, timer, readCounter)
431 | }
432 | tc, ok := writerConn.(*net.TCPConn)
433 | if !ok || readerConn == nil || writerConn == nil {
434 | return readV(ctx, reader, writer, timer, readCounter)
435 | }
436 | inbound := InboundFromContext(ctx)
437 | if inbound == nil || inbound.CanSpliceCopy == 3 {
438 | return readV(ctx, reader, writer, timer, readCounter)
439 | }
440 | outbounds := OutboundsFromContext(ctx)
441 | if len(outbounds) == 0 {
442 | return readV(ctx, reader, writer, timer, readCounter)
443 | }
444 | for _, ob := range outbounds {
445 | if ob.CanSpliceCopy == 3 {
446 | return readV(ctx, reader, writer, timer, readCounter)
447 | }
448 | }
449 |
450 | for {
451 | inbound := InboundFromContext(ctx)
452 | outbounds := OutboundsFromContext(ctx)
453 | var splice = inbound.CanSpliceCopy == 1
454 | for _, ob := range outbounds {
455 | if ob.CanSpliceCopy != 1 {
456 | splice = false
457 | }
458 | }
459 | if splice {
460 | //runtime.Gosched() // necessary
461 | time.Sleep(time.Millisecond) // without this, there will be a rare ssl error for freedom splice
462 | timer.SetTimeout(8 * time.Hour) // prevent leak, just in case
463 | if inTimer != nil {
464 | inTimer.SetTimeout(8 * time.Hour)
465 | }
466 | w, err := tc.ReadFrom(readerConn)
467 | if readCounter != nil {
468 | readCounter.Add(uint64(w)) // outbound stats
469 | }
470 | if writeCounter != nil {
471 | writeCounter.Add(uint64(w)) // inbound stats
472 | }
473 | if inbound.DownCounter != nil {
474 | inbound.DownCounter.Add(uint64(w)) // user stats
475 | }
476 | if err != nil && !errors.Is(err, io.EOF) {
477 | return err
478 | }
479 | return nil
480 | }
481 | buffer, err := reader.ReadMultiBuffer()
482 | if !buffer.IsEmpty() {
483 | if readCounter != nil {
484 | readCounter.Add(uint64(buffer.Len()))
485 | }
486 | timer.Update()
487 | if werr := writer.WriteMultiBuffer(buffer); werr != nil {
488 | return werr
489 | }
490 | }
491 | if err != nil {
492 | return err
493 | }
494 | }
495 | }
496 |
497 | func readV(ctx context.Context, reader buf.Reader, writer buf.Writer, timer *signal.ActivityChecker, readCounter *atomic.Uint64) error {
498 | if err := buf.Copy(reader, writer, buf.UpdateActivityCopyOption(timer), buf.AddToStatCounter(readCounter)); err != nil {
499 | return fmt.Errorf("failed to copy, %w", err)
500 | }
501 | return nil
502 | }
503 |
--------------------------------------------------------------------------------
/vless/vless.go:
--------------------------------------------------------------------------------
1 | // Package vless contains the implementation of VLess protocol and transportation.
2 | //
3 | // VLess contains both inbound and outbound connections. VLess inbound is usually used on servers
4 | // together with 'freedom' to talk to final destination, while VLess outbound is usually used on
5 | // clients with 'socks' for proxying.
6 | package vless
7 |
8 | import (
9 | "context"
10 | "sync/atomic"
11 |
12 | "github.com/5vnetwork/x/common/net"
13 | "github.com/5vnetwork/x/common/signal"
14 | )
15 |
16 | const (
17 | XRV = "xtls-rprx-vision"
18 | )
19 |
20 | type InboundInfo struct {
21 | CanSpliceCopy int
22 | Conn net.Conn
23 | Timer *signal.ActivityChecker
24 | UpCounter *atomic.Uint64
25 | DownCounter *atomic.Uint64
26 | }
27 |
28 | var ContextKeyInbound = 0
29 |
30 | func InboundFromContext(ctx context.Context) *InboundInfo {
31 | ib, _ := ctx.Value(ContextKeyInbound).(*InboundInfo)
32 | return ib
33 | }
34 |
35 | func WithInbound(ctx context.Context, ib *InboundInfo) context.Context {
36 | return context.WithValue(ctx, ContextKeyInbound, ib)
37 | }
38 |
39 | type OutboundInfo struct {
40 | Target net.Destination
41 | Conn net.Conn
42 | // 1 yes, 2 maybe, we'll see, 3 no
43 | CanSpliceCopy int
44 | }
45 |
46 | var ContextKeyOutbound = 1
47 |
48 | func OutboundsFromContext(ctx context.Context) []*OutboundInfo {
49 | ob, _ := ctx.Value(ContextKeyOutbound).([]*OutboundInfo)
50 | return ob
51 | }
52 |
53 | func WithOutbounds(ctx context.Context, ob []*OutboundInfo) context.Context {
54 | return context.WithValue(ctx, ContextKeyOutbound, ob)
55 | }
56 |
--------------------------------------------------------------------------------
/vless/xudp/xudp.go:
--------------------------------------------------------------------------------
1 | package xudp
2 |
3 | import (
4 | "context"
5 | "crypto/rand"
6 | "encoding/base64"
7 | "io"
8 | "strconv"
9 | "time"
10 |
11 | "github.com/5vnetwork/x/common/buf"
12 | "github.com/5vnetwork/x/common/net"
13 | "github.com/5vnetwork/x/common/net/udp"
14 | "github.com/5vnetwork/x/common/platform"
15 | "github.com/5vnetwork/x/common/serial/address_parser"
16 | "github.com/5vnetwork/x/common/session"
17 |
18 | "lukechampine.com/blake3"
19 | )
20 |
21 | var AddrParser = address_parser.VAddressSerializer
22 |
23 | var (
24 | BaseKey []byte
25 | )
26 |
27 | func init() {
28 |
29 | rand.Read(BaseKey)
30 | go func() {
31 | time.Sleep(100 * time.Millisecond) // this is not nice, but need to give some time for Android to setup ENV
32 | if raw := platform.NewEnvFlag(platform.XUDPBaseKey).GetValue(func() string { return "" }); raw != "" {
33 | if BaseKey, _ = base64.RawURLEncoding.DecodeString(raw); len(BaseKey) == 32 {
34 | return
35 | }
36 | panic(platform.XUDPBaseKey + ": invalid value (BaseKey must be 32 bytes): " + raw + " len " + strconv.Itoa(len(BaseKey)))
37 | }
38 | }()
39 | }
40 |
41 | func GetGlobalID(ctx context.Context) (globalID [8]byte) {
42 | // if cone := ctx.Value("cone"); cone == nil || !cone.(bool) { // cone is nil only in some unit tests
43 | // return
44 | // }
45 | if info := session.InfoFromContext(ctx); info != nil && info.Source.Network == net.Network_UDP &&
46 | (info.InboundProtocol == "dokodemo-door" || info.InboundProtocol == "socks" || info.InboundProtocol == "shadowsocks" ||
47 | info.InboundTag == "wfp" || info.InboundTag == "tun" || info.InboundTag == "gvisor") {
48 | h := blake3.New(8, BaseKey)
49 | h.Write([]byte(info.Source.String()))
50 | copy(globalID[:], h.Sum(nil))
51 | }
52 | // info := session.InfoFromContext(ctx)
53 | // if info != nil && info.UdpUuid.IsSet() {
54 | // copy(globalID[:], info.UdpUuid.Bytes()[:8])
55 | // }
56 | // rand.Read(globalID[:])
57 | return
58 | }
59 |
60 | func NewPacketWriter(writer buf.Writer, dest net.Destination, globalID [8]byte) *PacketWriter {
61 | return &PacketWriter{
62 | Writer: writer,
63 | Dest: dest,
64 | GlobalID: globalID,
65 | }
66 | }
67 |
68 | type PacketWriter struct {
69 | Writer buf.Writer
70 | Dest net.Destination
71 | GlobalID [8]byte
72 | }
73 |
74 | func (w *PacketWriter) CloseWrite() error {
75 | return nil
76 | }
77 |
78 | func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
79 | defer buf.ReleaseMulti(mb)
80 | mb2Write := make(buf.MultiBuffer, 0, len(mb))
81 | for _, b := range mb {
82 | length := b.Len()
83 | if length == 0 || length+666 > buf.Size {
84 | continue
85 | }
86 |
87 | eb := buf.New()
88 | eb.Write([]byte{0, 0, 0, 0}) // Meta data length; Mux Session ID
89 | if w.Dest.Network == net.Network_UDP {
90 | eb.WriteByte(1) // New
91 | eb.WriteByte(1) // Opt
92 | eb.WriteByte(2) // UDP
93 | AddrParser.WriteAddressPort(eb, w.Dest.Address, w.Dest.Port)
94 | // if b.UDP != nil { // make sure it's user's proxy request
95 | // eb.Write(w.GlobalID[:]) // no need to check whether it's empty
96 | // }
97 | w.Dest.Network = net.Network_Unknown
98 | } else {
99 | eb.WriteByte(2) // Keep
100 | eb.WriteByte(1) // Opt
101 | // if b.UDP != nil {
102 | eb.WriteByte(2) // UDP
103 | AddrParser.WriteAddressPort(eb, w.Dest.Address, w.Dest.Port)
104 | // }
105 | }
106 | l := eb.Len() - 2
107 | eb.SetByte(0, byte(l>>8))
108 | eb.SetByte(1, byte(l))
109 | eb.WriteByte(byte(length >> 8))
110 | eb.WriteByte(byte(length))
111 | eb.Write(b.Bytes())
112 |
113 | mb2Write = append(mb2Write, eb)
114 | }
115 | if mb2Write.IsEmpty() {
116 | return nil
117 | }
118 | return w.Writer.WriteMultiBuffer(mb2Write)
119 | }
120 |
121 | func (w *PacketWriter) WritePacket(p *udp.Packet) error {
122 | defer p.Release()
123 | length := p.Payload.Len()
124 | if length == 0 || length+666 > buf.Size {
125 | return nil
126 | }
127 |
128 | eb := buf.New()
129 | eb.Write([]byte{0, 0, 0, 0}) // Meta data length; Mux Session ID
130 | if w.Dest.Network == net.Network_UDP {
131 | eb.WriteByte(1) // New
132 | eb.WriteByte(1) // Opt
133 | eb.WriteByte(2) // UDP
134 | AddrParser.WriteAddressPort(eb, w.Dest.Address, w.Dest.Port)
135 | // make sure it's user's proxy request
136 | eb.Write(w.GlobalID[:]) // no need to check whether it's empty
137 | w.Dest.Network = net.Network_Unknown
138 | } else {
139 | eb.WriteByte(2) // Keep
140 | eb.WriteByte(1) // Opt
141 | eb.WriteByte(2) // UDP
142 | AddrParser.WriteAddressPort(eb, w.Dest.Address, w.Dest.Port)
143 | }
144 | l := eb.Len() - 2
145 | eb.SetByte(0, byte(l>>8))
146 | eb.SetByte(1, byte(l))
147 | eb.WriteByte(byte(length >> 8))
148 | eb.WriteByte(byte(length))
149 | eb.Write(p.Payload.Bytes())
150 |
151 | return w.Writer.WriteMultiBuffer(buf.MultiBuffer{eb})
152 | }
153 |
154 | func NewPacketReader(reader io.Reader) *PacketReader {
155 | return &PacketReader{
156 | Reader: reader,
157 | cache: make([]byte, 2),
158 | }
159 | }
160 |
161 | type PacketReader struct {
162 | Reader io.Reader
163 | cache []byte
164 | }
165 |
166 | func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
167 | for {
168 | if _, err := io.ReadFull(r.Reader, r.cache); err != nil {
169 | return nil, err
170 | }
171 | l := int32(r.cache[0])<<8 | int32(r.cache[1])
172 | if l < 4 {
173 | return nil, io.EOF
174 | }
175 | b := buf.New()
176 | if _, err := b.ReadFullFrom(r.Reader, l); err != nil {
177 | b.Release()
178 | return nil, err
179 | }
180 | discard := false
181 | switch b.Byte(2) {
182 | case 2:
183 | if l > 4 && b.Byte(4) == 2 { // MUST check the flag first
184 | b.AdvanceStart(5)
185 | // b.Clear() will be called automatically if all data had been read.
186 | // TODO: the addr might be different from flow. check src of xray
187 | _, _, err := AddrParser.ReadAddressPort(nil, b)
188 | if err != nil {
189 | b.Release()
190 | return nil, err
191 | }
192 | }
193 | case 4:
194 | discard = true
195 | default:
196 | b.Release()
197 | return nil, io.EOF
198 | }
199 | b.Clear() // in case there is padding (empty bytes) attached
200 | if b.Byte(3) == 1 {
201 | if _, err := io.ReadFull(r.Reader, r.cache); err != nil {
202 | b.Release()
203 | return nil, err
204 | }
205 | length := int32(r.cache[0])<<8 | int32(r.cache[1])
206 | if length > 0 {
207 | if _, err := b.ReadFullFrom(r.Reader, length); err != nil {
208 | b.Release()
209 | return nil, err
210 | }
211 | if !discard {
212 | return buf.MultiBuffer{b}, nil
213 | }
214 | }
215 | }
216 | b.Release()
217 | }
218 | }
219 |
220 | func (r *PacketReader) ReadPacket() (*udp.Packet, error) {
221 | for {
222 | if _, err := io.ReadFull(r.Reader, r.cache); err != nil {
223 | return nil, err
224 | }
225 | l := int32(r.cache[0])<<8 | int32(r.cache[1])
226 | if l < 4 {
227 | return nil, io.EOF
228 | }
229 | b := buf.New()
230 | var src net.Destination
231 | if _, err := b.ReadFullFrom(r.Reader, l); err != nil {
232 | b.Release()
233 | return nil, err
234 | }
235 | discard := false
236 | switch b.Byte(2) {
237 | case 2:
238 | if l > 4 && b.Byte(4) == 2 { // MUST check the flag first
239 | b.AdvanceStart(5)
240 | // b.Clear() will be called automatically if all data had been read.
241 | addr, port, err := AddrParser.ReadAddressPort(nil, b)
242 | if err != nil {
243 | b.Release()
244 | return nil, err
245 | }
246 | src = net.Destination{
247 | Network: net.Network_UDP,
248 | Address: addr,
249 | Port: port,
250 | }
251 | }
252 | case 4:
253 | discard = true
254 | default:
255 | b.Release()
256 | return nil, io.EOF
257 | }
258 | b.Clear() // in case there is padding (empty bytes) attached
259 | if b.Byte(3) == 1 {
260 | if _, err := io.ReadFull(r.Reader, r.cache); err != nil {
261 | b.Release()
262 | return nil, err
263 | }
264 | length := int32(r.cache[0])<<8 | int32(r.cache[1])
265 | if length > 0 {
266 | if _, err := b.ReadFullFrom(r.Reader, length); err != nil {
267 | b.Release()
268 | return nil, err
269 | }
270 | if !discard {
271 | return &udp.Packet{Payload: b, Source: src}, nil
272 | }
273 | }
274 | }
275 | b.Release()
276 | }
277 | }
278 |
--------------------------------------------------------------------------------