mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-23 18:16:50 +08:00
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
416f2df11c | ||
![]() |
29c6318ffe | ||
![]() |
9ee9a0634e | ||
![]() |
544f7661ca | ||
![]() |
121eb7b4fc | ||
![]() |
3168d27b0b | ||
![]() |
b98d060ee0 | ||
![]() |
26d49df22e | ||
![]() |
1d450cfbd2 | ||
![]() |
017f53b5fc | ||
![]() |
0735053348 | ||
![]() |
e41a61c6f7 | ||
![]() |
a9715295b8 | ||
![]() |
f0b0a7cd4b | ||
![]() |
cefae55d7c | ||
![]() |
84eeb56ae4 | ||
![]() |
eba2906d3a | ||
![]() |
cc2849025d | ||
![]() |
c1ad35fba8 | ||
![]() |
447a49d16a | ||
![]() |
51504c624c | ||
![]() |
98a2e2c7a1 | ||
![]() |
a476310aec | ||
![]() |
b8924782a1 |
77
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
77
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -11,34 +11,77 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: I searched issues and did not find any similar issues.
|
- label: I searched issues and did not find any similar issues.
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Version
|
label: Version
|
||||||
description: Xray-core version
|
description: Version of Xray-core
|
||||||
render: shell
|
validations:
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Description
|
label: Description
|
||||||
description: Please provide a detailed description of the bug. And information that you consider valuable.
|
description: Please provide a detailed description of the error. And the information you think valuable.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Reproduction
|
label: Reproduction Method
|
||||||
description: |-
|
description: |-
|
||||||
Provide method to reproduce the bug.
|
Based on the configuration you provided below, provide the method to reproduce the bug.
|
||||||
Please provide config that can reproduce the problem, including both the server and client.
|
validations:
|
||||||
Do not paste a large exported config here. Removing unnecessary inbounds, outbounds, route rules, and options. This cloud help us locate the problem if you really want to get help.
|
required: true
|
||||||
Even if you are using a GUI/script/panel, please follow the above requirements.
|
- type: markdown
|
||||||
DO NOT just write "I'm using xxx GUI/ xxx panel" instead of providing config. We do not have the energy or obligation to find the software and spend time reproducing according to the description.
|
attributes:
|
||||||
|
value: |-
|
||||||
|
## Configuration and Log Section
|
||||||
|
|
||||||
|
### For config
|
||||||
|
Please provide the configuration files that can reproduce the problem, including the server and client.
|
||||||
|
Don't just paste a big exported config file here. Eliminate useless inbound/outbound, rules, options, this can help determine the problem, if you really want to get help.
|
||||||
|
|
||||||
|
### For logs
|
||||||
|
Please set the log level to debug first.
|
||||||
|
Restart Xray-core, then operate according to the reproduction method, try to reduce the irrelevant part in the log.
|
||||||
|
Remember to delete parts with personal information (such as UUID and IP).
|
||||||
|
Provide the log of Xray-core, not the log output by the panel or other things.
|
||||||
|
|
||||||
|
### Finally
|
||||||
|
After removing parts that do not affect reproduction, provide the actual running **complete** file, do not only provide inbound or outbound or a few lines of logs based on your own judgment.
|
||||||
|
Put the content between the preset ```<details><pre><code>``` ```</code></pre></details>``` in the text box.
|
||||||
|
If the problem is very clear that only related to one end (such as core startup failure/crash after correctly writing the config according to the documents), N/A can be filled in for unnecessary areas below.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Client config
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: log
|
label: Server config
|
||||||
description: |-
|
value: |-
|
||||||
Set the log level to debug.
|
<details><pre><code>
|
||||||
Please Restart Xray-core, and then follow the reproduction method to reduce irrelevant parts in log.
|
|
||||||
Remember to remove personal information such as UUID, IP.
|
</code></pre></details>
|
||||||
Provid complete log, DO NOT just paste the the parts that you think necessary based on your own judgment.
|
validations:
|
||||||
render: shell
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Client log
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Server log
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
|
validations:
|
||||||
|
required: true
|
69
.github/ISSUE_TEMPLATE/bug_report_zh.yml
vendored
69
.github/ISSUE_TEMPLATE/bug_report_zh.yml
vendored
@@ -11,11 +11,12 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: 我搜索了issues,没有发现已提出的类似问题。
|
- label: 我搜索了issues,没有发现已提出的类似问题。
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: 版本
|
label: 版本
|
||||||
description: 使用的Xray-core版本
|
description: 使用的Xray-core版本
|
||||||
render: shell
|
validations:
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: 描述
|
label: 描述
|
||||||
@@ -26,19 +27,61 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: 重现方式
|
label: 重现方式
|
||||||
description: |-
|
description: |-
|
||||||
提供重现BUG方法。
|
基于你下面提供的配置,提供重现BUG方法。
|
||||||
请提供可以重现问题的配置文件,包括服务端和客户端
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |-
|
||||||
|
## 配置与日志部分
|
||||||
|
|
||||||
|
### 对于配置文件
|
||||||
|
请提供可以重现问题的配置文件,包括服务端和客户端。
|
||||||
不要直接在这里黏贴一大段导出的 config 文件。去掉无用的出入站、规则、选项,这可以帮助确定问题,如果你真的想得到帮助。
|
不要直接在这里黏贴一大段导出的 config 文件。去掉无用的出入站、规则、选项,这可以帮助确定问题,如果你真的想得到帮助。
|
||||||
即使你在使用图形客户端/脚本/面板,也请遵照上述要求。
|
|
||||||
不要直接用“我使用xxx客户端/xxx面板”替代config,我们没有精力也没有义务去找到项目再花时间按描述重新问题。
|
### 对于日志
|
||||||
|
请先将日志等级设置为 debug.
|
||||||
|
重启 Xray-core ,再按复现方式操作,尽量减少日志中的无关部分。
|
||||||
|
记得删除有关个人信息(如UUID与IP)的部分。
|
||||||
|
提供 Xray-core 的日志,而不是面板或者别的东西输出的日志。
|
||||||
|
|
||||||
|
### 最后
|
||||||
|
在去掉不影响复现的部分后,提供实际运行的**完整**文件,不要出于自己的判断只提供入站出站或者几行日志。
|
||||||
|
把内容放在文本框预置的 ```<details><pre><code>``` 和 ```</code></pre></details>``` 中间。
|
||||||
|
如果问题十分明确只出现在某一端(如按文档正确编写配置后核心启动失败/崩溃),可以在下面不需要的项目填入N/A.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 客户端配置
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: 日志
|
label: 服务端配置
|
||||||
description: |-
|
value: |-
|
||||||
请先将日志等级设置为 debug.
|
<details><pre><code>
|
||||||
重启 Xray-core ,再按复现方式操作,尽量减少日志中的无关部分。
|
|
||||||
记得删除有关个人信息(如UUID与IP)的部分。
|
</code></pre></details>
|
||||||
提供完整的日志,不要仅提供你自己觉得有用的部分。
|
validations:
|
||||||
render: shell
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 客户端日志
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 服务端日志
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -1,5 +1,11 @@
|
|||||||
name: Build and Release
|
name: Build and Release
|
||||||
|
|
||||||
|
# NOTE: This Github Actions file depends on the Makefile.
|
||||||
|
# Building the correct package requires the correct binaries generated by the Makefile. To
|
||||||
|
# ensure the correct output, the Makefile must accept the appropriate input and compile the
|
||||||
|
# correct file with the correct name. If you need to modify this file, please ensure it won't
|
||||||
|
# disrupt the Makefile.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
|
18
Makefile
18
Makefile
@@ -2,14 +2,16 @@ NAME = xray
|
|||||||
|
|
||||||
VERSION=$(shell git describe --always --dirty)
|
VERSION=$(shell git describe --always --dirty)
|
||||||
|
|
||||||
export GOARCH ?=
|
# NOTE: This MAKEFILE can be used to build Xray-core locally and in Automatic workflows. It is \
|
||||||
export GOOS ?=
|
provided for convinience in automatic building and functions as a part of it.
|
||||||
|
# NOTE: If you need to modify this file, please be aware that:\
|
||||||
ifdef GOARCH
|
- This file is not the main Makefile; it only accepts environment variables and builds the \
|
||||||
ifeq ($(GOOS),darwin)
|
binary.\
|
||||||
NAME:=$(NAME)-$(GOARCH)
|
- Automatic building expects the correct binaries to be built by this Makefile. If you \
|
||||||
endif
|
intend to propose a change to this Makefile, carefully review the file below and ensure \
|
||||||
endif
|
that the change will not accidently break the automatic building:\
|
||||||
|
.github/workflows/release.yml \
|
||||||
|
Otherwise it is recommended to contact the project maintainers.
|
||||||
|
|
||||||
LDFLAGS = -X github.com/xtls/xray-core/core.build=$(VERSION) -s -w -buildid=
|
LDFLAGS = -X github.com/xtls/xray-core/core.build=$(VERSION) -s -w -buildid=
|
||||||
PARAMS = -trimpath -ldflags "$(LDFLAGS)" -v
|
PARAMS = -trimpath -ldflags "$(LDFLAGS)" -v
|
||||||
|
@@ -21,11 +21,10 @@
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
- Linux Script
|
- Linux Script
|
||||||
- [XTLS/Xray-install](https://github.com/XTLS/Xray-install)
|
- [XTLS/Xray-install](https://github.com/XTLS/Xray-install) (**Official**)
|
||||||
- [team-cloudchaser/tempest](https://github.com/team-cloudchaser/tempest) (supports [`systemd`](https://systemd.io) and [OpenRC](https://github.com/OpenRC/openrc); Linux-only)
|
- [tempest](https://github.com/team-cloudchaser/tempest) (supports [`systemd`](https://systemd.io) and [OpenRC](https://github.com/OpenRC/openrc); Linux-only)
|
||||||
- Docker
|
- Docker
|
||||||
- Official: [ghcr.io/xtls/xray-core](https://ghcr.io/xtls/xray-core)
|
- [ghcr.io/xtls/xray-core](https://ghcr.io/xtls/xray-core) (**Official**)
|
||||||
- [iamybj/docker-xray](https://hub.docker.com/r/iamybj/docker-xray)
|
|
||||||
- [teddysun/xray](https://hub.docker.com/r/teddysun/xray)
|
- [teddysun/xray](https://hub.docker.com/r/teddysun/xray)
|
||||||
- Web Panel
|
- Web Panel
|
||||||
- [X-UI-English](https://github.com/NidukaAkalanka/x-ui-english), [3X-UI](https://github.com/MHSanaei/3x-ui), [X-UI](https://github.com/alireza0/x-ui), [X-UI](https://github.com/diditra/x-ui)
|
- [X-UI-English](https://github.com/NidukaAkalanka/x-ui-english), [3X-UI](https://github.com/MHSanaei/3x-ui), [X-UI](https://github.com/alireza0/x-ui), [X-UI](https://github.com/diditra/x-ui)
|
||||||
|
@@ -21,12 +21,14 @@ type Commander struct {
|
|||||||
services []Service
|
services []Service
|
||||||
ohm outbound.Manager
|
ohm outbound.Manager
|
||||||
tag string
|
tag string
|
||||||
|
listen string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCommander creates a new Commander based on the given config.
|
// NewCommander creates a new Commander based on the given config.
|
||||||
func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
|
func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
|
||||||
c := &Commander{
|
c := &Commander{
|
||||||
tag: config.Tag,
|
tag: config.Tag,
|
||||||
|
listen: config.Listen,
|
||||||
}
|
}
|
||||||
|
|
||||||
common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) {
|
common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) {
|
||||||
@@ -66,16 +68,29 @@ func (c *Commander) Start() error {
|
|||||||
}
|
}
|
||||||
c.Unlock()
|
c.Unlock()
|
||||||
|
|
||||||
|
var listen = func(listener net.Listener) {
|
||||||
|
if err := c.server.Serve(listener); err != nil {
|
||||||
|
newError("failed to start grpc server").Base(err).AtError().WriteToLog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.listen) > 0 {
|
||||||
|
if l, err := net.Listen("tcp", c.listen); err != nil {
|
||||||
|
newError("API server failed to listen on ", c.listen).Base(err).AtError().WriteToLog()
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
newError("API server listening on ", l.Addr()).AtInfo().WriteToLog()
|
||||||
|
go listen(l)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
listener := &OutboundListener{
|
listener := &OutboundListener{
|
||||||
buffer: make(chan net.Conn, 4),
|
buffer: make(chan net.Conn, 4),
|
||||||
done: done.New(),
|
done: done.New(),
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go listen(listener)
|
||||||
if err := c.server.Serve(listener); err != nil {
|
|
||||||
newError("failed to start grpc server").Base(err).AtError().WriteToLog()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := c.ohm.RemoveHandler(context.Background(), c.tag); err != nil {
|
if err := c.ohm.RemoveHandler(context.Background(), c.tag); err != nil {
|
||||||
newError("failed to remove existing handler").WriteToLog()
|
newError("failed to remove existing handler").WriteToLog()
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.33.0
|
// protoc-gen-go v1.28.1
|
||||||
// protoc v4.23.1
|
// protoc v4.25.3
|
||||||
// source: app/commander/config.proto
|
// source: app/commander/config.proto
|
||||||
|
|
||||||
package commander
|
package commander
|
||||||
@@ -29,6 +29,8 @@ type Config struct {
|
|||||||
|
|
||||||
// Tag of the outbound handler that handles grpc connections.
|
// Tag of the outbound handler that handles grpc connections.
|
||||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||||
|
// Network address of commander grpc service.
|
||||||
|
Listen string `protobuf:"bytes,3,opt,name=listen,proto3" json:"listen,omitempty"`
|
||||||
// Services that supported by this server. All services must implement Service
|
// Services that supported by this server. All services must implement Service
|
||||||
// interface.
|
// interface.
|
||||||
Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"`
|
Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"`
|
||||||
@@ -73,6 +75,13 @@ func (x *Config) GetTag() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetListen() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Listen
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (x *Config) GetService() []*serial.TypedMessage {
|
func (x *Config) GetService() []*serial.TypedMessage {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Service
|
return x.Service
|
||||||
@@ -127,20 +136,21 @@ var file_app_commander_config_proto_rawDesc = []byte{
|
|||||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72,
|
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72,
|
||||||
0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f,
|
0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f,
|
||||||
0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72,
|
0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72,
|
||||||
0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a,
|
0x6f, 0x74, 0x6f, 0x22, 0x6e, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a,
|
||||||
0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12,
|
0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12,
|
||||||
0x3a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
|
0x16, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73,
|
0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x3a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69,
|
||||||
0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
0x67, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52,
|
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79,
|
||||||
0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42,
|
0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76,
|
||||||
0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
0x69, 0x63, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
||||||
0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74,
|
0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
|
||||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
|
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65,
|
||||||
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
|
0x72, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
||||||
0x6e, 0x64, 0x65, 0x72, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e,
|
0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61,
|
||||||
0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0xaa, 0x02, 0x12, 0x58,
|
||||||
0x33,
|
0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65,
|
||||||
|
0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -12,6 +12,10 @@ import "common/serial/typed_message.proto";
|
|||||||
message Config {
|
message Config {
|
||||||
// Tag of the outbound handler that handles grpc connections.
|
// Tag of the outbound handler that handles grpc connections.
|
||||||
string tag = 1;
|
string tag = 1;
|
||||||
|
|
||||||
|
// Network address of commander grpc service.
|
||||||
|
string listen = 3;
|
||||||
|
|
||||||
// Services that supported by this server. All services must implement Service
|
// Services that supported by this server. All services must implement Service
|
||||||
// interface.
|
// interface.
|
||||||
repeated xray.common.serial.TypedMessage service = 2;
|
repeated xray.common.serial.TypedMessage service = 2;
|
||||||
|
@@ -218,11 +218,12 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
|||||||
if !destination.IsValid() {
|
if !destination.IsValid() {
|
||||||
panic("Dispatcher: Invalid destination.")
|
panic("Dispatcher: Invalid destination.")
|
||||||
}
|
}
|
||||||
ob := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if ob == nil {
|
if len(outbounds) == 0 {
|
||||||
ob = &session.Outbound{}
|
outbounds = []*session.Outbound{{}}
|
||||||
ctx = session.ContextWithOutbound(ctx, ob)
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
}
|
}
|
||||||
|
ob := outbounds[len(outbounds) - 1]
|
||||||
ob.OriginalTarget = destination
|
ob.OriginalTarget = destination
|
||||||
ob.Target = destination
|
ob.Target = destination
|
||||||
content := session.ContentFromContext(ctx)
|
content := session.ContentFromContext(ctx)
|
||||||
@@ -274,11 +275,12 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
|||||||
if !destination.IsValid() {
|
if !destination.IsValid() {
|
||||||
return newError("Dispatcher: Invalid destination.")
|
return newError("Dispatcher: Invalid destination.")
|
||||||
}
|
}
|
||||||
ob := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if ob == nil {
|
if len(outbounds) == 0 {
|
||||||
ob = &session.Outbound{}
|
outbounds = []*session.Outbound{{}}
|
||||||
ctx = session.ContextWithOutbound(ctx, ob)
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
}
|
}
|
||||||
|
ob := outbounds[len(outbounds) - 1]
|
||||||
ob.OriginalTarget = destination
|
ob.OriginalTarget = destination
|
||||||
ob.Target = destination
|
ob.Target = destination
|
||||||
content := session.ContentFromContext(ctx)
|
content := session.ContentFromContext(ctx)
|
||||||
@@ -368,7 +370,8 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
|
|||||||
return contentResult, contentErr
|
return contentResult, contentErr
|
||||||
}
|
}
|
||||||
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
||||||
ob := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ob := outbounds[len(outbounds) - 1]
|
||||||
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
|
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
|
||||||
proxied := hosts.LookupHosts(ob.Target.String())
|
proxied := hosts.LookupHosts(ob.Target.String())
|
||||||
if proxied != nil {
|
if proxied != nil {
|
||||||
@@ -425,6 +428,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ob.Tag = handler.Tag()
|
||||||
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
|
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
|
||||||
if tag := handler.Tag(); tag != "" {
|
if tag := handler.Tag(); tag != "" {
|
||||||
if inTag == "" {
|
if inTag == "" {
|
||||||
|
@@ -26,11 +26,12 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error)
|
|||||||
return protocolSnifferWithMetadata{}, errNotInit
|
return protocolSnifferWithMetadata{}, errNotInit
|
||||||
}
|
}
|
||||||
return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
|
return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
|
||||||
Target := session.OutboundFromContext(ctx).Target
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if Target.Network == net.Network_TCP || Target.Network == net.Network_UDP {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address)
|
if ob.Target.Network == net.Network_TCP || ob.Target.Network == net.Network_UDP {
|
||||||
|
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(ob.Target.Address)
|
||||||
if domainFromFakeDNS != "" {
|
if domainFromFakeDNS != "" {
|
||||||
newError("fake dns got domain: ", domainFromFakeDNS, " for ip: ", Target.Address.String()).WriteToLog(session.ExportIDToError(ctx))
|
newError("fake dns got domain: ", domainFromFakeDNS, " for ip: ", ob.Target.Address.String()).WriteToLog(session.ExportIDToError(ctx))
|
||||||
return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
|
return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,7 +39,7 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error)
|
|||||||
if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil {
|
if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil {
|
||||||
ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt)
|
ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt)
|
||||||
if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {
|
if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {
|
||||||
inPool := fkr0.IsIPInIPPool(Target.Address)
|
inPool := fkr0.IsIPInIPPool(ob.Target.Address)
|
||||||
ipAddressInRangeValue.addressInRange = &inPool
|
ipAddressInRangeValue.addressInRange = &inPool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -60,7 +60,7 @@ func (w *tcpWorker) callback(conn stat.Connection) {
|
|||||||
sid := session.NewID()
|
sid := session.NewID()
|
||||||
ctx = session.ContextWithID(ctx, sid)
|
ctx = session.ContextWithID(ctx, sid)
|
||||||
|
|
||||||
var outbound = &session.Outbound{}
|
outbounds := []*session.Outbound{{}}
|
||||||
if w.recvOrigDest {
|
if w.recvOrigDest {
|
||||||
var dest net.Destination
|
var dest net.Destination
|
||||||
switch getTProxyType(w.stream) {
|
switch getTProxyType(w.stream) {
|
||||||
@@ -75,10 +75,10 @@ func (w *tcpWorker) callback(conn stat.Connection) {
|
|||||||
dest = net.DestinationFromAddr(conn.LocalAddr())
|
dest = net.DestinationFromAddr(conn.LocalAddr())
|
||||||
}
|
}
|
||||||
if dest.IsValid() {
|
if dest.IsValid() {
|
||||||
outbound.Target = dest
|
outbounds[0].Target = dest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx = session.ContextWithOutbound(ctx, outbound)
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
|
|
||||||
if w.uplinkCounter != nil || w.downlinkCounter != nil {
|
if w.uplinkCounter != nil || w.downlinkCounter != nil {
|
||||||
conn = &stat.CounterConnection{
|
conn = &stat.CounterConnection{
|
||||||
@@ -309,9 +309,10 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
|
|||||||
ctx = session.ContextWithID(ctx, sid)
|
ctx = session.ContextWithID(ctx, sid)
|
||||||
|
|
||||||
if originalDest.IsValid() {
|
if originalDest.IsValid() {
|
||||||
ctx = session.ContextWithOutbound(ctx, &session.Outbound{
|
outbounds := []*session.Outbound{{
|
||||||
Target: originalDest,
|
Target: originalDest,
|
||||||
})
|
}}
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
}
|
}
|
||||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||||
Source: source,
|
Source: source,
|
||||||
|
@@ -169,10 +169,11 @@ func (h *Handler) Tag() string {
|
|||||||
|
|
||||||
// Dispatch implements proxy.Outbound.Dispatch.
|
// Dispatch implements proxy.Outbound.Dispatch.
|
||||||
func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
|
func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound.Target.Network == net.Network_UDP && outbound.OriginalTarget.Address != nil && outbound.OriginalTarget.Address != outbound.Target.Address {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: outbound.Target.Address, OriginalDest: outbound.OriginalTarget.Address}
|
if ob.Target.Network == net.Network_UDP && ob.OriginalTarget.Address != nil && ob.OriginalTarget.Address != ob.Target.Address {
|
||||||
link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: outbound.Target.Address, OriginalDest: outbound.OriginalTarget.Address}
|
link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
|
||||||
|
link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
|
||||||
}
|
}
|
||||||
if h.mux != nil {
|
if h.mux != nil {
|
||||||
test := func(err error) {
|
test := func(err error) {
|
||||||
@@ -183,7 +184,7 @@ func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
|
|||||||
common.Interrupt(link.Writer)
|
common.Interrupt(link.Writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if outbound.Target.Network == net.Network_UDP && outbound.Target.Port == 443 {
|
if ob.Target.Network == net.Network_UDP && ob.Target.Port == 443 {
|
||||||
switch h.udp443 {
|
switch h.udp443 {
|
||||||
case "reject":
|
case "reject":
|
||||||
test(newError("XUDP rejected UDP/443 traffic").AtInfo())
|
test(newError("XUDP rejected UDP/443 traffic").AtInfo())
|
||||||
@@ -192,7 +193,7 @@ func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
|
|||||||
goto out
|
goto out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if h.xudp != nil && outbound.Target.Network == net.Network_UDP {
|
if h.xudp != nil && ob.Target.Network == net.Network_UDP {
|
||||||
if !h.xudp.Enabled {
|
if !h.xudp.Enabled {
|
||||||
goto out
|
goto out
|
||||||
}
|
}
|
||||||
@@ -243,10 +244,11 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
|||||||
handler := h.outboundManager.GetHandler(tag)
|
handler := h.outboundManager.GetHandler(tag)
|
||||||
if handler != nil {
|
if handler != nil {
|
||||||
newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
||||||
ctx = session.ContextWithOutbound(ctx, &session.Outbound{
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, append(outbounds, &session.Outbound{
|
||||||
Target: dest,
|
Target: dest,
|
||||||
})
|
Tag: tag,
|
||||||
|
})) // add another outbound in session ctx
|
||||||
opts := pipe.OptionsFromContext(ctx)
|
opts := pipe.OptionsFromContext(ctx)
|
||||||
uplinkReader, uplinkWriter := pipe.New(opts...)
|
uplinkReader, uplinkWriter := pipe.New(opts...)
|
||||||
downlinkReader, downlinkWriter := pipe.New(opts...)
|
downlinkReader, downlinkWriter := pipe.New(opts...)
|
||||||
@@ -266,15 +268,12 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
|||||||
}
|
}
|
||||||
|
|
||||||
if h.senderSettings.Via != nil {
|
if h.senderSettings.Via != nil {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
outbound = new(session.Outbound)
|
|
||||||
ctx = session.ContextWithOutbound(ctx, outbound)
|
|
||||||
}
|
|
||||||
if h.senderSettings.ViaCidr == "" {
|
if h.senderSettings.ViaCidr == "" {
|
||||||
outbound.Gateway = h.senderSettings.Via.AsAddress()
|
ob.Gateway = h.senderSettings.Via.AsAddress()
|
||||||
} else { //Get a random address.
|
} else { //Get a random address.
|
||||||
outbound.Gateway = ParseRandomIPv6(h.senderSettings.Via.AsAddress(), h.senderSettings.ViaCidr)
|
ob.Gateway = ParseRandomIPv6(h.senderSettings.Via.AsAddress(), h.senderSettings.ViaCidr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,10 +284,9 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
|||||||
|
|
||||||
conn, err := internet.Dial(ctx, dest, h.streamSettings)
|
conn, err := internet.Dial(ctx, dest, h.streamSettings)
|
||||||
conn = h.getStatCouterConnection(conn)
|
conn = h.getStatCouterConnection(conn)
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound != nil {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
outbound.Conn = conn
|
ob.Conn = conn
|
||||||
}
|
|
||||||
return conn, err
|
return conn, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/app/stats"
|
"github.com/xtls/xray-core/app/stats"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
|
"github.com/xtls/xray-core/common/session"
|
||||||
core "github.com/xtls/xray-core/core"
|
core "github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
"github.com/xtls/xray-core/proxy/freedom"
|
"github.com/xtls/xray-core/proxy/freedom"
|
||||||
@@ -44,6 +45,7 @@ func TestOutboundWithoutStatCounter(t *testing.T) {
|
|||||||
v, _ := core.New(config)
|
v, _ := core.New(config)
|
||||||
v.AddFeature((outbound.Manager)(new(Manager)))
|
v.AddFeature((outbound.Manager)(new(Manager)))
|
||||||
ctx := context.WithValue(context.Background(), xrayKey, v)
|
ctx := context.WithValue(context.Background(), xrayKey, v)
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}})
|
||||||
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
||||||
Tag: "tag",
|
Tag: "tag",
|
||||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
@@ -73,6 +75,7 @@ func TestOutboundWithStatCounter(t *testing.T) {
|
|||||||
v, _ := core.New(config)
|
v, _ := core.New(config)
|
||||||
v.AddFeature((outbound.Manager)(new(Manager)))
|
v.AddFeature((outbound.Manager)(new(Manager)))
|
||||||
ctx := context.WithValue(context.Background(), xrayKey, v)
|
ctx := context.WithValue(context.Background(), xrayKey, v)
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}})
|
||||||
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
||||||
Tag: "tag",
|
Tag: "tag",
|
||||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
|
@@ -62,12 +62,13 @@ func (p *Portal) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Portal) HandleConnection(ctx context.Context, link *transport.Link) error {
|
func (p *Portal) HandleConnection(ctx context.Context, link *transport.Link) error {
|
||||||
outboundMeta := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outboundMeta == nil {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if ob == nil {
|
||||||
return newError("outbound metadata not found").AtError()
|
return newError("outbound metadata not found").AtError()
|
||||||
}
|
}
|
||||||
|
|
||||||
if isDomain(outboundMeta.Target, p.domain) {
|
if isDomain(ob.Target, p.domain) {
|
||||||
muxClient, err := mux.NewClientWorker(*link, mux.ClientStrategy{})
|
muxClient, err := mux.NewClientWorker(*link, mux.ClientStrategy{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newError("failed to create mux client worker").Base(err).AtWarning()
|
return newError("failed to create mux client worker").Base(err).AtWarning()
|
||||||
@@ -206,9 +207,10 @@ func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
|
|||||||
downlinkReader, downlinkWriter := pipe.New(opt...)
|
downlinkReader, downlinkWriter := pipe.New(opt...)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = session.ContextWithOutbound(ctx, &session.Outbound{
|
outbounds := []*session.Outbound{{
|
||||||
Target: net.UDPDestination(net.DomainAddress(internalDomain), 0),
|
Target: net.UDPDestination(net.DomainAddress(internalDomain), 0),
|
||||||
})
|
}}
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
f := client.Dispatch(ctx, &transport.Link{
|
f := client.Dispatch(ctx, &transport.Link{
|
||||||
Reader: uplinkReader,
|
Reader: uplinkReader,
|
||||||
Writer: downlinkWriter,
|
Writer: downlinkWriter,
|
||||||
|
@@ -4,6 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
sync "sync"
|
sync "sync"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/app/observatory"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/features/extension"
|
"github.com/xtls/xray-core/features/extension"
|
||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
)
|
)
|
||||||
@@ -17,14 +20,58 @@ type BalancingPrincipleTarget interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RoundRobinStrategy struct {
|
type RoundRobinStrategy struct {
|
||||||
|
FallbackTag string
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
observatory extension.Observatory
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
index int
|
index int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *RoundRobinStrategy) InjectContext(ctx context.Context) {
|
||||||
|
s.ctx = ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RoundRobinStrategy) GetPrincipleTarget(strings []string) []string {
|
||||||
|
return strings
|
||||||
|
}
|
||||||
|
|
||||||
func (s *RoundRobinStrategy) PickOutbound(tags []string) string {
|
func (s *RoundRobinStrategy) PickOutbound(tags []string) string {
|
||||||
|
if len(s.FallbackTag) > 0 && s.observatory == nil {
|
||||||
|
common.Must(core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error {
|
||||||
|
s.observatory = observatory
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
if s.observatory != nil {
|
||||||
|
observeReport, err := s.observatory.GetObservation(s.ctx)
|
||||||
|
if err == nil {
|
||||||
|
aliveTags := make([]string, 0)
|
||||||
|
if result, ok := observeReport.(*observatory.ObservationResult); ok {
|
||||||
|
status := result.Status
|
||||||
|
statusMap := make(map[string]*observatory.OutboundStatus)
|
||||||
|
for _, outboundStatus := range status {
|
||||||
|
statusMap[outboundStatus.OutboundTag] = outboundStatus
|
||||||
|
}
|
||||||
|
for _, candidate := range tags {
|
||||||
|
if outboundStatus, found := statusMap[candidate]; found {
|
||||||
|
if outboundStatus.Alive {
|
||||||
|
aliveTags = append(aliveTags, candidate)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// unfound candidate is considered alive
|
||||||
|
aliveTags = append(aliveTags, candidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tags = aliveTags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
n := len(tags)
|
n := len(tags)
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
panic("0 tags")
|
// goes to fallbackTag
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
|
@@ -135,7 +135,7 @@ func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatch
|
|||||||
case "roundrobin":
|
case "roundrobin":
|
||||||
return &Balancer{
|
return &Balancer{
|
||||||
selectors: br.OutboundSelector,
|
selectors: br.OutboundSelector,
|
||||||
strategy: &RoundRobinStrategy{},
|
strategy: &RoundRobinStrategy{FallbackTag: br.FallbackTag},
|
||||||
fallbackTag: br.FallbackTag,
|
fallbackTag: br.FallbackTag,
|
||||||
ohm: ohm,
|
ohm: ohm,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -162,7 +162,7 @@ func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatch
|
|||||||
selectors: br.OutboundSelector,
|
selectors: br.OutboundSelector,
|
||||||
ohm: ohm,
|
ohm: ohm,
|
||||||
fallbackTag: br.FallbackTag,
|
fallbackTag: br.FallbackTag,
|
||||||
strategy: &RandomStrategy{},
|
strategy: &RandomStrategy{FallbackTag: br.FallbackTag},
|
||||||
}, nil
|
}, nil
|
||||||
default:
|
default:
|
||||||
return nil, newError("unrecognized balancer type")
|
return nil, newError("unrecognized balancer type")
|
||||||
|
@@ -45,7 +45,9 @@ func TestSimpleRouter(t *testing.T) {
|
|||||||
HandlerSelector: mockHs,
|
HandlerSelector: mockHs,
|
||||||
}, nil))
|
}, nil))
|
||||||
|
|
||||||
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)})
|
ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
|
Target: net.TCPDestination(net.DomainAddress("example.com"), 80),
|
||||||
|
}})
|
||||||
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if tag := route.GetOutboundTag(); tag != "test" {
|
if tag := route.GetOutboundTag(); tag != "test" {
|
||||||
@@ -86,7 +88,9 @@ func TestSimpleBalancer(t *testing.T) {
|
|||||||
HandlerSelector: mockHs,
|
HandlerSelector: mockHs,
|
||||||
}, nil))
|
}, nil))
|
||||||
|
|
||||||
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)})
|
ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
|
Target: net.TCPDestination(net.DomainAddress("example.com"), 80),
|
||||||
|
}})
|
||||||
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if tag := route.GetOutboundTag(); tag != "test" {
|
if tag := route.GetOutboundTag(); tag != "test" {
|
||||||
@@ -174,7 +178,9 @@ func TestIPOnDemand(t *testing.T) {
|
|||||||
r := new(Router)
|
r := new(Router)
|
||||||
common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil))
|
common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil))
|
||||||
|
|
||||||
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)})
|
ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
|
Target: net.TCPDestination(net.DomainAddress("example.com"), 80),
|
||||||
|
}})
|
||||||
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if tag := route.GetOutboundTag(); tag != "test" {
|
if tag := route.GetOutboundTag(); tag != "test" {
|
||||||
@@ -213,7 +219,9 @@ func TestIPIfNonMatchDomain(t *testing.T) {
|
|||||||
r := new(Router)
|
r := new(Router)
|
||||||
common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil))
|
common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil))
|
||||||
|
|
||||||
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)})
|
ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
|
Target: net.TCPDestination(net.DomainAddress("example.com"), 80),
|
||||||
|
}})
|
||||||
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if tag := route.GetOutboundTag(); tag != "test" {
|
if tag := route.GetOutboundTag(); tag != "test" {
|
||||||
@@ -247,7 +255,9 @@ func TestIPIfNonMatchIP(t *testing.T) {
|
|||||||
r := new(Router)
|
r := new(Router)
|
||||||
common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil))
|
common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil))
|
||||||
|
|
||||||
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 80)})
|
ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
|
Target: net.TCPDestination(net.LocalHostIP, 80),
|
||||||
|
}})
|
||||||
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if tag := route.GetOutboundTag(); tag != "test" {
|
if tag := route.GetOutboundTag(); tag != "test" {
|
||||||
|
@@ -1,17 +1,63 @@
|
|||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/app/observatory"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/dice"
|
"github.com/xtls/xray-core/common/dice"
|
||||||
|
"github.com/xtls/xray-core/core"
|
||||||
|
"github.com/xtls/xray-core/features/extension"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RandomStrategy represents a random balancing strategy
|
// RandomStrategy represents a random balancing strategy
|
||||||
type RandomStrategy struct{}
|
type RandomStrategy struct{
|
||||||
|
FallbackTag string
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
observatory extension.Observatory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RandomStrategy) InjectContext(ctx context.Context) {
|
||||||
|
s.ctx = ctx
|
||||||
|
}
|
||||||
|
|
||||||
func (s *RandomStrategy) GetPrincipleTarget(strings []string) []string {
|
func (s *RandomStrategy) GetPrincipleTarget(strings []string) []string {
|
||||||
return strings
|
return strings
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RandomStrategy) PickOutbound(candidates []string) string {
|
func (s *RandomStrategy) PickOutbound(candidates []string) string {
|
||||||
|
if len(s.FallbackTag) > 0 && s.observatory == nil {
|
||||||
|
common.Must(core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error {
|
||||||
|
s.observatory = observatory
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
if s.observatory != nil {
|
||||||
|
observeReport, err := s.observatory.GetObservation(s.ctx)
|
||||||
|
if err == nil {
|
||||||
|
aliveTags := make([]string, 0)
|
||||||
|
if result, ok := observeReport.(*observatory.ObservationResult); ok {
|
||||||
|
status := result.Status
|
||||||
|
statusMap := make(map[string]*observatory.OutboundStatus)
|
||||||
|
for _, outboundStatus := range status {
|
||||||
|
statusMap[outboundStatus.OutboundTag] = outboundStatus
|
||||||
|
}
|
||||||
|
for _, candidate := range candidates {
|
||||||
|
if outboundStatus, found := statusMap[candidate]; found {
|
||||||
|
if outboundStatus.Alive {
|
||||||
|
aliveTags = append(aliveTags, candidate)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// unfound candidate is considered alive
|
||||||
|
aliveTags = append(aliveTags, candidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
candidates = aliveTags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
count := len(candidates)
|
count := len(candidates)
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
// goes to fallbackTag
|
// goes to fallbackTag
|
||||||
|
@@ -148,9 +148,10 @@ func (f *DialingWorkerFactory) Create() (*ClientWorker, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func(p proxy.Outbound, d internet.Dialer, c common.Closable) {
|
go func(p proxy.Outbound, d internet.Dialer, c common.Closable) {
|
||||||
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{
|
outbounds := []*session.Outbound{{
|
||||||
Target: net.TCPDestination(muxCoolAddress, muxCoolPort),
|
Target: net.TCPDestination(muxCoolAddress, muxCoolPort),
|
||||||
})
|
}}
|
||||||
|
ctx := session.ContextWithOutbounds(context.Background(), outbounds)
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
if err := p.Process(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}, d); err != nil {
|
if err := p.Process(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}, d); err != nil {
|
||||||
@@ -242,17 +243,18 @@ func writeFirstPayload(reader buf.Reader, writer *Writer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
|
func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
|
||||||
dest := session.OutboundFromContext(ctx).Target
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ob := outbounds[len(outbounds) - 1]
|
||||||
transferType := protocol.TransferTypeStream
|
transferType := protocol.TransferTypeStream
|
||||||
if dest.Network == net.Network_UDP {
|
if ob.Target.Network == net.Network_UDP {
|
||||||
transferType = protocol.TransferTypePacket
|
transferType = protocol.TransferTypePacket
|
||||||
}
|
}
|
||||||
s.transferType = transferType
|
s.transferType = transferType
|
||||||
writer := NewWriter(s.ID, dest, output, transferType, xudp.GetGlobalID(ctx))
|
writer := NewWriter(s.ID, ob.Target, output, transferType, xudp.GetGlobalID(ctx))
|
||||||
defer s.Close(false)
|
defer s.Close(false)
|
||||||
defer writer.Close()
|
defer writer.Close()
|
||||||
|
|
||||||
newError("dispatching request to ", dest).WriteToLog(session.ExportIDToError(ctx))
|
newError("dispatching request to ", ob.Target).WriteToLog(session.ExportIDToError(ctx))
|
||||||
if err := writeFirstPayload(s.input, writer); err != nil {
|
if err := writeFirstPayload(s.input, writer); err != nil {
|
||||||
newError("failed to write first payload").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
newError("failed to write first payload").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||||
writer.hasError = true
|
writer.hasError = true
|
||||||
|
@@ -86,9 +86,9 @@ func TestClientWorkerClose(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tr1, tw1 := pipe.New(pipe.WithoutSizeLimit())
|
tr1, tw1 := pipe.New(pipe.WithoutSizeLimit())
|
||||||
ctx1 := session.ContextWithOutbound(context.Background(), &session.Outbound{
|
ctx1 := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
Target: net.TCPDestination(net.DomainAddress("www.example.com"), 80),
|
Target: net.TCPDestination(net.DomainAddress("www.example.com"), 80),
|
||||||
})
|
}})
|
||||||
common.Must(manager.Dispatch(ctx1, &transport.Link{
|
common.Must(manager.Dispatch(ctx1, &transport.Link{
|
||||||
Reader: tr1,
|
Reader: tr1,
|
||||||
Writer: tw1,
|
Writer: tw1,
|
||||||
@@ -103,9 +103,9 @@ func TestClientWorkerClose(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tr2, tw2 := pipe.New(pipe.WithoutSizeLimit())
|
tr2, tw2 := pipe.New(pipe.WithoutSizeLimit())
|
||||||
ctx2 := session.ContextWithOutbound(context.Background(), &session.Outbound{
|
ctx2 := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
Target: net.TCPDestination(net.DomainAddress("www.example.com"), 80),
|
Target: net.TCPDestination(net.DomainAddress("www.example.com"), 80),
|
||||||
})
|
}})
|
||||||
common.Must(manager.Dispatch(ctx2, &transport.Link{
|
common.Must(manager.Dispatch(ctx2, &transport.Link{
|
||||||
Reader: tr2,
|
Reader: tr2,
|
||||||
Writer: tw2,
|
Writer: tw2,
|
||||||
|
@@ -51,13 +51,13 @@ func InboundFromContext(ctx context.Context) *Inbound {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ContextWithOutbound(ctx context.Context, outbound *Outbound) context.Context {
|
func ContextWithOutbounds(ctx context.Context, outbounds []*Outbound) context.Context {
|
||||||
return context.WithValue(ctx, outboundSessionKey, outbound)
|
return context.WithValue(ctx, outboundSessionKey, outbounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OutboundFromContext(ctx context.Context) *Outbound {
|
func OutboundsFromContext(ctx context.Context) []*Outbound {
|
||||||
if outbound, ok := ctx.Value(outboundSessionKey).(*Outbound); ok {
|
if outbounds, ok := ctx.Value(outboundSessionKey).([]*Outbound); ok {
|
||||||
return outbound
|
return outbounds
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -50,18 +50,11 @@ type Inbound struct {
|
|||||||
Conn net.Conn
|
Conn net.Conn
|
||||||
// Timer of the inbound buf copier. May be nil.
|
// Timer of the inbound buf copier. May be nil.
|
||||||
Timer *signal.ActivityTimer
|
Timer *signal.ActivityTimer
|
||||||
// CanSpliceCopy is a property for this connection, set by both inbound and outbound
|
// CanSpliceCopy is a property for this connection
|
||||||
// 1 = can, 2 = after processing protocol info should be able to, 3 = cannot
|
// 1 = can, 2 = after processing protocol info should be able to, 3 = cannot
|
||||||
CanSpliceCopy int
|
CanSpliceCopy int
|
||||||
}
|
}
|
||||||
|
|
||||||
func(i *Inbound) SetCanSpliceCopy(canSpliceCopy int) int {
|
|
||||||
if canSpliceCopy > i.CanSpliceCopy {
|
|
||||||
i.CanSpliceCopy = canSpliceCopy
|
|
||||||
}
|
|
||||||
return i.CanSpliceCopy
|
|
||||||
}
|
|
||||||
|
|
||||||
// Outbound is the metadata of an outbound connection.
|
// Outbound is the metadata of an outbound connection.
|
||||||
type Outbound struct {
|
type Outbound struct {
|
||||||
// Target address of the outbound connection.
|
// Target address of the outbound connection.
|
||||||
@@ -70,10 +63,15 @@ type Outbound struct {
|
|||||||
RouteTarget net.Destination
|
RouteTarget net.Destination
|
||||||
// Gateway address
|
// Gateway address
|
||||||
Gateway net.Address
|
Gateway net.Address
|
||||||
|
// Tag of the outbound proxy that handles the connection.
|
||||||
|
Tag string
|
||||||
// Name of the outbound proxy that handles the connection.
|
// Name of the outbound proxy that handles the connection.
|
||||||
Name string
|
Name string
|
||||||
// Conn is actually internet.Connection. May be nil. It is currently nil for outbound with proxySettings
|
// Conn is actually internet.Connection. May be nil. It is currently nil for outbound with proxySettings
|
||||||
Conn net.Conn
|
Conn net.Conn
|
||||||
|
// CanSpliceCopy is a property for this connection
|
||||||
|
// 1 = can, 2 = after processing protocol info should be able to, 3 = cannot
|
||||||
|
CanSpliceCopy int
|
||||||
}
|
}
|
||||||
|
|
||||||
// SniffingRequest controls the behavior of content sniffing.
|
// SniffingRequest controls the behavior of content sniffing.
|
||||||
|
@@ -43,9 +43,14 @@ func NewOutboundDialer(outbound proxy.Outbound, dialer internet.Dialer) *XrayOut
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *XrayOutboundDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (d *XrayOutboundDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
ctx = session.ContextWithOutbound(ctx, &session.Outbound{
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
Target: ToDestination(destination, ToNetwork(network)),
|
if len(outbounds) == 0 {
|
||||||
})
|
outbounds = []*session.Outbound{{}}
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
|
}
|
||||||
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
ob.Target = ToDestination(destination, ToNetwork(network))
|
||||||
|
|
||||||
opts := []pipe.Option{pipe.WithSizeLimit(64 * 1024)}
|
opts := []pipe.Option{pipe.WithSizeLimit(64 * 1024)}
|
||||||
uplinkReader, uplinkWriter := pipe.New(opts...)
|
uplinkReader, uplinkWriter := pipe.New(opts...)
|
||||||
downlinkReader, downlinkWriter := pipe.New(opts...)
|
downlinkReader, downlinkWriter := pipe.New(opts...)
|
||||||
|
@@ -21,7 +21,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
Version_x byte = 1
|
Version_x byte = 1
|
||||||
Version_y byte = 8
|
Version_y byte = 8
|
||||||
Version_z byte = 11
|
Version_z byte = 12
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -124,9 +124,11 @@ func (ctx *Context) GetSkipDNSResolve() bool {
|
|||||||
|
|
||||||
// AsRoutingContext creates a context from context.context with session info.
|
// AsRoutingContext creates a context from context.context with session info.
|
||||||
func AsRoutingContext(ctx context.Context) routing.Context {
|
func AsRoutingContext(ctx context.Context) routing.Context {
|
||||||
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ob := outbounds[len(outbounds) - 1]
|
||||||
return &Context{
|
return &Context{
|
||||||
Inbound: session.InboundFromContext(ctx),
|
Inbound: session.InboundFromContext(ctx),
|
||||||
Outbound: session.OutboundFromContext(ctx),
|
Outbound: ob,
|
||||||
Content: session.ContentFromContext(ctx),
|
Content: session.ContentFromContext(ctx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
go.mod
28
go.mod
@@ -12,26 +12,26 @@ require (
|
|||||||
github.com/miekg/dns v1.1.59
|
github.com/miekg/dns v1.1.59
|
||||||
github.com/pelletier/go-toml v1.9.5
|
github.com/pelletier/go-toml v1.9.5
|
||||||
github.com/pires/go-proxyproto v0.7.0
|
github.com/pires/go-proxyproto v0.7.0
|
||||||
github.com/quic-go/quic-go v0.42.0
|
github.com/quic-go/quic-go v0.44.0
|
||||||
github.com/refraction-networking/utls v1.6.4
|
github.com/refraction-networking/utls v1.6.6
|
||||||
github.com/sagernet/sing v0.3.8
|
github.com/sagernet/sing v0.3.8
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.6
|
github.com/sagernet/sing-shadowsocks v0.2.6
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb
|
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
||||||
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3
|
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3
|
||||||
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19
|
github.com/xtls/reality v0.0.0-20240429224917-ecc4401070cc
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.22.0
|
golang.org/x/crypto v0.23.0
|
||||||
golang.org/x/net v0.24.0
|
golang.org/x/net v0.25.0
|
||||||
golang.org/x/sync v0.7.0
|
golang.org/x/sync v0.7.0
|
||||||
golang.org/x/sys v0.19.0
|
golang.org/x/sys v0.20.0
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||||
google.golang.org/grpc v1.63.2
|
google.golang.org/grpc v1.64.0
|
||||||
google.golang.org/protobuf v1.33.0
|
google.golang.org/protobuf v1.34.1
|
||||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489
|
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489
|
||||||
h12.io/socks v1.0.3
|
h12.io/socks v1.0.3
|
||||||
lukechampine.com/blake3 v1.2.2
|
lukechampine.com/blake3 v1.3.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -49,13 +49,13 @@ require (
|
|||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
go.uber.org/mock v0.4.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||||
golang.org/x/mod v0.16.0 // indirect
|
golang.org/x/mod v0.17.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.15.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.19.0 // indirect
|
golang.org/x/tools v0.21.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
56
go.sum
56
go.sum
@@ -114,10 +114,10 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
|
|||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
|
github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0=
|
||||||
github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
|
github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek=
|
||||||
github.com/refraction-networking/utls v1.6.4 h1:aeynTroaYn7y+mFtqv8D0bQ4bw0y9nJHneGxJ7lvRDM=
|
github.com/refraction-networking/utls v1.6.6 h1:igFsYBUJPYM8Rno9xUuDoM5GQrVEqY4llzEXOkL43Ig=
|
||||||
github.com/refraction-networking/utls v1.6.4/go.mod h1:2VL2xfiqgFAZtJKeUTlf+PSYFs3Eu7km0gCtXJ3m8zs=
|
github.com/refraction-networking/utls v1.6.6/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
@@ -167,8 +167,8 @@ github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3/go.mo
|
|||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 h1:capMfFYRgH9BCLd6A3Er/cH3A9Nz3CU2KwxwOQZIePI=
|
github.com/xtls/reality v0.0.0-20240429224917-ecc4401070cc h1:0Nj8T1n7F7+v4vRVroaJIvY6R0vNABLfPH+lzPHRJvI=
|
||||||
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
|
github.com/xtls/reality v0.0.0-20240429224917-ecc4401070cc/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
|
||||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||||
@@ -181,17 +181,17 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -203,8 +203,8 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@@ -230,15 +230,15 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
@@ -250,8 +250,8 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
|
||||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -271,16 +271,16 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
|
|||||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 h1:em/y72n4XlYRtayY/cVj6pnVzHa//BDA1BdoO+z9mdE=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
||||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -301,7 +301,7 @@ h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck=
|
|||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
lukechampine.com/blake3 v1.2.2 h1:wEAbSg0IVU4ih44CVlpMqMZMpzr5hf/6aqodLlevd/w=
|
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||||
lukechampine.com/blake3 v1.2.2/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||||
|
@@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
type APIConfig struct {
|
type APIConfig struct {
|
||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
|
Listen string `json:"listen"`
|
||||||
Services []string `json:"services"`
|
Services []string `json:"services"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ func (c *APIConfig) Build() (*commander.Config, error) {
|
|||||||
|
|
||||||
return &commander.Config{
|
return &commander.Config{
|
||||||
Tag: c.Tag,
|
Tag: c.Tag,
|
||||||
|
Listen: c.Listen,
|
||||||
Service: services,
|
Service: services,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@@ -97,7 +97,8 @@ func TestRouterConfig(t *testing.T) {
|
|||||||
"balancers": [
|
"balancers": [
|
||||||
{
|
{
|
||||||
"tag": "b1",
|
"tag": "b1",
|
||||||
"selector": ["test"]
|
"selector": ["test"],
|
||||||
|
"fallbackTag": "fall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tag": "b2",
|
"tag": "b2",
|
||||||
@@ -137,6 +138,7 @@ func TestRouterConfig(t *testing.T) {
|
|||||||
Tag: "b1",
|
Tag: "b1",
|
||||||
OutboundSelector: []string{"test"},
|
OutboundSelector: []string{"test"},
|
||||||
Strategy: "random",
|
Strategy: "random",
|
||||||
|
FallbackTag: "fall",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Tag: "b2",
|
Tag: "b2",
|
||||||
|
@@ -31,10 +31,9 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
|
|||||||
|
|
||||||
// Process implements OutboundHandler.Dispatch().
|
// Process implements OutboundHandler.Dispatch().
|
||||||
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound != nil {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
outbound.Name = "blackhole"
|
ob.Name = "blackhole"
|
||||||
}
|
|
||||||
|
|
||||||
nBytes := h.response.WriteTo(link.Writer)
|
nBytes := h.response.WriteTo(link.Writer)
|
||||||
if nBytes > 0 {
|
if nBytes > 0 {
|
||||||
|
@@ -7,13 +7,15 @@ import (
|
|||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
|
"github.com/xtls/xray-core/common/session"
|
||||||
"github.com/xtls/xray-core/proxy/blackhole"
|
"github.com/xtls/xray-core/proxy/blackhole"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
"github.com/xtls/xray-core/transport/pipe"
|
"github.com/xtls/xray-core/transport/pipe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBlackholeHTTPResponse(t *testing.T) {
|
func TestBlackholeHTTPResponse(t *testing.T) {
|
||||||
handler, err := blackhole.New(context.Background(), &blackhole.Config{
|
ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{}})
|
||||||
|
handler, err := blackhole.New(ctx, &blackhole.Config{
|
||||||
Response: serial.ToTypedMessage(&blackhole.HTTPResponse{}),
|
Response: serial.ToTypedMessage(&blackhole.HTTPResponse{}),
|
||||||
})
|
})
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
@@ -32,7 +34,7 @@ func TestBlackholeHTTPResponse(t *testing.T) {
|
|||||||
Reader: reader,
|
Reader: reader,
|
||||||
Writer: writer,
|
Writer: writer,
|
||||||
}
|
}
|
||||||
common.Must(handler.Process(context.Background(), &link, nil))
|
common.Must(handler.Process(ctx, &link, nil))
|
||||||
common.Must(rerr)
|
common.Must(rerr)
|
||||||
if mb.IsEmpty() {
|
if mb.IsEmpty() {
|
||||||
t.Error("expect http response, but nothing")
|
t.Error("expect http response, but nothing")
|
||||||
|
@@ -96,15 +96,16 @@ func parseIPQuery(b []byte) (r bool, domain string, id uint16, qType dnsmessage.
|
|||||||
|
|
||||||
// Process implements proxy.Outbound.
|
// Process implements proxy.Outbound.
|
||||||
func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.Dialer) error {
|
func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.Dialer) error {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil || !outbound.Target.IsValid() {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if !ob.Target.IsValid() {
|
||||||
return newError("invalid outbound")
|
return newError("invalid outbound")
|
||||||
}
|
}
|
||||||
outbound.Name = "dns"
|
ob.Name = "dns"
|
||||||
|
|
||||||
srcNetwork := outbound.Target.Network
|
srcNetwork := ob.Target.Network
|
||||||
|
|
||||||
dest := outbound.Target
|
dest := ob.Target
|
||||||
if h.server.Network != net.Network_Unknown {
|
if h.server.Network != net.Network_Unknown {
|
||||||
dest.Network = h.server.Network
|
dest.Network = h.server.Network
|
||||||
}
|
}
|
||||||
|
@@ -86,10 +86,15 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
|
|||||||
|
|
||||||
destinationOverridden := false
|
destinationOverridden := false
|
||||||
if d.config.FollowRedirect {
|
if d.config.FollowRedirect {
|
||||||
if outbound := session.OutboundFromContext(ctx); outbound != nil && outbound.Target.IsValid() {
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
dest = outbound.Target
|
if len(outbounds) > 0 {
|
||||||
destinationOverridden = true
|
ob := outbounds[len(outbounds) - 1]
|
||||||
} else if handshake, ok := conn.(hasHandshakeAddressContext); ok {
|
if ob.Target.IsValid() {
|
||||||
|
dest = ob.Target
|
||||||
|
destinationOverridden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if handshake, ok := conn.(hasHandshakeAddressContext); ok && !destinationOverridden {
|
||||||
addr := handshake.HandshakeAddressContext(ctx)
|
addr := handshake.HandshakeAddressContext(ctx)
|
||||||
if addr != nil {
|
if addr != nil {
|
||||||
dest.Address = addr
|
dest.Address = addr
|
||||||
@@ -103,7 +108,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
|
|||||||
|
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
inbound.Name = "dokodemo-door"
|
inbound.Name = "dokodemo-door"
|
||||||
inbound.SetCanSpliceCopy(1)
|
inbound.CanSpliceCopy = 1
|
||||||
inbound.User = &protocol.MemoryUser{
|
inbound.User = &protocol.MemoryUser{
|
||||||
Level: d.config.UserLevel,
|
Level: d.config.UserLevel,
|
||||||
}
|
}
|
||||||
|
@@ -106,16 +106,16 @@ func isValidAddress(addr *net.IPOrDomain) bool {
|
|||||||
|
|
||||||
// Process implements proxy.Outbound.
|
// Process implements proxy.Outbound.
|
||||||
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil || !outbound.Target.IsValid() {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if !ob.Target.IsValid() {
|
||||||
return newError("target not specified.")
|
return newError("target not specified.")
|
||||||
}
|
}
|
||||||
outbound.Name = "freedom"
|
ob.Name = "freedom"
|
||||||
|
ob.CanSpliceCopy = 1
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
if inbound != nil {
|
|
||||||
inbound.SetCanSpliceCopy(1)
|
destination := ob.Target
|
||||||
}
|
|
||||||
destination := outbound.Target
|
|
||||||
UDPOverride := net.UDPDestination(nil, 0)
|
UDPOverride := net.UDPDestination(nil, 0)
|
||||||
if h.config.DestinationOverride != nil {
|
if h.config.DestinationOverride != nil {
|
||||||
server := h.config.DestinationOverride.Server
|
server := h.config.DestinationOverride.Server
|
||||||
@@ -219,10 +219,12 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
|
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
|
||||||
if destination.Network == net.Network_TCP {
|
if destination.Network == net.Network_TCP {
|
||||||
var writeConn net.Conn
|
var writeConn net.Conn
|
||||||
|
var inTimer *signal.ActivityTimer
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil && useSplice {
|
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil && useSplice {
|
||||||
writeConn = inbound.Conn
|
writeConn = inbound.Conn
|
||||||
|
inTimer = inbound.Timer
|
||||||
}
|
}
|
||||||
return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer)
|
return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer, inTimer)
|
||||||
}
|
}
|
||||||
reader := NewPacketReader(conn, UDPOverride)
|
reader := NewPacketReader(conn, UDPOverride)
|
||||||
if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
|
if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
|
||||||
|
@@ -69,16 +69,14 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
|
|||||||
|
|
||||||
// Process implements proxy.Outbound.Process. We first create a socket tunnel via HTTP CONNECT method, then redirect all inbound traffic to that tunnel.
|
// Process implements proxy.Outbound.Process. We first create a socket tunnel via HTTP CONNECT method, then redirect all inbound traffic to that tunnel.
|
||||||
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil || !outbound.Target.IsValid() {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if !ob.Target.IsValid() {
|
||||||
return newError("target not specified.")
|
return newError("target not specified.")
|
||||||
}
|
}
|
||||||
outbound.Name = "http"
|
ob.Name = "http"
|
||||||
inbound := session.InboundFromContext(ctx)
|
ob.CanSpliceCopy = 2
|
||||||
if inbound != nil {
|
target := ob.Target
|
||||||
inbound.SetCanSpliceCopy(2)
|
|
||||||
}
|
|
||||||
target := outbound.Target
|
|
||||||
targetAddr := target.NetAddr()
|
targetAddr := target.NetAddr()
|
||||||
|
|
||||||
if target.Network == net.Network_UDP {
|
if target.Network == net.Network_UDP {
|
||||||
@@ -175,9 +173,10 @@ func fillRequestHeader(ctx context.Context, header []*Header) ([]*Header, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
|
||||||
if inbound == nil || outbound == nil {
|
if inbound == nil || ob == nil {
|
||||||
return nil, newError("missing inbound or outbound metadata from context")
|
return nil, newError("missing inbound or outbound metadata from context")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +185,7 @@ func fillRequestHeader(ctx context.Context, header []*Header) ([]*Header, error)
|
|||||||
Target net.Destination
|
Target net.Destination
|
||||||
}{
|
}{
|
||||||
Source: inbound.Source,
|
Source: inbound.Source,
|
||||||
Target: outbound.Target,
|
Target: ob.Target,
|
||||||
}
|
}
|
||||||
|
|
||||||
filled := make([]*Header, len(header))
|
filled := make([]*Header, len(header))
|
||||||
|
@@ -85,7 +85,7 @@ type readerOnly struct {
|
|||||||
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
inbound.Name = "http"
|
inbound.Name = "http"
|
||||||
inbound.SetCanSpliceCopy(2)
|
inbound.CanSpliceCopy = 2
|
||||||
inbound.User = &protocol.MemoryUser{
|
inbound.User = &protocol.MemoryUser{
|
||||||
Level: s.config.UserLevel,
|
Level: s.config.UserLevel,
|
||||||
}
|
}
|
||||||
|
@@ -22,12 +22,13 @@ type Loopback struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *Loopback) Process(ctx context.Context, link *transport.Link, _ internet.Dialer) error {
|
func (l *Loopback) Process(ctx context.Context, link *transport.Link, _ internet.Dialer) error {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil || !outbound.Target.IsValid() {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if !ob.Target.IsValid() {
|
||||||
return newError("target not specified.")
|
return newError("target not specified.")
|
||||||
}
|
}
|
||||||
outbound.Name = "loopback"
|
ob.Name = "loopback"
|
||||||
destination := outbound.Target
|
destination := ob.Target
|
||||||
|
|
||||||
newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx))
|
newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
|
||||||
|
108
proxy/proxy.go
108
proxy/proxy.go
@@ -470,49 +470,81 @@ func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
|
|||||||
// CopyRawConnIfExist use the most efficient copy method.
|
// CopyRawConnIfExist use the most efficient copy method.
|
||||||
// - If caller don't want to turn on splice, do not pass in both reader conn and writer conn
|
// - If caller don't want to turn on splice, do not pass in both reader conn and writer conn
|
||||||
// - writer are from *transport.Link
|
// - writer are from *transport.Link
|
||||||
func CopyRawConnIfExist(ctx context.Context, readerConn net.Conn, writerConn net.Conn, writer buf.Writer, timer signal.ActivityUpdater) error {
|
func CopyRawConnIfExist(ctx context.Context, readerConn net.Conn, writerConn net.Conn, writer buf.Writer, timer *signal.ActivityTimer, inTimer *signal.ActivityTimer) error {
|
||||||
readerConn, readCounter, _ := UnwrapRawConn(readerConn)
|
readerConn, readCounter, _ := UnwrapRawConn(readerConn)
|
||||||
writerConn, _, writeCounter := UnwrapRawConn(writerConn)
|
writerConn, _, writeCounter := UnwrapRawConn(writerConn)
|
||||||
reader := buf.NewReader(readerConn)
|
reader := buf.NewReader(readerConn)
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
if runtime.GOOS != "linux" && runtime.GOOS != "android" {
|
||||||
if tc, ok := writerConn.(*net.TCPConn); ok && readerConn != nil && writerConn != nil && (runtime.GOOS == "linux" || runtime.GOOS == "android") {
|
return readV(ctx, reader, writer, timer, readCounter)
|
||||||
for inbound.CanSpliceCopy != 3 {
|
}
|
||||||
if inbound.CanSpliceCopy == 1 {
|
tc, ok := writerConn.(*net.TCPConn)
|
||||||
newError("CopyRawConn splice").WriteToLog(session.ExportIDToError(ctx))
|
if !ok || readerConn == nil || writerConn == nil {
|
||||||
statWriter, _ := writer.(*dispatcher.SizeStatWriter)
|
return readV(ctx, reader, writer, timer, readCounter)
|
||||||
//runtime.Gosched() // necessary
|
}
|
||||||
time.Sleep(time.Millisecond) // without this, there will be a rare ssl error for freedom splice
|
inbound := session.InboundFromContext(ctx)
|
||||||
w, err := tc.ReadFrom(readerConn)
|
if inbound == nil || inbound.CanSpliceCopy == 3 {
|
||||||
if readCounter != nil {
|
return readV(ctx, reader, writer, timer, readCounter)
|
||||||
readCounter.Add(w) // outbound stats
|
}
|
||||||
}
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if writeCounter != nil {
|
if len(outbounds) == 0 {
|
||||||
writeCounter.Add(w) // inbound stats
|
return readV(ctx, reader, writer, timer, readCounter)
|
||||||
}
|
}
|
||||||
if statWriter != nil {
|
for _, ob := range outbounds {
|
||||||
statWriter.Counter.Add(w) // user stats
|
if ob.CanSpliceCopy == 3 {
|
||||||
}
|
return readV(ctx, reader, writer, timer, readCounter)
|
||||||
if err != nil && errors.Cause(err) != io.EOF {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
buffer, err := reader.ReadMultiBuffer()
|
|
||||||
if !buffer.IsEmpty() {
|
|
||||||
if readCounter != nil {
|
|
||||||
readCounter.Add(int64(buffer.Len()))
|
|
||||||
}
|
|
||||||
timer.Update()
|
|
||||||
if werr := writer.WriteMultiBuffer(buffer); werr != nil {
|
|
||||||
return werr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
inbound := session.InboundFromContext(ctx)
|
||||||
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
var splice = inbound.CanSpliceCopy == 1
|
||||||
|
for _, ob := range outbounds {
|
||||||
|
if ob.CanSpliceCopy != 1 {
|
||||||
|
splice = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if splice {
|
||||||
|
newError("CopyRawConn splice").WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
statWriter, _ := writer.(*dispatcher.SizeStatWriter)
|
||||||
|
//runtime.Gosched() // necessary
|
||||||
|
time.Sleep(time.Millisecond) // without this, there will be a rare ssl error for freedom splice
|
||||||
|
timer.SetTimeout(8 * time.Hour) // prevent leak, just in case
|
||||||
|
if inTimer != nil {
|
||||||
|
inTimer.SetTimeout(8 * time.Hour)
|
||||||
|
}
|
||||||
|
w, err := tc.ReadFrom(readerConn)
|
||||||
|
if readCounter != nil {
|
||||||
|
readCounter.Add(w) // outbound stats
|
||||||
|
}
|
||||||
|
if writeCounter != nil {
|
||||||
|
writeCounter.Add(w) // inbound stats
|
||||||
|
}
|
||||||
|
if statWriter != nil {
|
||||||
|
statWriter.Counter.Add(w) // user stats
|
||||||
|
}
|
||||||
|
if err != nil && errors.Cause(err) != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
buffer, err := reader.ReadMultiBuffer()
|
||||||
|
if !buffer.IsEmpty() {
|
||||||
|
if readCounter != nil {
|
||||||
|
readCounter.Add(int64(buffer.Len()))
|
||||||
|
}
|
||||||
|
timer.Update()
|
||||||
|
if werr := writer.WriteMultiBuffer(buffer); werr != nil {
|
||||||
|
return werr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readV(ctx context.Context, reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, readCounter stats.Counter) error {
|
||||||
newError("CopyRawConn readv").WriteToLog(session.ExportIDToError(ctx))
|
newError("CopyRawConn readv").WriteToLog(session.ExportIDToError(ctx))
|
||||||
if err := buf.Copy(reader, writer, buf.UpdateActivity(timer), buf.AddToStatCounter(readCounter)); err != nil {
|
if err := buf.Copy(reader, writer, buf.UpdateActivity(timer), buf.AddToStatCounter(readCounter)); err != nil {
|
||||||
return newError("failed to process response").Base(err)
|
return newError("failed to process response").Base(err)
|
||||||
|
@@ -49,16 +49,14 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
|
|||||||
|
|
||||||
// Process implements OutboundHandler.Process().
|
// Process implements OutboundHandler.Process().
|
||||||
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil || !outbound.Target.IsValid() {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if !ob.Target.IsValid() {
|
||||||
return newError("target not specified")
|
return newError("target not specified")
|
||||||
}
|
}
|
||||||
outbound.Name = "shadowsocks"
|
ob.Name = "shadowsocks"
|
||||||
inbound := session.InboundFromContext(ctx)
|
ob.CanSpliceCopy = 3
|
||||||
if inbound != nil {
|
destination := ob.Target
|
||||||
inbound.SetCanSpliceCopy(3)
|
|
||||||
}
|
|
||||||
destination := outbound.Target
|
|
||||||
network := destination.Network
|
network := destination.Network
|
||||||
|
|
||||||
var server *protocol.ServerSpec
|
var server *protocol.ServerSpec
|
||||||
|
@@ -73,7 +73,7 @@ func (s *Server) Network() []net.Network {
|
|||||||
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
inbound.Name = "shadowsocks"
|
inbound.Name = "shadowsocks"
|
||||||
inbound.SetCanSpliceCopy(3)
|
inbound.CanSpliceCopy = 3
|
||||||
|
|
||||||
switch network {
|
switch network {
|
||||||
case net.Network_TCP:
|
case net.Network_TCP:
|
||||||
|
@@ -66,7 +66,7 @@ func (i *Inbound) Network() []net.Network {
|
|||||||
func (i *Inbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
func (i *Inbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
inbound.Name = "shadowsocks-2022"
|
inbound.Name = "shadowsocks-2022"
|
||||||
inbound.SetCanSpliceCopy(3)
|
inbound.CanSpliceCopy = 3
|
||||||
|
|
||||||
var metadata M.Metadata
|
var metadata M.Metadata
|
||||||
if inbound.Source.IsValid() {
|
if inbound.Source.IsValid() {
|
||||||
|
@@ -155,7 +155,7 @@ func (i *MultiUserInbound) Network() []net.Network {
|
|||||||
func (i *MultiUserInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
func (i *MultiUserInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
inbound.Name = "shadowsocks-2022-multi"
|
inbound.Name = "shadowsocks-2022-multi"
|
||||||
inbound.SetCanSpliceCopy(3)
|
inbound.CanSpliceCopy = 3
|
||||||
|
|
||||||
var metadata M.Metadata
|
var metadata M.Metadata
|
||||||
if inbound.Source.IsValid() {
|
if inbound.Source.IsValid() {
|
||||||
|
@@ -87,7 +87,7 @@ func (i *RelayInbound) Network() []net.Network {
|
|||||||
func (i *RelayInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
func (i *RelayInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
inbound.Name = "shadowsocks-2022-relay"
|
inbound.Name = "shadowsocks-2022-relay"
|
||||||
inbound.SetCanSpliceCopy(3)
|
inbound.CanSpliceCopy = 3
|
||||||
|
|
||||||
var metadata M.Metadata
|
var metadata M.Metadata
|
||||||
if inbound.Source.IsValid() {
|
if inbound.Source.IsValid() {
|
||||||
|
@@ -65,15 +65,16 @@ func (o *Outbound) Process(ctx context.Context, link *transport.Link, dialer int
|
|||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
if inbound != nil {
|
if inbound != nil {
|
||||||
inboundConn = inbound.Conn
|
inboundConn = inbound.Conn
|
||||||
inbound.SetCanSpliceCopy(3)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil || !outbound.Target.IsValid() {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if !ob.Target.IsValid() {
|
||||||
return newError("target not specified")
|
return newError("target not specified")
|
||||||
}
|
}
|
||||||
outbound.Name = "shadowsocks-2022"
|
ob.Name = "shadowsocks-2022"
|
||||||
destination := outbound.Target
|
ob.CanSpliceCopy = 3
|
||||||
|
destination := ob.Target
|
||||||
network := destination.Network
|
network := destination.Network
|
||||||
|
|
||||||
newError("tunneling request to ", destination, " via ", o.server.NetAddr()).WriteToLog(session.ExportIDToError(ctx))
|
newError("tunneling request to ", destination, " via ", o.server.NetAddr()).WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
@@ -57,17 +57,15 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
|
|||||||
|
|
||||||
// Process implements proxy.Outbound.Process.
|
// Process implements proxy.Outbound.Process.
|
||||||
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil || !outbound.Target.IsValid() {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if !ob.Target.IsValid() {
|
||||||
return newError("target not specified.")
|
return newError("target not specified.")
|
||||||
}
|
}
|
||||||
outbound.Name = "socks"
|
ob.Name = "socks"
|
||||||
inbound := session.InboundFromContext(ctx)
|
ob.CanSpliceCopy = 2
|
||||||
if inbound != nil {
|
|
||||||
inbound.SetCanSpliceCopy(2)
|
|
||||||
}
|
|
||||||
// Destination of the inner request.
|
// Destination of the inner request.
|
||||||
destination := outbound.Target
|
destination := ob.Target
|
||||||
|
|
||||||
// Outbound server.
|
// Outbound server.
|
||||||
var server *protocol.ServerSpec
|
var server *protocol.ServerSpec
|
||||||
|
@@ -27,6 +27,7 @@ type Server struct {
|
|||||||
config *ServerConfig
|
config *ServerConfig
|
||||||
policyManager policy.Manager
|
policyManager policy.Manager
|
||||||
cone bool
|
cone bool
|
||||||
|
udpFilter *UDPFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new Server object.
|
// NewServer creates a new Server object.
|
||||||
@@ -37,6 +38,9 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
|
|||||||
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
||||||
cone: ctx.Value("cone").(bool),
|
cone: ctx.Value("cone").(bool),
|
||||||
}
|
}
|
||||||
|
if config.AuthType == AuthType_PASSWORD {
|
||||||
|
s.udpFilter = new(UDPFilter) // We only use this when auth is enabled
|
||||||
|
}
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +69,7 @@ func (s *Server) Network() []net.Network {
|
|||||||
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
inbound.Name = "socks"
|
inbound.Name = "socks"
|
||||||
inbound.SetCanSpliceCopy(2)
|
inbound.CanSpliceCopy = 2
|
||||||
inbound.User = &protocol.MemoryUser{
|
inbound.User = &protocol.MemoryUser{
|
||||||
Level: s.config.UserLevel,
|
Level: s.config.UserLevel,
|
||||||
}
|
}
|
||||||
@@ -135,6 +139,9 @@ func (s *Server) processTCP(ctx context.Context, conn stat.Connection, dispatche
|
|||||||
}
|
}
|
||||||
|
|
||||||
if request.Command == protocol.RequestCommandUDP {
|
if request.Command == protocol.RequestCommandUDP {
|
||||||
|
if s.udpFilter != nil {
|
||||||
|
s.udpFilter.Add(conn.RemoteAddr())
|
||||||
|
}
|
||||||
return s.handleUDP(conn)
|
return s.handleUDP(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +200,10 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
|
if s.udpFilter != nil && !s.udpFilter.Check(conn.RemoteAddr()) {
|
||||||
|
newError("Unauthorized UDP access from ", conn.RemoteAddr().String()).AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
||||||
payload := packet.Payload
|
payload := packet.Payload
|
||||||
newError("writing back UDP response with ", payload.Len(), " bytes").AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
newError("writing back UDP response with ", payload.Len(), " bytes").AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
31
proxy/socks/udpfilter.go
Normal file
31
proxy/socks/udpfilter.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package socks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
In the sock implementation of * ray, UDP authentication is flawed and can be bypassed.
|
||||||
|
Tracking a UDP connection may be a bit troublesome.
|
||||||
|
Here is a simple solution.
|
||||||
|
We creat a filter, add remote IP to the pool when it try to establish a UDP connection with auth.
|
||||||
|
And drop UDP packets from unauthorized IP.
|
||||||
|
After discussion, we believe it is not necessary to add a timeout mechanism to this filter.
|
||||||
|
*/
|
||||||
|
|
||||||
|
type UDPFilter struct {
|
||||||
|
ips sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *UDPFilter) Add(addr net.Addr) bool {
|
||||||
|
ip, _, _ := net.SplitHostPort(addr.String())
|
||||||
|
f.ips.Store(ip, true)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *UDPFilter) Check(addr net.Addr) bool {
|
||||||
|
ip, _, _ := net.SplitHostPort(addr.String())
|
||||||
|
_, ok := f.ips.Load(ip)
|
||||||
|
return ok
|
||||||
|
}
|
@@ -50,16 +50,14 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) {
|
|||||||
|
|
||||||
// Process implements OutboundHandler.Process().
|
// Process implements OutboundHandler.Process().
|
||||||
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
func (c *Client) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil || !outbound.Target.IsValid() {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if !ob.Target.IsValid() {
|
||||||
return newError("target not specified")
|
return newError("target not specified")
|
||||||
}
|
}
|
||||||
outbound.Name = "trojan"
|
ob.Name = "trojan"
|
||||||
inbound := session.InboundFromContext(ctx)
|
ob.CanSpliceCopy = 3
|
||||||
if inbound != nil {
|
destination := ob.Target
|
||||||
inbound.SetCanSpliceCopy(3)
|
|
||||||
}
|
|
||||||
destination := outbound.Target
|
|
||||||
network := destination.Network
|
network := destination.Network
|
||||||
|
|
||||||
var server *protocol.ServerSpec
|
var server *protocol.ServerSpec
|
||||||
|
@@ -215,7 +215,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con
|
|||||||
|
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
inbound.Name = "trojan"
|
inbound.Name = "trojan"
|
||||||
inbound.SetCanSpliceCopy(3)
|
inbound.CanSpliceCopy = 3
|
||||||
inbound.User = user
|
inbound.User = user
|
||||||
sessionPolicy = s.policyManager.ForLevel(user.Level)
|
sessionPolicy = s.policyManager.ForLevel(user.Level)
|
||||||
|
|
||||||
|
@@ -174,18 +174,23 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A
|
|||||||
}
|
}
|
||||||
|
|
||||||
// XtlsRead filter and read xtls protocol
|
// XtlsRead filter and read xtls protocol
|
||||||
func XtlsRead(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ctx context.Context) error {
|
func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, ctx context.Context) error {
|
||||||
err := func() error {
|
err := func() error {
|
||||||
for {
|
for {
|
||||||
if trafficState.ReaderSwitchToDirectCopy {
|
if trafficState.ReaderSwitchToDirectCopy {
|
||||||
var writerConn net.Conn
|
var writerConn net.Conn
|
||||||
|
var inTimer *signal.ActivityTimer
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil {
|
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil {
|
||||||
writerConn = inbound.Conn
|
writerConn = inbound.Conn
|
||||||
|
inTimer = inbound.Timer
|
||||||
if inbound.CanSpliceCopy == 2 {
|
if inbound.CanSpliceCopy == 2 {
|
||||||
inbound.CanSpliceCopy = 1 // force the value to 1, don't use setter
|
inbound.CanSpliceCopy = 1
|
||||||
|
}
|
||||||
|
if ob != nil && ob.CanSpliceCopy == 2 { // ob need to be passed in due to context can change
|
||||||
|
ob.CanSpliceCopy = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return proxy.CopyRawConnIfExist(ctx, conn, writerConn, writer, timer)
|
return proxy.CopyRawConnIfExist(ctx, conn, writerConn, writer, timer, inTimer)
|
||||||
}
|
}
|
||||||
buffer, err := reader.ReadMultiBuffer()
|
buffer, err := reader.ReadMultiBuffer()
|
||||||
if !buffer.IsEmpty() {
|
if !buffer.IsEmpty() {
|
||||||
@@ -219,14 +224,19 @@ func XtlsRead(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater
|
|||||||
}
|
}
|
||||||
|
|
||||||
// XtlsWrite filter and write xtls protocol
|
// XtlsWrite filter and write xtls protocol
|
||||||
func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn net.Conn, trafficState *proxy.TrafficState, ctx context.Context) error {
|
func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn net.Conn, trafficState *proxy.TrafficState, ob *session.Outbound, ctx context.Context) error {
|
||||||
err := func() error {
|
err := func() error {
|
||||||
var ct stats.Counter
|
var ct stats.Counter
|
||||||
for {
|
for {
|
||||||
buffer, err := reader.ReadMultiBuffer()
|
buffer, err := reader.ReadMultiBuffer()
|
||||||
if trafficState.WriterSwitchToDirectCopy {
|
if trafficState.WriterSwitchToDirectCopy {
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.CanSpliceCopy == 2 {
|
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||||
inbound.CanSpliceCopy = 1 // force the value to 1, don't use setter
|
if inbound.CanSpliceCopy == 2 {
|
||||||
|
inbound.CanSpliceCopy = 1
|
||||||
|
}
|
||||||
|
if ob != nil && ob.CanSpliceCopy == 2 {
|
||||||
|
ob.CanSpliceCopy = 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rawConn, _, writerCounter := proxy.UnwrapRawConn(conn)
|
rawConn, _, writerCounter := proxy.UnwrapRawConn(conn)
|
||||||
writer = buf.NewWriter(rawConn)
|
writer = buf.NewWriter(rawConn)
|
||||||
|
@@ -449,7 +449,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
switch requestAddons.Flow {
|
switch requestAddons.Flow {
|
||||||
case vless.XRV:
|
case vless.XRV:
|
||||||
if account.Flow == requestAddons.Flow {
|
if account.Flow == requestAddons.Flow {
|
||||||
inbound.SetCanSpliceCopy(2)
|
inbound.CanSpliceCopy = 2
|
||||||
switch request.Command {
|
switch request.Command {
|
||||||
case protocol.RequestCommandUDP:
|
case protocol.RequestCommandUDP:
|
||||||
return newError(requestAddons.Flow + " doesn't support UDP").AtWarning()
|
return newError(requestAddons.Flow + " doesn't support UDP").AtWarning()
|
||||||
@@ -479,7 +479,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
return newError(account.ID.String() + " is not able to use " + requestAddons.Flow).AtWarning()
|
return newError(account.ID.String() + " is not able to use " + requestAddons.Flow).AtWarning()
|
||||||
}
|
}
|
||||||
case "":
|
case "":
|
||||||
inbound.SetCanSpliceCopy(3)
|
inbound.CanSpliceCopy = 3
|
||||||
if account.Flow == vless.XRV && (request.Command == protocol.RequestCommandTCP || isMuxAndNotXUDP(request, first)) {
|
if account.Flow == vless.XRV && (request.Command == protocol.RequestCommandTCP || isMuxAndNotXUDP(request, first)) {
|
||||||
return newError(account.ID.String() + " is not able to use \"\". Note that the pure TLS proxy has certain TLS in TLS characters.").AtWarning()
|
return newError(account.ID.String() + " is not able to use \"\". Note that the pure TLS proxy has certain TLS in TLS characters.").AtWarning()
|
||||||
}
|
}
|
||||||
@@ -502,6 +502,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
sessionPolicy = h.policyManager.ForLevel(request.User.Level)
|
sessionPolicy = h.policyManager.ForLevel(request.User.Level)
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
|
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
|
||||||
|
inbound.Timer = timer
|
||||||
ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)
|
ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)
|
||||||
|
|
||||||
link, err := dispatcher.Dispatch(ctx, request.Destination())
|
link, err := dispatcher.Dispatch(ctx, request.Destination())
|
||||||
@@ -523,7 +524,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
if requestAddons.Flow == vless.XRV {
|
if requestAddons.Flow == vless.XRV {
|
||||||
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
|
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
|
||||||
clientReader = proxy.NewVisionReader(clientReader, trafficState, ctx1)
|
clientReader = proxy.NewVisionReader(clientReader, trafficState, ctx1)
|
||||||
err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, input, rawInput, trafficState, ctx1)
|
err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, input, rawInput, trafficState, nil, ctx1)
|
||||||
} else {
|
} else {
|
||||||
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer
|
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer
|
||||||
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
||||||
@@ -560,7 +561,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
if requestAddons.Flow == vless.XRV {
|
if requestAddons.Flow == vless.XRV {
|
||||||
err = encoding.XtlsWrite(serverReader, clientWriter, timer, connection, trafficState, ctx)
|
err = encoding.XtlsWrite(serverReader, clientWriter, timer, connection, trafficState, nil, ctx)
|
||||||
} else {
|
} else {
|
||||||
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer
|
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer
|
||||||
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
||||||
|
@@ -70,12 +70,12 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
|
|||||||
|
|
||||||
// Process implements proxy.Outbound.Process().
|
// Process implements proxy.Outbound.Process().
|
||||||
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil || !outbound.Target.IsValid() {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if !ob.Target.IsValid() {
|
||||||
return newError("target not specified").AtError()
|
return newError("target not specified").AtError()
|
||||||
}
|
}
|
||||||
outbound.Name = "vless"
|
ob.Name = "vless"
|
||||||
inbound := session.InboundFromContext(ctx)
|
|
||||||
|
|
||||||
var rec *protocol.ServerSpec
|
var rec *protocol.ServerSpec
|
||||||
var conn stat.Connection
|
var conn stat.Connection
|
||||||
@@ -96,7 +96,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
if statConn, ok := iConn.(*stat.CounterConnection); ok {
|
if statConn, ok := iConn.(*stat.CounterConnection); ok {
|
||||||
iConn = statConn.Connection
|
iConn = statConn.Connection
|
||||||
}
|
}
|
||||||
target := outbound.Target
|
target := ob.Target
|
||||||
newError("tunneling request to ", target, " via ", rec.Destination().NetAddr()).AtInfo().WriteToLog(session.ExportIDToError(ctx))
|
newError("tunneling request to ", target, " via ", rec.Destination().NetAddr()).AtInfo().WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
|
||||||
command := protocol.RequestCommandTCP
|
command := protocol.RequestCommandTCP
|
||||||
@@ -130,9 +130,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
requestAddons.Flow = requestAddons.Flow[:16]
|
requestAddons.Flow = requestAddons.Flow[:16]
|
||||||
fallthrough
|
fallthrough
|
||||||
case vless.XRV:
|
case vless.XRV:
|
||||||
if inbound != nil {
|
ob.CanSpliceCopy = 2
|
||||||
inbound.SetCanSpliceCopy(2)
|
|
||||||
}
|
|
||||||
switch request.Command {
|
switch request.Command {
|
||||||
case protocol.RequestCommandUDP:
|
case protocol.RequestCommandUDP:
|
||||||
if !allowUDP443 && request.Port == 443 {
|
if !allowUDP443 && request.Port == 443 {
|
||||||
@@ -161,9 +159,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset))
|
rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if inbound != nil {
|
ob.CanSpliceCopy = 3
|
||||||
inbound.SetCanSpliceCopy(3)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var newCtx context.Context
|
var newCtx context.Context
|
||||||
@@ -238,8 +234,8 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
return newError(`failed to use `+requestAddons.Flow+`, found outer tls version `, utlsConn.ConnectionState().Version).AtWarning()
|
return newError(`failed to use `+requestAddons.Flow+`, found outer tls version `, utlsConn.ConnectionState().Version).AtWarning()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx1 := session.ContextWithOutbound(ctx, nil) // TODO enable splice
|
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
|
||||||
err = encoding.XtlsWrite(clientReader, serverWriter, timer, conn, trafficState, ctx1)
|
err = encoding.XtlsWrite(clientReader, serverWriter, timer, conn, trafficState, ob, ctx1)
|
||||||
} else {
|
} else {
|
||||||
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer
|
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer
|
||||||
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
||||||
@@ -277,7 +273,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
}
|
}
|
||||||
|
|
||||||
if requestAddons.Flow == vless.XRV {
|
if requestAddons.Flow == vless.XRV {
|
||||||
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, input, rawInput, trafficState, ctx)
|
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, input, rawInput, trafficState, ob, ctx)
|
||||||
} else {
|
} else {
|
||||||
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer
|
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer
|
||||||
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
||||||
|
@@ -257,7 +257,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
|
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
inbound.Name = "vmess"
|
inbound.Name = "vmess"
|
||||||
inbound.SetCanSpliceCopy(3)
|
inbound.CanSpliceCopy = 3
|
||||||
inbound.User = request.User
|
inbound.User = request.User
|
||||||
|
|
||||||
sessionPolicy = h.policyManager.ForLevel(request.User.Level)
|
sessionPolicy = h.policyManager.ForLevel(request.User.Level)
|
||||||
|
@@ -60,15 +60,13 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
|
|||||||
|
|
||||||
// Process implements proxy.Outbound.Process().
|
// Process implements proxy.Outbound.Process().
|
||||||
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil || !outbound.Target.IsValid() {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if !ob.Target.IsValid() {
|
||||||
return newError("target not specified").AtError()
|
return newError("target not specified").AtError()
|
||||||
}
|
}
|
||||||
outbound.Name = "vmess"
|
ob.Name = "vmess"
|
||||||
inbound := session.InboundFromContext(ctx)
|
ob.CanSpliceCopy = 3
|
||||||
if inbound != nil {
|
|
||||||
inbound.SetCanSpliceCopy(3)
|
|
||||||
}
|
|
||||||
|
|
||||||
var rec *protocol.ServerSpec
|
var rec *protocol.ServerSpec
|
||||||
var conn stat.Connection
|
var conn stat.Connection
|
||||||
@@ -87,7 +85,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
target := outbound.Target
|
target := ob.Target
|
||||||
newError("tunneling request to ", target, " via ", rec.Destination().NetAddr()).WriteToLog(session.ExportIDToError(ctx))
|
newError("tunneling request to ", target, " via ", rec.Destination().NetAddr()).WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
|
||||||
command := protocol.RequestCommandTCP
|
command := protocol.RequestCommandTCP
|
||||||
|
@@ -127,22 +127,20 @@ func (h *Handler) processWireGuard(dialer internet.Dialer) (err error) {
|
|||||||
|
|
||||||
// Process implements OutboundHandler.Dispatch().
|
// Process implements OutboundHandler.Dispatch().
|
||||||
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil || !outbound.Target.IsValid() {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if !ob.Target.IsValid() {
|
||||||
return newError("target not specified")
|
return newError("target not specified")
|
||||||
}
|
}
|
||||||
outbound.Name = "wireguard"
|
ob.Name = "wireguard"
|
||||||
inbound := session.InboundFromContext(ctx)
|
ob.CanSpliceCopy = 3
|
||||||
if inbound != nil {
|
|
||||||
inbound.SetCanSpliceCopy(3)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := h.processWireGuard(dialer); err != nil {
|
if err := h.processWireGuard(dialer); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destination of the inner request.
|
// Destination of the inner request.
|
||||||
destination := outbound.Target
|
destination := ob.Target
|
||||||
command := protocol.RequestCommandTCP
|
command := protocol.RequestCommandTCP
|
||||||
if destination.Network == net.Network_UDP {
|
if destination.Network == net.Network_UDP {
|
||||||
command = protocol.RequestCommandUDP
|
command = protocol.RequestCommandUDP
|
||||||
|
@@ -79,13 +79,15 @@ func (*Server) Network() []net.Network {
|
|||||||
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error {
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
inbound.Name = "wireguard"
|
inbound.Name = "wireguard"
|
||||||
inbound.SetCanSpliceCopy(3)
|
inbound.CanSpliceCopy = 3
|
||||||
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
|
||||||
s.info = routingInfo{
|
s.info = routingInfo{
|
||||||
ctx: core.ToBackgroundDetachedContext(ctx),
|
ctx: core.ToBackgroundDetachedContext(ctx),
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
inboundTag: session.InboundFromContext(ctx),
|
inboundTag: session.InboundFromContext(ctx),
|
||||||
outboundTag: session.OutboundFromContext(ctx),
|
outboundTag: ob,
|
||||||
contentTag: session.ContentFromContext(ctx),
|
contentTag: session.ContentFromContext(ctx),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +147,7 @@ func (s *Server) forwardConnection(dest net.Destination, conn net.Conn) {
|
|||||||
ctx = session.ContextWithInbound(ctx, s.info.inboundTag)
|
ctx = session.ContextWithInbound(ctx, s.info.inboundTag)
|
||||||
}
|
}
|
||||||
if s.info.outboundTag != nil {
|
if s.info.outboundTag != nil {
|
||||||
ctx = session.ContextWithOutbound(ctx, s.info.outboundTag)
|
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{s.info.outboundTag})
|
||||||
}
|
}
|
||||||
if s.info.contentTag != nil {
|
if s.info.contentTag != nil {
|
||||||
ctx = session.ContextWithContent(ctx, s.info.contentTag)
|
ctx = session.ContextWithContent(ctx, s.info.contentTag)
|
||||||
|
@@ -33,6 +33,80 @@ import (
|
|||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestCommanderListenConfigurationItem(t *testing.T) {
|
||||||
|
tcpServer := tcp.Server{
|
||||||
|
MsgProcessor: xor,
|
||||||
|
}
|
||||||
|
dest, err := tcpServer.Start()
|
||||||
|
common.Must(err)
|
||||||
|
defer tcpServer.Close()
|
||||||
|
|
||||||
|
clientPort := tcp.PickPort()
|
||||||
|
cmdPort := tcp.PickPort()
|
||||||
|
clientConfig := &core.Config{
|
||||||
|
App: []*serial.TypedMessage{
|
||||||
|
serial.ToTypedMessage(&commander.Config{
|
||||||
|
Tag: "api",
|
||||||
|
Listen: fmt.Sprintf("127.0.0.1:%d", cmdPort),
|
||||||
|
Service: []*serial.TypedMessage{
|
||||||
|
serial.ToTypedMessage(&command.Config{}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Inbound: []*core.InboundHandlerConfig{
|
||||||
|
{
|
||||||
|
Tag: "d",
|
||||||
|
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||||
|
PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(clientPort)}},
|
||||||
|
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||||
|
Address: net.NewIPOrDomain(dest.Address),
|
||||||
|
Port: uint32(dest.Port),
|
||||||
|
Networks: []net.Network{net.Network_TCP},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbound: []*core.OutboundHandlerConfig{
|
||||||
|
{
|
||||||
|
Tag: "default-outbound",
|
||||||
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
servers, err := InitializeServerConfigs(clientConfig)
|
||||||
|
common.Must(err)
|
||||||
|
defer CloseAllServers(servers)
|
||||||
|
|
||||||
|
if err := testTCPConn(clientPort, 1024, time.Second*5)(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
|
||||||
|
common.Must(err)
|
||||||
|
defer cmdConn.Close()
|
||||||
|
|
||||||
|
hsClient := command.NewHandlerServiceClient(cmdConn)
|
||||||
|
resp, err := hsClient.RemoveInbound(context.Background(), &command.RemoveInboundRequest{
|
||||||
|
Tag: "d",
|
||||||
|
})
|
||||||
|
common.Must(err)
|
||||||
|
if resp == nil {
|
||||||
|
t.Error("unexpected nil response")
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
_, err := net.DialTCP("tcp", nil, &net.TCPAddr{
|
||||||
|
IP: []byte{127, 0, 0, 1},
|
||||||
|
Port: int(clientPort),
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Error("unexpected nil error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCommanderRemoveHandler(t *testing.T) {
|
func TestCommanderRemoveHandler(t *testing.T) {
|
||||||
tcpServer := tcp.Server{
|
tcpServer := tcp.Server{
|
||||||
MsgProcessor: xor,
|
MsgProcessor: xor,
|
||||||
|
@@ -112,7 +112,12 @@ func canLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig
|
|||||||
func redirect(ctx context.Context, dst net.Destination, obt string) net.Conn {
|
func redirect(ctx context.Context, dst net.Destination, obt string) net.Conn {
|
||||||
newError("redirecting request " + dst.String() + " to " + obt).WriteToLog(session.ExportIDToError(ctx))
|
newError("redirecting request " + dst.String() + " to " + obt).WriteToLog(session.ExportIDToError(ctx))
|
||||||
h := obm.GetHandler(obt)
|
h := obm.GetHandler(obt)
|
||||||
ctx = session.ContextWithOutbound(ctx, &session.Outbound{Target: dst, Gateway: nil})
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, append(outbounds, &session.Outbound{
|
||||||
|
Target: dst,
|
||||||
|
Gateway: nil,
|
||||||
|
Tag: obt,
|
||||||
|
})) // add another outbound in session ctx
|
||||||
if h != nil {
|
if h != nil {
|
||||||
ur, uw := pipe.New(pipe.OptionsFromContext(ctx)...)
|
ur, uw := pipe.New(pipe.OptionsFromContext(ctx)...)
|
||||||
dr, dw := pipe.New(pipe.OptionsFromContext(ctx)...)
|
dr, dw := pipe.New(pipe.OptionsFromContext(ctx)...)
|
||||||
@@ -131,8 +136,10 @@ func redirect(ctx context.Context, dst net.Destination, obt string) net.Conn {
|
|||||||
// DialSystem calls system dialer to create a network connection.
|
// DialSystem calls system dialer to create a network connection.
|
||||||
func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
|
func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
|
||||||
var src net.Address
|
var src net.Address
|
||||||
if outbound := session.OutboundFromContext(ctx); outbound != nil {
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
src = outbound.Gateway
|
if len(outbounds) > 0 {
|
||||||
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
src = ob.Gateway
|
||||||
}
|
}
|
||||||
if sockopt == nil {
|
if sockopt == nil {
|
||||||
return effectiveSystemDialer.Dial(ctx, src, dest, sockopt)
|
return effectiveSystemDialer.Dial(ctx, src, dest, sockopt)
|
||||||
|
@@ -118,7 +118,7 @@ func getGrpcClient(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
address := net.ParseAddress(rawHost)
|
address := net.ParseAddress(rawHost)
|
||||||
|
|
||||||
gctx = session.ContextWithID(gctx, session.IDFromContext(ctx))
|
gctx = session.ContextWithID(gctx, session.IDFromContext(ctx))
|
||||||
gctx = session.ContextWithOutbound(gctx, session.OutboundFromContext(ctx))
|
gctx = session.ContextWithOutbounds(gctx, session.OutboundsFromContext(ctx))
|
||||||
gctx = session.ContextWithTimeoutOnly(gctx, true)
|
gctx = session.ContextWithTimeoutOnly(gctx, true)
|
||||||
|
|
||||||
c, err := internet.DialSystem(gctx, net.TCPDestination(address, port), sockopt)
|
c, err := internet.DialSystem(gctx, net.TCPDestination(address, port), sockopt)
|
||||||
|
@@ -68,7 +68,7 @@ func getHTTPClient(ctx context.Context, dest net.Destination, streamSettings *in
|
|||||||
address := net.ParseAddress(rawHost)
|
address := net.ParseAddress(rawHost)
|
||||||
|
|
||||||
hctx = session.ContextWithID(hctx, session.IDFromContext(ctx))
|
hctx = session.ContextWithID(hctx, session.IDFromContext(ctx))
|
||||||
hctx = session.ContextWithOutbound(hctx, session.OutboundFromContext(ctx))
|
hctx = session.ContextWithOutbounds(hctx, session.OutboundsFromContext(ctx))
|
||||||
hctx = session.ContextWithTimeoutOnly(hctx, true)
|
hctx = session.ContextWithTimeoutOnly(hctx, true)
|
||||||
|
|
||||||
pconn, err := internet.DialSystem(hctx, net.TCPDestination(address, port), sockopt)
|
pconn, err := internet.DialSystem(hctx, net.TCPDestination(address, port), sockopt)
|
||||||
|
Reference in New Issue
Block a user