mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-23 01:56:48 +08:00
Compare commits
109 Commits
v1.1.3
...
issue-temp
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8a647c1d8e | ||
![]() |
ea3be76fd5 | ||
![]() |
7abb02ab44 | ||
![]() |
18574aca47 | ||
![]() |
769bed9dbc | ||
![]() |
1233bd5031 | ||
![]() |
039c8e63e7 | ||
![]() |
29db059a87 | ||
![]() |
e1a5392beb | ||
![]() |
24f564b401 | ||
![]() |
54af48a1ae | ||
![]() |
055fb51ed9 | ||
![]() |
6380abca73 | ||
![]() |
1dae2c5636 | ||
![]() |
e9ea658852 | ||
![]() |
d67cf3d598 | ||
![]() |
ca633fc8c5 | ||
![]() |
c345d4818e | ||
![]() |
7fb1f65354 | ||
![]() |
4b97edae74 | ||
![]() |
8aabbeefe1 | ||
![]() |
48fab4d398 | ||
![]() |
8b9c0ae593 | ||
![]() |
347d9735da | ||
![]() |
9aa49be703 | ||
![]() |
fed8610d3f | ||
![]() |
d22c2d034c | ||
![]() |
4c10a9eb4e | ||
![]() |
4ff1ff1d7d | ||
![]() |
573b7807c0 | ||
![]() |
81d993158f | ||
![]() |
df39991bb3 | ||
![]() |
1b87264c53 | ||
![]() |
96d7156eba | ||
![]() |
d170416219 | ||
![]() |
8ca8a7126b | ||
![]() |
1174ff3090 | ||
![]() |
523c416bb5 | ||
![]() |
c13b8ec9bb | ||
![]() |
4cd343f2d5 | ||
![]() |
d032a8deb7 | ||
![]() |
303fd6e261 | ||
![]() |
c880b916ee | ||
![]() |
ceff4185dc | ||
![]() |
59c7c4897c | ||
![]() |
8ffc430351 | ||
![]() |
7da97635b2 | ||
![]() |
ba41513967 | ||
![]() |
5bc1bf30ae | ||
![]() |
5aa053a65f | ||
![]() |
0b4858d016 | ||
![]() |
7f5e34c857 | ||
![]() |
b60cf02603 | ||
![]() |
ae98dc75cf | ||
![]() |
8ff43519fd | ||
![]() |
33755d6e90 | ||
![]() |
99863aa2ac | ||
![]() |
8eed8a0824 | ||
![]() |
88f6537540 | ||
![]() |
f0efc0cfde | ||
![]() |
f13ac3cb55 | ||
![]() |
638e8384b6 | ||
![]() |
d85162ea44 | ||
![]() |
11a851f957 | ||
![]() |
822afb0cc8 | ||
![]() |
157918859f | ||
![]() |
40271c09a0 | ||
![]() |
96adf3fbca | ||
![]() |
e254424c43 | ||
![]() |
ee15cc253f | ||
![]() |
43eb5d1b25 | ||
![]() |
700966508f | ||
![]() |
7427a55ef1 | ||
![]() |
fb0e517158 | ||
![]() |
d5aeb6c545 | ||
![]() |
161e18299c | ||
![]() |
be9421fedf | ||
![]() |
8fc2d3b61f | ||
![]() |
9d4038427d | ||
![]() |
38ec9208d8 | ||
![]() |
7df135a5c4 | ||
![]() |
c41a1a56fe | ||
![]() |
310a938511 | ||
![]() |
2da07e0f8a | ||
![]() |
13ad3fddf6 | ||
![]() |
6bcac6cb10 | ||
![]() |
0203190a98 | ||
![]() |
a78db47571 | ||
![]() |
ffd8fd1d8a | ||
![]() |
3d7e86efba | ||
![]() |
6f25191822 | ||
![]() |
85619b5a29 | ||
![]() |
f073456ac0 | ||
![]() |
09f9d03fb6 | ||
![]() |
8f8f7dd66f | ||
![]() |
4140ed7ab0 | ||
![]() |
f390047b37 | ||
![]() |
ff9bb2d8df | ||
![]() |
38faac5ffc | ||
![]() |
88dfed931b | ||
![]() |
19ce0e99a5 | ||
![]() |
fe445f8e1a | ||
![]() |
6a5618bc54 | ||
![]() |
ed0e9b12dc | ||
![]() |
dab978749c | ||
![]() |
45f44c401a | ||
![]() |
2e942e0303 | ||
![]() |
decb012f9d | ||
![]() |
574446f942 |
92
.github/ISSUE_TEMPLATE/bug-en.md
vendored
Normal file
92
.github/ISSUE_TEMPLATE/bug-en.md
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a bug report of Xray.
|
||||
title: '[Bug] <bug you are reporting>'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Thanks for your reporting.
|
||||
1. Please make sure you are submitting a bug of Xray-Core instead of acquiring usage or bug in third-party programs. If you are not sure, please contact us in our official Telegram group.
|
||||
2. Bug: **An error, flaw or fault in a program ** that causes it to produce an incorrect or unexpected result. (Reference: Wikipedia)
|
||||
3. Please check existing Issues and Discussions first and read the documentation in detail. Duplicated issues will be closed.
|
||||
4. Please don't report issue like "I can't use a feature". It's probably your own mistake.
|
||||
5. You should fully complete the following contents or this issue may not be handled.
|
||||
6. Please *make sure* the content you are submitting does not contain your private information.
|
||||
-->
|
||||
|
||||
|
||||
**Describe the bug**
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
**To Reproduce**
|
||||
<!-- Steps to reproduce the bug: -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
**Expected behavior**
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
**Client Log**
|
||||
|
||||
<details>
|
||||
|
||||
```
|
||||
Please paste your client log here:
|
||||
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**Server Log**
|
||||
|
||||
<details>
|
||||
|
||||
```
|
||||
Please paste your server log here:
|
||||
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**Client config**
|
||||
|
||||
<details>
|
||||
|
||||
```json
|
||||
Please paste your client config file here:
|
||||
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**Server Config**
|
||||
|
||||
<details>
|
||||
|
||||
```json
|
||||
Please paste your server config file here:
|
||||
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**Client Information**
|
||||
- OS: [e.g. Windows 10]
|
||||
- Xray version [e.g. 1.3.1]
|
||||
- Xray installing approach:[e.g. Xray-install]
|
||||
|
||||
**Server Information**
|
||||
- OS: [e.g. Windows 10]
|
||||
- Xray version [e.g. 1.3.1]
|
||||
- Xray installing approach:[e.g. Xray-install]
|
||||
|
||||
**Additional Information**
|
||||
<!-- Add any other information about the problem here. -->
|
91
.github/ISSUE_TEMPLATE/bug-zh-CN.md
vendored
Normal file
91
.github/ISSUE_TEMPLATE/bug-zh-CN.md
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
name: Bug 反馈
|
||||
about: 这是 Xray 的一个 bug。
|
||||
title: '[Bug] 你发现的bug'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- 感谢您的反馈!
|
||||
1. 请先确认您提交的是 Xray-Core 的 Bug,而非使用咨询,抑或是第三方程序的 Bug。如果您不确定,请在 Telegram 群中反馈。
|
||||
2. Bug:软件运行中因为 **程序本身有错误** 而造成的功能不正常。(Reference: Wikipedia)
|
||||
3. 请先查询已有的 Issues 与 Discussions ,并且详细阅读文档的相关内容。如果您提出的是已知的问题,此 issue 将有可能被关闭。
|
||||
4. 请不要轻易提出类似“不能使用某功能”的问题。这往往是你自己的问题。
|
||||
5. 您需要完整地完成下列内容,否则此 issue 可能不会被处理。
|
||||
6. 请 *务必* 确保不包含任何个人隐私信息。
|
||||
-->
|
||||
|
||||
**问题描述**
|
||||
<!-- 请清晰简洁地描述此问题。-->
|
||||
|
||||
**复现方式**
|
||||
<!-- 复现此步骤的过程: -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
**预期行为**
|
||||
<!-- 请清晰简洁地描述您期望的的行为。-->
|
||||
|
||||
**客户端日志**
|
||||
|
||||
<details>
|
||||
|
||||
```
|
||||
请删除此行,并在此处粘贴客户端日志
|
||||
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**服务端日志**
|
||||
|
||||
<details>
|
||||
|
||||
```
|
||||
请删除此行,并在此处粘贴服务端日志
|
||||
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**客户端配置**
|
||||
|
||||
<details>
|
||||
|
||||
```json
|
||||
请删除此行,并在此处粘贴客户端配置
|
||||
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**服务端配置**
|
||||
|
||||
<details>
|
||||
|
||||
```json
|
||||
请删除此行,并在此处粘贴服务端日志
|
||||
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**客户端环境**
|
||||
- 系统与版本: [如 Windows 10]
|
||||
- Xray 版本 [如 1.3.1]
|
||||
- Xray 安装方式:[如 Xray-install]
|
||||
|
||||
**服务端环境**
|
||||
- 系统与版本: [如 Windows 10]
|
||||
- Xray 版本 [如 1.3.1]
|
||||
- Xray 安装方式:[如 Xray-install]
|
||||
|
||||
**附加信息**
|
||||
<!-- 如果您有额外的信息,请在此处说明。-->
|
9
.github/ISSUE_TEMPLATE/config.yaml
vendored
Normal file
9
.github/ISSUE_TEMPLATE/config.yaml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
contact_links:
|
||||
- name: 加入 Telegram 群组 / Join Telegram Group
|
||||
url: https://t.me/projectXray
|
||||
- name: 官方文档 / Official Document
|
||||
url: https://xtls.github.io/
|
||||
- name: 安装脚本 / Installing Script
|
||||
url: https://github.com/XTLS/Xray-install
|
||||
- name: 示例配置 / Example Config
|
||||
url: https://github.com/XTLS/Xray-examples
|
32
.github/ISSUE_TEMPLATE/feature-en.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/feature-en.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for Xray
|
||||
title: '[Feature] your idea'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Thanks for your support!
|
||||
1. Please confirm that you are submitting a feature request.
|
||||
2. We do not recommend any inexperienced users to request a new feature.
|
||||
3. Please check existing Issues and Discussions first and read the documentation in detail. Duplicated issues will be closed
|
||||
4. To be clear: none of developers is obliged to meet your needs. In particular, features that do not make any sense.
|
||||
5. You should fully complete the following contents.
|
||||
-->
|
||||
|
||||
**What's your idea?**
|
||||
<!-- A clear and concise description of your idea. -->
|
||||
|
||||
**Is it related to an issue?**
|
||||
<!-- Please specify here if yes. -->
|
||||
|
||||
**What's your solution?**
|
||||
<!-- Describe the solution you'd like. -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
**Is there any additional information?**
|
||||
<!-- Add any other information here. -->
|
32
.github/ISSUE_TEMPLATE/feature-zh-CN.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/feature-zh-CN.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: 功能请求
|
||||
about: 希望 Xray 添加一个新功能。
|
||||
title: '[Feature] 你的想法'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- 感谢您的反馈!
|
||||
1. 请先确认您提交的是功能请求。
|
||||
2. 我们不建议缺少经验的用户提出功能请求。
|
||||
3. 请先查询已有的 Issues 与 Discussions ,并且详细阅读文档的相关内容。重复的 issue 将有可能被关闭。
|
||||
4. 需要向您明确一点:任何开发者均没有义务满足您的需求。特别是不合理或没有意义的功能。
|
||||
5. 您需要完整地完成下列内容,否则此 issue 可能不会被处理。
|
||||
-->
|
||||
|
||||
**您的想法是什么?**
|
||||
<!-- 请清晰简洁地描述您预期中的功能。-->
|
||||
|
||||
**是否与已知的 issue 相关?**
|
||||
<!-- 如果是,请在此注明 -->
|
||||
|
||||
**您认为应如何实现此功能?**
|
||||
<!-- 请清晰简洁地描述如何实现此功能。-->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
**有没有额外的信息?**
|
||||
<!-- 如果您有额外的信息,请在此处说明。-->
|
17
.github/ISSUE_TEMPLATE/question-en.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/question-en.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Question
|
||||
about: Question of Xray.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Thanks for your support!
|
||||
1. Issue is **NOT** a proper place to ask a question, otherwise your issue may be closed.
|
||||
2. Please check existing Issues, Discussions and documentation in detail first. You may find the answers you want before you ask the question.
|
||||
3. If it is still not resolved, please give feedback in the our official Telegram group or Discussions.
|
||||
4. Wherever you ask a question, please *make sure* the content does not contain your private information.
|
||||
5. We highly recommend you to read https://github.com/tvvocold/How-To-Ask-Questions-The-Smart-Way.
|
||||
-->
|
||||
|
17
.github/ISSUE_TEMPLATE/question-zh-CN.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/question-zh-CN.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: 使用疑问
|
||||
about: 使用 Xray 时的疑问。
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- 感谢您的支持!
|
||||
1. Issue **不是**用来提问的, 否则将可能会被 close。
|
||||
2. 请先查询已有的 issue、discussion ,并且详细阅读文档的相关内容。那里可能有您需要的答案。
|
||||
3. 如果仍未解决,请在官方 Telegram 群中或 Discussion 区反馈。
|
||||
4. 无论在何处提问,请 *务必确保* 其不包含任何个人隐私信息。
|
||||
5. 我们强烈推荐您阅读 https://github.com/tvvocold/How-To-Ask-Questions-The-Smart-Way。
|
||||
-->
|
||||
|
33
.github/build/friendly-filenames.json
vendored
Normal file
33
.github/build/friendly-filenames.json
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"android-arm64": { "friendlyName": "android-arm64-v8a" },
|
||||
"darwin-amd64": { "friendlyName": "macos-64" },
|
||||
"darwin-arm64": { "friendlyName": "macos-arm64-v8a" },
|
||||
"dragonfly-amd64": { "friendlyName": "dragonfly-64" },
|
||||
"freebsd-386": { "friendlyName": "freebsd-32" },
|
||||
"freebsd-amd64": { "friendlyName": "freebsd-64" },
|
||||
"freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" },
|
||||
"freebsd-arm7": { "friendlyName": "freebsd-arm32-v7a" },
|
||||
"linux-386": { "friendlyName": "linux-32" },
|
||||
"linux-amd64": { "friendlyName": "linux-64" },
|
||||
"linux-arm5": { "friendlyName": "linux-arm32-v5" },
|
||||
"linux-arm64": { "friendlyName": "linux-arm64-v8a" },
|
||||
"linux-arm6": { "friendlyName": "linux-arm32-v6" },
|
||||
"linux-arm7": { "friendlyName": "linux-arm32-v7a" },
|
||||
"linux-mips64le": { "friendlyName": "linux-mips64le" },
|
||||
"linux-mips64": { "friendlyName": "linux-mips64" },
|
||||
"linux-mipslesoftfloat": { "friendlyName": "linux-mips32le-softfloat" },
|
||||
"linux-mipsle": { "friendlyName": "linux-mips32le" },
|
||||
"linux-mipssoftfloat": { "friendlyName": "linux-mips32-softfloat" },
|
||||
"linux-mips": { "friendlyName": "linux-mips32" },
|
||||
"linux-ppc64le": { "friendlyName": "linux-ppc64le" },
|
||||
"linux-ppc64": { "friendlyName": "linux-ppc64" },
|
||||
"linux-riscv64": { "friendlyName": "linux-riscv64" },
|
||||
"linux-s390x": { "friendlyName": "linux-s390x" },
|
||||
"openbsd-386": { "friendlyName": "openbsd-32" },
|
||||
"openbsd-amd64": { "friendlyName": "openbsd-64" },
|
||||
"openbsd-arm64": { "friendlyName": "openbsd-arm64-v8a" },
|
||||
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
|
||||
"windows-386": { "friendlyName": "windows-32" },
|
||||
"windows-amd64": { "friendlyName": "windows-64" },
|
||||
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
|
||||
}
|
204
.github/workflows/release.yml
vendored
Normal file
204
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
# Include amd64 on all platforms.
|
||||
goos: [windows, freebsd, openbsd, linux, dragonfly, darwin]
|
||||
goarch: [amd64, 386]
|
||||
exclude:
|
||||
# Exclude i386 on darwin and dragonfly.
|
||||
- goarch: 386
|
||||
goos: dragonfly
|
||||
- goarch: 386
|
||||
goos: darwin
|
||||
include:
|
||||
# BEIGIN MacOS ARM64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
# END MacOS ARM64
|
||||
# BEGIN Linux ARM 5 6 7
|
||||
- goos: linux
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
- goos: linux
|
||||
goarch: arm
|
||||
goarm: 6
|
||||
- goos: linux
|
||||
goarch: arm
|
||||
goarm: 5
|
||||
# END Linux ARM 5 6 7
|
||||
# BEGIN Android ARM 8
|
||||
- goos: android
|
||||
goarch: arm64
|
||||
# END Android ARM 8
|
||||
# Windows ARM 7
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
# BEGIN Other architectures
|
||||
# BEGIN riscv64 & ARM64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
- goos: linux
|
||||
goarch: riscv64
|
||||
# END riscv64 & ARM64
|
||||
# BEGIN MIPS
|
||||
- goos: linux
|
||||
goarch: mips64
|
||||
- goos: linux
|
||||
goarch: mips64le
|
||||
- goos: linux
|
||||
goarch: mipsle
|
||||
- goos: linux
|
||||
goarch: mips
|
||||
# END MIPS
|
||||
# BEGIN PPC
|
||||
- goos: linux
|
||||
goarch: ppc64
|
||||
- goos: linux
|
||||
goarch: ppc64le
|
||||
# END PPC
|
||||
# BEGIN FreeBSD ARM
|
||||
- goos: freebsd
|
||||
goarch: arm64
|
||||
- goos: freebsd
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
# END FreeBSD ARM
|
||||
# BEGIN S390X
|
||||
- goos: linux
|
||||
goarch: s390x
|
||||
# END S390X
|
||||
# END Other architectures
|
||||
# BEGIN OPENBSD ARM
|
||||
- goos: openbsd
|
||||
goarch: arm64
|
||||
- goos: openbsd
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
# END OPENBSD ARM
|
||||
fail-fast: false
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
CGO_ENABLED: 0
|
||||
steps:
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Show workflow information
|
||||
id: get_filename
|
||||
run: |
|
||||
export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json)
|
||||
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
|
||||
echo "::set-output name=ASSET_NAME::$_NAME"
|
||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.16
|
||||
|
||||
- name: Get project dependencies
|
||||
run: go mod download
|
||||
|
||||
- name: Replace Custom to Commit ID
|
||||
if: github.event_name != 'release'
|
||||
run: |
|
||||
ID=$(git rev-parse --short ${{ github.sha }})
|
||||
if [ "${{ github.event_name }}" == 'pull_request' ]
|
||||
then
|
||||
ID=$(git rev-parse --short ${{ github.event.pull_request.head.sha }})
|
||||
fi
|
||||
sed -i '/build/ s/Custom/'$ID'/' ./core/core.go
|
||||
|
||||
- name: Build Xray
|
||||
run: |
|
||||
mkdir -p build_assets
|
||||
go build -v -o build_assets/xray -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
|
||||
- name: Build Mips softfloat Xray
|
||||
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
|
||||
run: |
|
||||
GOMIPS=softfloat go build -v -o build_assets/xray_softfloat -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
|
||||
- name: Rename Windows Xray
|
||||
if: matrix.goos == 'windows'
|
||||
run: |
|
||||
cd ./build_assets || exit 1
|
||||
mv xray xray.exe
|
||||
|
||||
- name: Prepare to release
|
||||
run: |
|
||||
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
|
||||
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
|
||||
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
|
||||
for i in "${LIST[@]}"
|
||||
do
|
||||
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
|
||||
LASTEST_TAG="$(curl -sL "https://api.github.com/repos/v2fly/${INFO[0]}/releases" | jq -r ".[0].tag_name" || echo "latest")"
|
||||
FILE_NAME="${INFO[2]}.dat"
|
||||
echo -e "Downloading ${FILE_NAME}..."
|
||||
curl -L "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat" -o ./build_assets/${FILE_NAME}
|
||||
echo -e "Verifying HASH key..."
|
||||
HASH="$(curl -sL "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
|
||||
[ "$(sha256sum "./build_assets/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
|
||||
done
|
||||
|
||||
- name: Create ZIP archive
|
||||
shell: bash
|
||||
run: |
|
||||
pushd build_assets || exit 1
|
||||
touch -mt $(date +%Y01010000) *
|
||||
zip -9vr ../Xray-$ASSET_NAME.zip .
|
||||
popd || exit 1
|
||||
FILE=./Xray-$ASSET_NAME.zip
|
||||
DGST=$FILE.dgst
|
||||
for METHOD in {"md5","sha1","sha256","sha512"}
|
||||
do
|
||||
openssl dgst -$METHOD $FILE | sed 's/([^)]*)//g' >>$DGST
|
||||
done
|
||||
|
||||
- name: Change the name
|
||||
run: |
|
||||
mv build_assets Xray-$ASSET_NAME
|
||||
|
||||
- name: Upload files to Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Xray-${{ steps.get_filename.outputs.ASSET_NAME }}
|
||||
path: |
|
||||
./Xray-${{ steps.get_filename.outputs.ASSET_NAME }}/*
|
||||
|
||||
- name: Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ./Xray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip*
|
||||
tag: ${{ github.ref }}
|
||||
file_glob: true
|
48
.github/workflows/test.yml
vendored
Normal file
48
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-latest, ubuntu-latest, macos-latest]
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.16
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Prepare geo*dat
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
run: |
|
||||
mkdir resources
|
||||
wget -O ./resources/geoip.dat https://github.com/v2fly/geoip/releases/latest/download/geoip.dat
|
||||
wget -O ./resources/geosite.dat https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat
|
||||
- name: Prepare geo*dat for Windows
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
run: |
|
||||
mkdir resources
|
||||
Invoke-WebRequest -Uri "https://github.com/v2fly/geoip/releases/latest/download/geoip.dat" -OutFile "./resources/geoip.dat"
|
||||
Invoke-WebRequest -Uri "https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat" -OutFile "./resources/geosite.dat"
|
||||
- name: Test
|
||||
run: go test -timeout 1h -v ./...
|
11
README.md
11
README.md
@@ -17,9 +17,14 @@
|
||||
- One Click
|
||||
- [ProxySU](https://github.com/proxysu/ProxySU)
|
||||
- [v2ray-agent](https://github.com/mack-a/v2ray-agent)
|
||||
- [Xray-yes](https://github.com/jiuqi9997/Xray-yes)
|
||||
- [Xray_onekey](https://github.com/wulabing/Xray_onekey)
|
||||
- Magisk
|
||||
- [Xray4Magisk](https://github.com/CerteKim/Xray4Magisk)
|
||||
- [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk)
|
||||
- Homebrew
|
||||
- [Repository 0](https://github.com/N4FA/homebrew-xray)
|
||||
- [Repository 1](https://github.com/xiruizhao/homebrew-xray)
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -29,10 +34,16 @@
|
||||
|
||||
- OpenWrt
|
||||
- [PassWall](https://github.com/xiaorouji/openwrt-passwall)
|
||||
- [Hello World](https://github.com/jerrykuku/luci-app-vssr)
|
||||
- [ShadowSocksR Plus+](https://github.com/fw876/helloworld)
|
||||
- [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
|
||||
- Windows
|
||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||
- [Qv2ray](https://github.com/Qv2ray/Qv2ray)
|
||||
- [Netch (NetFilter & TUN/TAP)](https://github.com/NetchX/Netch)
|
||||
- Android
|
||||
- [v2rayNG](https://github.com/2dust/v2rayNG)
|
||||
- [Kitsunebi](https://github.com/rurirei/Kitsunebi/tree/release_xtls)
|
||||
- iOS / Mac
|
||||
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
|
||||
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/signal/done"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
)
|
||||
@@ -79,7 +80,7 @@ func (co *Outbound) Dispatch(ctx context.Context, link *transport.Link) {
|
||||
}
|
||||
|
||||
closeSignal := done.New()
|
||||
c := net.NewConnection(net.ConnectionInputMulti(link.Writer), net.ConnectionOutputMulti(link.Reader), net.ConnectionOnClose(closeSignal))
|
||||
c := cnc.NewConnection(cnc.ConnectionInputMulti(link.Writer), cnc.ConnectionOutputMulti(link.Reader), cnc.ConnectionOnClose(closeSignal))
|
||||
co.listener.add(c)
|
||||
co.access.RUnlock()
|
||||
<-closeSignal.Wait()
|
||||
|
@@ -175,12 +175,21 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
|
||||
return inboundLink, outboundLink
|
||||
}
|
||||
|
||||
func shouldOverride(result SniffResult, domainOverride []string) bool {
|
||||
for _, p := range domainOverride {
|
||||
if strings.HasPrefix(result.Protocol(), p) {
|
||||
func shouldOverride(result SniffResult, request session.SniffingRequest) bool {
|
||||
domain := result.Domain()
|
||||
for _, d := range request.ExcludeForDomain {
|
||||
if domain == d {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
protocol := result.Protocol()
|
||||
for _, p := range request.OverrideDestinationForProtocol {
|
||||
if strings.HasPrefix(protocol, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -213,7 +222,7 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
||||
if err == nil {
|
||||
content.Protocol = result.Protocol()
|
||||
}
|
||||
if err == nil && shouldOverride(result, sniffingRequest.OverrideDestinationForProtocol) {
|
||||
if err == nil && shouldOverride(result, sniffingRequest) {
|
||||
domain := result.Domain()
|
||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
||||
destination.Address = net.ParseAddress(domain)
|
||||
@@ -263,14 +272,18 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
||||
skipRoutePick = content.SkipRoutePick
|
||||
}
|
||||
|
||||
routingLink := routing_session.AsRoutingContext(ctx)
|
||||
inTag := routingLink.GetInboundTag()
|
||||
isPickRoute := false
|
||||
if d.router != nil && !skipRoutePick {
|
||||
if route, err := d.router.PickRoute(routing_session.AsRoutingContext(ctx)); err == nil {
|
||||
tag := route.GetOutboundTag()
|
||||
if h := d.ohm.GetHandler(tag); h != nil {
|
||||
newError("taking detour [", tag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
|
||||
if route, err := d.router.PickRoute(routingLink); err == nil {
|
||||
outTag := route.GetOutboundTag()
|
||||
isPickRoute = true
|
||||
if h := d.ohm.GetHandler(outTag); h != nil {
|
||||
newError("taking detour [", outTag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
|
||||
handler = h
|
||||
} else {
|
||||
newError("non existing tag: ", tag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
||||
newError("non existing outTag: ", outTag).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
||||
}
|
||||
} else {
|
||||
newError("default route for ", destination).WriteToLog(session.ExportIDToError(ctx))
|
||||
@@ -290,7 +303,19 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
||||
|
||||
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
|
||||
if tag := handler.Tag(); tag != "" {
|
||||
accessMessage.Detour = tag
|
||||
if isPickRoute {
|
||||
if inTag != "" {
|
||||
accessMessage.Detour = inTag + " -> " + tag
|
||||
} else {
|
||||
accessMessage.Detour = tag
|
||||
}
|
||||
} else {
|
||||
if inTag != "" {
|
||||
accessMessage.Detour = inTag + " >> " + tag
|
||||
} else {
|
||||
accessMessage.Detour = tag
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Record(accessMessage)
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/protocol/dns"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/signal/pubsub"
|
||||
@@ -28,6 +29,7 @@ import (
|
||||
// which is compatible with traditional dns over udp(RFC1035),
|
||||
// thus most of the DOH implementation is copied from udpns.go
|
||||
type DoHNameServer struct {
|
||||
dispatcher routing.Dispatcher
|
||||
sync.RWMutex
|
||||
ips map[string]record
|
||||
pub *pubsub.Service
|
||||
@@ -44,40 +46,8 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, clientIP net.
|
||||
newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog()
|
||||
s := baseDOHNameServer(url, "DOH", clientIP)
|
||||
|
||||
// Dispatched connection will be closed (interrupted) after each request
|
||||
// This makes DOH inefficient without a keep-alived connection
|
||||
// See: core/app/proxyman/outbound/handler.go:113
|
||||
// Using mux (https request wrapped in a stream layer) improves the situation.
|
||||
// Recommend to use NewDoHLocalNameServer (DOHL:) if xray instance is running on
|
||||
// a normal network eg. the server side of xray
|
||||
tr := &http.Transport{
|
||||
MaxIdleConns: 30,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 30 * time.Second,
|
||||
ForceAttemptHTTP2: true,
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dest, err := net.ParseDestination(network + ":" + addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.dispatcher = dispatcher
|
||||
|
||||
link, err := dispatcher.Dispatch(ctx, dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return net.NewConnection(
|
||||
net.ConnectionInputMulti(link.Writer),
|
||||
net.ConnectionOutputMulti(link.Reader),
|
||||
), nil
|
||||
},
|
||||
}
|
||||
|
||||
dispatchedClient := &http.Client{
|
||||
Transport: tr,
|
||||
Timeout: 60 * time.Second,
|
||||
}
|
||||
|
||||
s.httpClient = dispatchedClient
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@@ -210,6 +180,11 @@ func (s *DoHNameServer) newReqID() uint16 {
|
||||
func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option IPOption) {
|
||||
newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx))
|
||||
|
||||
if s.name+"." == "DOH//"+domain {
|
||||
newError(s.name, " tries to resolve itself! Use IP or set \"hosts\" instead.").AtError().WriteToLog(session.ExportIDToError(ctx))
|
||||
return
|
||||
}
|
||||
|
||||
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP))
|
||||
|
||||
var deadline time.Time
|
||||
@@ -231,12 +206,12 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option IPO
|
||||
}
|
||||
|
||||
dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
|
||||
Protocol: "https",
|
||||
SkipRoutePick: true,
|
||||
Protocol: "https",
|
||||
//SkipRoutePick: true,
|
||||
})
|
||||
|
||||
// forced to use mux for DOH
|
||||
dnsCtx = session.ContextWithMuxPrefered(dnsCtx, true)
|
||||
// dnsCtx = session.ContextWithMuxPrefered(dnsCtx, true)
|
||||
|
||||
var cancel context.CancelFunc
|
||||
dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline)
|
||||
@@ -244,17 +219,17 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option IPO
|
||||
|
||||
b, err := dns.PackMessage(r.msg)
|
||||
if err != nil {
|
||||
newError("failed to pack dns query").Base(err).AtError().WriteToLog()
|
||||
newError("failed to pack dns query for ", domain).Base(err).AtError().WriteToLog()
|
||||
return
|
||||
}
|
||||
resp, err := s.dohHTTPSContext(dnsCtx, b.Bytes())
|
||||
if err != nil {
|
||||
newError("failed to retrieve response").Base(err).AtError().WriteToLog()
|
||||
newError("failed to retrieve response for ", domain).Base(err).AtError().WriteToLog()
|
||||
return
|
||||
}
|
||||
rec, err := parseResponse(resp)
|
||||
if err != nil {
|
||||
newError("failed to handle DOH response").Base(err).AtError().WriteToLog()
|
||||
newError("failed to handle DOH response for ", domain).Base(err).AtError().WriteToLog()
|
||||
return
|
||||
}
|
||||
s.updateIP(r, rec)
|
||||
@@ -272,7 +247,44 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
|
||||
req.Header.Add("Accept", "application/dns-message")
|
||||
req.Header.Add("Content-Type", "application/dns-message")
|
||||
|
||||
resp, err := s.httpClient.Do(req.WithContext(ctx))
|
||||
hc := s.httpClient
|
||||
|
||||
// Dispatched connection will be closed (interrupted) after each request
|
||||
// This makes DOH inefficient without a keep-alived connection
|
||||
// See: core/app/proxyman/outbound/handler.go:113
|
||||
// Using mux (https request wrapped in a stream layer) improves the situation.
|
||||
// Recommend to use NewDoHLocalNameServer (DOHL:) if xray instance is running on
|
||||
// a normal network eg. the server side of xray
|
||||
|
||||
if s.dispatcher != nil {
|
||||
tr := &http.Transport{
|
||||
MaxIdleConns: 30,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 30 * time.Second,
|
||||
ForceAttemptHTTP2: true,
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dest, err := net.ParseDestination(network + ":" + addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
link, err := s.dispatcher.Dispatch(ctx, dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cnc.NewConnection(
|
||||
cnc.ConnectionInputMulti(link.Writer),
|
||||
cnc.ConnectionOutputMulti(link.Reader),
|
||||
), nil
|
||||
},
|
||||
}
|
||||
hc = &http.Client{
|
||||
Timeout: time.Second * 180,
|
||||
Transport: tr,
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := hc.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -364,6 +364,7 @@ func (s *Server) lookupIPInternal(domain string, option IPOption) ([]net.IP, err
|
||||
if domain == "" {
|
||||
return nil, newError("empty domain name")
|
||||
}
|
||||
domain = strings.ToLower(domain)
|
||||
|
||||
// normalize the FQDN form query
|
||||
if domain[len(domain)-1] == '.' {
|
||||
|
@@ -101,8 +101,8 @@ func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||
rr, _ := dns.NewRR("localhost-b. IN A 127.0.0.4")
|
||||
ans.Answer = append(ans.Answer, rr)
|
||||
|
||||
case q.Name == "Mijia\\ Cloud." && q.Qtype == dns.TypeA:
|
||||
rr, _ := dns.NewRR("Mijia\\ Cloud. IN A 127.0.0.1")
|
||||
case q.Name == "mijia\\ cloud." && q.Qtype == dns.TypeA:
|
||||
rr, _ := dns.NewRR("mijia\\ cloud. IN A 127.0.0.1")
|
||||
ans.Answer = append(ans.Answer, rr)
|
||||
}
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ func (s *service) Register(server *grpc.Server) {
|
||||
RegisterLoggerServiceServer(server, ls)
|
||||
|
||||
// For compatibility purposes
|
||||
vCoreDesc := _LoggerService_serviceDesc
|
||||
vCoreDesc := LoggerService_ServiceDesc
|
||||
vCoreDesc.ServiceName = "v2ray.core.app.log.command.LoggerService"
|
||||
server.RegisterService(&vCoreDesc, ls)
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// LoggerServiceClient is the client API for LoggerService service.
|
||||
@@ -62,7 +63,7 @@ type UnsafeLoggerServiceServer interface {
|
||||
}
|
||||
|
||||
func RegisterLoggerServiceServer(s grpc.ServiceRegistrar, srv LoggerServiceServer) {
|
||||
s.RegisterService(&_LoggerService_serviceDesc, srv)
|
||||
s.RegisterService(&LoggerService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _LoggerService_RestartLogger_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
@@ -83,7 +84,10 @@ func _LoggerService_RestartLogger_Handler(srv interface{}, ctx context.Context,
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _LoggerService_serviceDesc = grpc.ServiceDesc{
|
||||
// LoggerService_ServiceDesc is the grpc.ServiceDesc for LoggerService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var LoggerService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "xray.app.log.command.LoggerService",
|
||||
HandlerType: (*LoggerServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
|
@@ -140,7 +140,7 @@ func (s *service) Register(server *grpc.Server) {
|
||||
RegisterHandlerServiceServer(server, hs)
|
||||
|
||||
// For compatibility purposes
|
||||
vCoreDesc := _HandlerService_serviceDesc
|
||||
vCoreDesc := HandlerService_ServiceDesc
|
||||
vCoreDesc.ServiceName = "v2ray.core.app.proxyman.command.HandlerService"
|
||||
server.RegisterService(&vCoreDesc, hs)
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// HandlerServiceClient is the client API for HandlerService service.
|
||||
@@ -132,7 +133,7 @@ type UnsafeHandlerServiceServer interface {
|
||||
}
|
||||
|
||||
func RegisterHandlerServiceServer(s grpc.ServiceRegistrar, srv HandlerServiceServer) {
|
||||
s.RegisterService(&_HandlerService_serviceDesc, srv)
|
||||
s.RegisterService(&HandlerService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _HandlerService_AddInbound_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
@@ -243,7 +244,10 @@ func _HandlerService_AlterOutbound_Handler(srv interface{}, ctx context.Context,
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _HandlerService_serviceDesc = grpc.ServiceDesc{
|
||||
// HandlerService_ServiceDesc is the grpc.ServiceDesc for HandlerService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var HandlerService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "xray.app.proxyman.command.HandlerService",
|
||||
HandlerType: (*HandlerServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
|
@@ -241,6 +241,7 @@ type SniffingConfig struct {
|
||||
// Override target destination if sniff'ed protocol is in the given list.
|
||||
// Supported values are "http", "tls".
|
||||
DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
|
||||
DomainsExcluded []string `protobuf:"bytes,3,rep,name=domains_excluded,json=domainsExcluded,proto3" json:"domains_excluded,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SniffingConfig) Reset() {
|
||||
@@ -289,6 +290,13 @@ func (x *SniffingConfig) GetDestinationOverride() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SniffingConfig) GetDomainsExcluded() []string {
|
||||
if x != nil {
|
||||
return x.DomainsExcluded
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ReceiverConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -756,92 +764,95 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
|
||||
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
|
||||
0x0a, 0x0a, 0x06, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x52,
|
||||
0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x78, 0x74, 0x65, 0x72,
|
||||
0x6e, 0x61, 0x6c, 0x10, 0x02, 0x22, 0x5d, 0x0a, 0x0e, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e,
|
||||
0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
|
||||
0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
|
||||
0x64, 0x12, 0x31, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||
0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72,
|
||||
0x72, 0x69, 0x64, 0x65, 0x22, 0x90, 0x04, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
|
||||
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x5f,
|
||||
0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f,
|
||||
0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e,
|
||||
0x67, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||
0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52,
|
||||
0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x56, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x63,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x12, 0x61, 0x6c, 0x6c,
|
||||
0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12,
|
||||
0x4e, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e,
|
||||
0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x65, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
|
||||
0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12,
|
||||
0x40, 0x0a, 0x1c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69,
|
||||
0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,
|
||||
0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4f, 0x72,
|
||||
0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x4e, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72,
|
||||
0x72, 0x69, 0x64, 0x65, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4b,
|
||||
0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x42, 0x02, 0x18,
|
||||
0x01, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
|
||||
0x65, 0x12, 0x4e, 0x0a, 0x11, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65,
|
||||
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e,
|
||||
0x2e, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
|
||||
0x10, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
|
||||
0x73, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x62, 0x6f,
|
||||
0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 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, 0x4d, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x73,
|
||||
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69,
|
||||
0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52,
|
||||
0x10, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
|
||||
0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
|
||||
0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||
0x6e, 0x61, 0x6c, 0x10, 0x02, 0x22, 0x88, 0x01, 0x0a, 0x0e, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x69,
|
||||
0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
|
||||
0x65, 0x64, 0x12, 0x31, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
|
||||
0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65,
|
||||
0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73,
|
||||
0x5f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||
0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64,
|
||||
0x22, 0x90, 0x04, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67,
|
||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x61,
|
||||
0x6e, 0x67, 0x65, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x33,
|
||||
0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b,
|
||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74,
|
||||
0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x6c, 0x69, 0x73,
|
||||
0x74, 0x65, 0x6e, 0x12, 0x56, 0x0a, 0x13, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53,
|
||||
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x4e, 0x0a, 0x0f, 0x73,
|
||||
0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53,
|
||||
0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74, 0x72,
|
||||
0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x72,
|
||||
0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f,
|
||||
0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e,
|
||||
0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a,
|
||||
0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65,
|
||||
0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
||||
0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e,
|
||||
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0e, 0x64,
|
||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x4e, 0x0a,
|
||||
0x11, 0x73, 0x6e, 0x69, 0x66, 0x66, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e,
|
||||
0x67, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x53, 0x6e, 0x69,
|
||||
0x66, 0x66, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x73, 0x6e, 0x69,
|
||||
0x66, 0x66, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x4a, 0x04, 0x08,
|
||||
0x06, 0x10, 0x07, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x48,
|
||||
0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 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, 0x4d,
|
||||
0x0a, 0x11, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
|
||||
0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54,
|
||||
0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f,
|
||||
0x78, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x75,
|
||||
0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xb0, 0x02, 0x0a,
|
||||
0x0c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a,
|
||||
0x03, 0x76, 0x69, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f,
|
||||
0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x03, 0x76, 0x69, 0x61, 0x12, 0x4e, 0x0a, 0x0f,
|
||||
0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
||||
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74,
|
||||
0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4b, 0x0a, 0x0e,
|
||||
0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x50,
|
||||
0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x6d, 0x75, 0x6c,
|
||||
0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18,
|
||||
0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70,
|
||||
0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75,
|
||||
0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22,
|
||||
0x50, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12,
|
||||
0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63,
|
||||
0x79, 0x2a, 0x23, 0x0a, 0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
||||
0x6f, 0x6c, 0x73, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a,
|
||||
0x03, 0x54, 0x4c, 0x53, 0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
|
||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50,
|
||||
0x01, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74,
|
||||
0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70,
|
||||
0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x10, 0x72, 0x65, 0x63,
|
||||
0x65, 0x69, 0x76, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47, 0x0a,
|
||||
0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64,
|
||||
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65,
|
||||
0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75,
|
||||
0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xb0, 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x6e,
|
||||
0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x03, 0x76, 0x69, 0x61,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
|
||||
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d,
|
||||
0x61, 0x69, 0x6e, 0x52, 0x03, 0x76, 0x69, 0x61, 0x12, 0x4e, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65,
|
||||
0x61, 0x6d, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x74, 0x72, 0x65,
|
||||
0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d,
|
||||
0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79,
|
||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x74,
|
||||
0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c,
|
||||
0x65, 0x78, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69,
|
||||
0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70,
|
||||
0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x50, 0x0a, 0x12, 0x4d,
|
||||
0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63,
|
||||
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
|
||||
0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x2a, 0x23, 0x0a,
|
||||
0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x12,
|
||||
0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x4c, 0x53,
|
||||
0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
|
||||
0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x26, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78,
|
||||
0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f,
|
||||
0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70,
|
||||
0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -56,6 +56,7 @@ message SniffingConfig {
|
||||
// Override target destination if sniff'ed protocol is in the given list.
|
||||
// Supported values are "http", "tls".
|
||||
repeated string destination_override = 2;
|
||||
repeated string domains_excluded = 3;
|
||||
}
|
||||
|
||||
message ReceiverConfig {
|
||||
|
@@ -136,6 +136,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
|
||||
uplinkCounter: uplinkCounter,
|
||||
downlinkCounter: downlinkCounter,
|
||||
stream: mss,
|
||||
ctx: ctx,
|
||||
}
|
||||
h.workers = append(h.workers, worker)
|
||||
}
|
||||
|
@@ -156,6 +156,7 @@ func (h *DynamicInboundHandler) refresh() error {
|
||||
uplinkCounter: uplinkCounter,
|
||||
downlinkCounter: downlinkCounter,
|
||||
stream: h.streamSettings,
|
||||
ctx: h.ctx,
|
||||
}
|
||||
if err := worker.Start(); err != nil {
|
||||
newError("failed to create UDP worker").Base(err).AtWarning().WriteToLog()
|
||||
|
@@ -42,6 +42,9 @@ func (m *Manager) AddHandler(ctx context.Context, handler inbound.Handler) error
|
||||
|
||||
tag := handler.Tag()
|
||||
if len(tag) > 0 {
|
||||
if _, found := m.taggedHandlers[tag]; found {
|
||||
return newError("existing tag found: " + tag)
|
||||
}
|
||||
m.taggedHandlers[tag] = handler
|
||||
} else {
|
||||
m.untaggedHandler = append(m.untaggedHandler, handler)
|
||||
|
@@ -97,6 +97,7 @@ func (w *tcpWorker) callback(conn internet.Connection) {
|
||||
if w.sniffingConfig != nil {
|
||||
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
|
||||
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
|
||||
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
|
||||
}
|
||||
ctx = session.ContextWithContent(ctx, content)
|
||||
|
||||
@@ -239,6 +240,9 @@ type udpWorker struct {
|
||||
|
||||
checker *task.Periodic
|
||||
activeConn map[connID]*udpConn
|
||||
|
||||
ctx context.Context
|
||||
cone bool
|
||||
}
|
||||
|
||||
func (w *udpWorker) getConnection(id connID) (*udpConn, bool) {
|
||||
@@ -279,7 +283,10 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
|
||||
src: source,
|
||||
}
|
||||
if originalDest.IsValid() {
|
||||
id.dest = originalDest
|
||||
if !w.cone {
|
||||
id.dest = originalDest
|
||||
}
|
||||
b.UDP = &originalDest
|
||||
}
|
||||
conn, existing := w.getConnection(id)
|
||||
|
||||
@@ -336,7 +343,7 @@ func (w *udpWorker) clean() error {
|
||||
}
|
||||
|
||||
for addr, conn := range w.activeConn {
|
||||
if nowSec-atomic.LoadInt64(&conn.lastActivityTime) > 8 { // TODO Timeout too small
|
||||
if nowSec-atomic.LoadInt64(&conn.lastActivityTime) > 300 {
|
||||
delete(w.activeConn, addr)
|
||||
conn.Close()
|
||||
}
|
||||
@@ -357,8 +364,10 @@ func (w *udpWorker) Start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
w.cone = w.ctx.Value("cone").(bool)
|
||||
|
||||
w.checker = &task.Periodic{
|
||||
Interval: time.Second * 16,
|
||||
Interval: time.Minute,
|
||||
Execute: w.clean,
|
||||
}
|
||||
|
||||
@@ -441,6 +450,7 @@ func (w *dsWorker) callback(conn internet.Connection) {
|
||||
if w.sniffingConfig != nil {
|
||||
content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
|
||||
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
|
||||
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
|
||||
}
|
||||
ctx = session.ContextWithContent(ctx, content)
|
||||
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/mux"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
@@ -173,7 +174,7 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn
|
||||
downlinkReader, downlinkWriter := pipe.New(opts...)
|
||||
|
||||
go handler.Dispatch(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter})
|
||||
conn := net.NewConnection(net.ConnectionInputMulti(uplinkWriter), net.ConnectionOutputMulti(downlinkReader))
|
||||
conn := cnc.NewConnection(cnc.ConnectionInputMulti(uplinkWriter), cnc.ConnectionOutputMulti(downlinkReader))
|
||||
|
||||
if config := tls.ConfigFromStreamSettings(h.streamSettings); config != nil {
|
||||
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
|
||||
|
@@ -109,6 +109,9 @@ func (m *Manager) AddHandler(ctx context.Context, handler outbound.Handler) erro
|
||||
|
||||
tag := handler.Tag()
|
||||
if len(tag) > 0 {
|
||||
if _, found := m.taggedHandler[tag]; found {
|
||||
return newError("existing tag found: " + tag)
|
||||
}
|
||||
m.taggedHandler[tag] = handler
|
||||
} else {
|
||||
m.untaggedHandlers = append(m.untaggedHandlers, handler)
|
||||
|
@@ -157,6 +157,9 @@ func (p *StaticMuxPicker) PickAvailable() (*mux.ClientWorker, error) {
|
||||
if w.draining {
|
||||
continue
|
||||
}
|
||||
if w.client.Closed() {
|
||||
continue
|
||||
}
|
||||
if w.client.ActiveConnections() < minConn {
|
||||
minConn = w.client.ActiveConnections()
|
||||
minIdx = i
|
||||
|
@@ -85,7 +85,7 @@ func (s *service) Register(server *grpc.Server) {
|
||||
RegisterRoutingServiceServer(server, rs)
|
||||
|
||||
// For compatibility purposes
|
||||
vCoreDesc := _RoutingService_serviceDesc
|
||||
vCoreDesc := RoutingService_ServiceDesc
|
||||
vCoreDesc.ServiceName = "v2ray.core.app.router.command.RoutingService"
|
||||
server.RegisterService(&vCoreDesc, rs)
|
||||
}))
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// RoutingServiceClient is the client API for RoutingService service.
|
||||
@@ -30,7 +31,7 @@ func NewRoutingServiceClient(cc grpc.ClientConnInterface) RoutingServiceClient {
|
||||
}
|
||||
|
||||
func (c *routingServiceClient) SubscribeRoutingStats(ctx context.Context, in *SubscribeRoutingStatsRequest, opts ...grpc.CallOption) (RoutingService_SubscribeRoutingStatsClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_RoutingService_serviceDesc.Streams[0], "/xray.app.router.command.RoutingService/SubscribeRoutingStats", opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &RoutingService_ServiceDesc.Streams[0], "/xray.app.router.command.RoutingService/SubscribeRoutingStats", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -99,7 +100,7 @@ type UnsafeRoutingServiceServer interface {
|
||||
}
|
||||
|
||||
func RegisterRoutingServiceServer(s grpc.ServiceRegistrar, srv RoutingServiceServer) {
|
||||
s.RegisterService(&_RoutingService_serviceDesc, srv)
|
||||
s.RegisterService(&RoutingService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _RoutingService_SubscribeRoutingStats_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
@@ -141,7 +142,10 @@ func _RoutingService_TestRoute_Handler(srv interface{}, ctx context.Context, dec
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _RoutingService_serviceDesc = grpc.ServiceDesc{
|
||||
// RoutingService_ServiceDesc is the grpc.ServiceDesc for RoutingService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var RoutingService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "xray.app.router.command.RoutingService",
|
||||
HandlerType: (*RoutingServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
|
@@ -91,7 +91,7 @@ func (m *DomainMatcher) Apply(ctx routing.Context) bool {
|
||||
if len(domain) == 0 {
|
||||
return false
|
||||
}
|
||||
return m.ApplyDomain(domain)
|
||||
return m.ApplyDomain(strings.ToLower(domain))
|
||||
}
|
||||
|
||||
type MultiGeoIPMatcher struct {
|
||||
|
@@ -18,10 +18,10 @@ func init() {
|
||||
common.Must(err)
|
||||
|
||||
if _, err := os.Stat(platform.GetAssetLocation("geoip.dat")); err != nil && os.IsNotExist(err) {
|
||||
common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "release", "config", "geoip.dat")))
|
||||
common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "resources", "geoip.dat")))
|
||||
}
|
||||
if _, err := os.Stat(platform.GetAssetLocation("geosite.dat")); err != nil && os.IsNotExist(err) {
|
||||
common.Must(filesystem.CopyFile(platform.GetAssetLocation("geosite.dat"), filepath.Join(wd, "..", "..", "release", "config", "geosite.dat")))
|
||||
common.Must(filesystem.CopyFile(platform.GetAssetLocation("geosite.dat"), filepath.Join(wd, "..", "..", "resources", "geosite.dat")))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -113,7 +113,7 @@ func (s *service) Register(server *grpc.Server) {
|
||||
RegisterStatsServiceServer(server, ss)
|
||||
|
||||
// For compatibility purposes
|
||||
vCoreDesc := _StatsService_serviceDesc
|
||||
vCoreDesc := StatsService_ServiceDesc
|
||||
vCoreDesc.ServiceName = "v2ray.core.app.stats.command.StatsService"
|
||||
server.RegisterService(&vCoreDesc, ss)
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// StatsServiceClient is the client API for StatsService service.
|
||||
@@ -90,7 +91,7 @@ type UnsafeStatsServiceServer interface {
|
||||
}
|
||||
|
||||
func RegisterStatsServiceServer(s grpc.ServiceRegistrar, srv StatsServiceServer) {
|
||||
s.RegisterService(&_StatsService_serviceDesc, srv)
|
||||
s.RegisterService(&StatsService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _StatsService_GetStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
@@ -147,7 +148,10 @@ func _StatsService_GetSysStats_Handler(srv interface{}, ctx context.Context, dec
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _StatsService_serviceDesc = grpc.ServiceDesc{
|
||||
// StatsService_ServiceDesc is the grpc.ServiceDesc for StatsService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var StatsService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "xray.app.stats.command.StatsService",
|
||||
HandlerType: (*StatsServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/xtls/xray-core/common/bytespool"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -20,6 +21,7 @@ type Buffer struct {
|
||||
v []byte
|
||||
start int32
|
||||
end int32
|
||||
UDP *net.Destination
|
||||
}
|
||||
|
||||
// New creates a Buffer with 0 length and 2K capacity.
|
||||
@@ -47,6 +49,7 @@ func (b *Buffer) Release() {
|
||||
b.v = nil
|
||||
b.Clear()
|
||||
pool.Put(p)
|
||||
b.UDP = nil
|
||||
}
|
||||
|
||||
// Clear clears the content of the buffer, results an empty buffer with
|
||||
@@ -107,6 +110,9 @@ func (b *Buffer) BytesTo(to int32) []byte {
|
||||
if to < 0 {
|
||||
to += b.Len()
|
||||
}
|
||||
if to < 0 {
|
||||
to = 0
|
||||
}
|
||||
return b.v[b.start : b.start+to]
|
||||
}
|
||||
|
||||
|
@@ -36,19 +36,23 @@ func (m *AccessMessage) String() string {
|
||||
builder.WriteString(string(m.Status))
|
||||
builder.WriteByte(' ')
|
||||
builder.WriteString(serial.ToString(m.To))
|
||||
builder.WriteByte(' ')
|
||||
|
||||
if len(m.Detour) > 0 {
|
||||
builder.WriteByte('[')
|
||||
builder.WriteString(" [")
|
||||
builder.WriteString(m.Detour)
|
||||
builder.WriteString("] ")
|
||||
builder.WriteByte(']')
|
||||
}
|
||||
|
||||
if reason := serial.ToString(m.Reason); len(reason) > 0 {
|
||||
builder.WriteString(" ")
|
||||
builder.WriteString(reason)
|
||||
}
|
||||
builder.WriteString(serial.ToString(m.Reason))
|
||||
|
||||
if len(m.Email) > 0 {
|
||||
builder.WriteString("email:")
|
||||
builder.WriteString(" email: ")
|
||||
builder.WriteString(m.Email)
|
||||
builder.WriteByte(' ')
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
|
@@ -330,7 +330,7 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
}
|
||||
|
||||
rr := s.NewReader(reader)
|
||||
rr := s.NewReader(reader, &meta.Target)
|
||||
err := buf.Copy(rr, s.output)
|
||||
if err != nil && buf.IsWriteError(err) {
|
||||
newError("failed to write to downstream. closing session ", s.ID).Base(err).WriteToLog()
|
||||
|
@@ -81,6 +81,9 @@ func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||
if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if b.UDP != nil {
|
||||
b.WriteByte(byte(TargetNetworkUDP))
|
||||
addrParser.WriteAddressPort(b, b.UDP.Address, b.UDP.Port)
|
||||
}
|
||||
|
||||
len1 := b.Len()
|
||||
@@ -119,7 +122,7 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
|
||||
f.Option = bitmask.Byte(b.Byte(3))
|
||||
f.Target.Network = net.Network_Unknown
|
||||
|
||||
if f.SessionStatus == SessionStatusNew {
|
||||
if f.SessionStatus == SessionStatusNew || (f.SessionStatus == SessionStatusKeep && b.Len() != 4) {
|
||||
if b.Len() < 8 {
|
||||
return newError("insufficient buffer: ", b.Len())
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
)
|
||||
|
||||
@@ -12,13 +13,15 @@ import (
|
||||
type PacketReader struct {
|
||||
reader io.Reader
|
||||
eof bool
|
||||
dest *net.Destination
|
||||
}
|
||||
|
||||
// NewPacketReader creates a new PacketReader.
|
||||
func NewPacketReader(reader io.Reader) *PacketReader {
|
||||
func NewPacketReader(reader io.Reader, dest *net.Destination) *PacketReader {
|
||||
return &PacketReader{
|
||||
reader: reader,
|
||||
eof: false,
|
||||
dest: dest,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +46,9 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
return nil, err
|
||||
}
|
||||
r.eof = true
|
||||
if r.dest != nil && r.dest.Network == net.Network_UDP {
|
||||
b.UDP = r.dest
|
||||
}
|
||||
return buf.MultiBuffer{b}, nil
|
||||
}
|
||||
|
||||
|
@@ -145,7 +145,7 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
|
||||
return nil
|
||||
}
|
||||
|
||||
rr := s.NewReader(reader)
|
||||
rr := s.NewReader(reader, &meta.Target)
|
||||
if err := buf.Copy(rr, s.output); err != nil {
|
||||
buf.Copy(rr, buf.Discard)
|
||||
common.Interrupt(s.input)
|
||||
@@ -168,7 +168,7 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
}
|
||||
|
||||
rr := s.NewReader(reader)
|
||||
rr := s.NewReader(reader, &meta.Target)
|
||||
err := buf.Copy(rr, s.output)
|
||||
|
||||
if err != nil && buf.IsWriteError(err) {
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
)
|
||||
|
||||
@@ -152,9 +153,9 @@ func (s *Session) Close() error {
|
||||
}
|
||||
|
||||
// NewReader creates a buf.Reader based on the transfer type of this Session.
|
||||
func (s *Session) NewReader(reader *buf.BufferedReader) buf.Reader {
|
||||
func (s *Session) NewReader(reader *buf.BufferedReader, dest *net.Destination) buf.Reader {
|
||||
if s.transferType == protocol.TransferTypeStream {
|
||||
return NewStreamReader(reader)
|
||||
}
|
||||
return NewPacketReader(reader)
|
||||
return NewPacketReader(reader, dest)
|
||||
}
|
||||
|
@@ -63,6 +63,9 @@ func (w *Writer) writeMetaOnly() error {
|
||||
|
||||
func writeMetaWithFrame(writer buf.Writer, meta FrameMetadata, data buf.MultiBuffer) error {
|
||||
frame := buf.New()
|
||||
if len(data) == 1 {
|
||||
frame.UDP = data[0].UDP
|
||||
}
|
||||
if err := meta.WriteTo(frame); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -1,12 +1,12 @@
|
||||
package net
|
||||
package cnc
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/signal/done"
|
||||
)
|
||||
|
||||
@@ -88,8 +88,8 @@ type connection struct {
|
||||
writer buf.Writer
|
||||
done *done.Instance
|
||||
onClose io.Closer
|
||||
local Addr
|
||||
remote Addr
|
||||
local net.Addr
|
||||
remote net.Addr
|
||||
}
|
||||
|
||||
func (c *connection) Read(b []byte) (int, error) {
|
@@ -1,4 +1,4 @@
|
||||
package jsonem
|
||||
package ocsp
|
||||
|
||||
import "github.com/xtls/xray-core/common/errors"
|
||||
|
136
common/ocsp/ocsp.go
Normal file
136
common/ocsp/ocsp.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package ocsp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"golang.org/x/crypto/ocsp"
|
||||
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
)
|
||||
|
||||
func GetOCSPForFile(path string) ([]byte, error) {
|
||||
return filesystem.ReadFile(path)
|
||||
}
|
||||
|
||||
func CheckOCSPFileIsNotExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return os.IsNotExist(err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetOCSPStapling(cert [][]byte, path string) ([]byte, error) {
|
||||
ocspData, err := GetOCSPForFile(path)
|
||||
if err != nil {
|
||||
ocspData, err = GetOCSPForCert(cert)
|
||||
if !CheckOCSPFileIsNotExist(path) {
|
||||
err = os.Remove(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
newFile, err := os.Create(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newFile.Write(ocspData)
|
||||
defer newFile.Close()
|
||||
}
|
||||
return ocspData, nil
|
||||
}
|
||||
|
||||
func GetOCSPForCert(cert [][]byte) ([]byte, error) {
|
||||
bundle := new(bytes.Buffer)
|
||||
for _, derBytes := range cert {
|
||||
err := pem.Encode(bundle, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
pemBundle := bundle.Bytes()
|
||||
|
||||
certificates, err := parsePEMBundle(pemBundle)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
issuedCert := certificates[0]
|
||||
if len(issuedCert.OCSPServer) == 0 {
|
||||
return nil, newError("no OCSP server specified in cert")
|
||||
}
|
||||
if len(certificates) == 1 {
|
||||
if len(issuedCert.IssuingCertificateURL) == 0 {
|
||||
return nil, newError("no issuing certificate URL")
|
||||
}
|
||||
resp, errC := http.Get(issuedCert.IssuingCertificateURL[0])
|
||||
if errC != nil {
|
||||
return nil, newError("no issuing certificate URL")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
issuerBytes, errC := ioutil.ReadAll(resp.Body)
|
||||
if errC != nil {
|
||||
return nil, newError(errC)
|
||||
}
|
||||
|
||||
issuerCert, errC := x509.ParseCertificate(issuerBytes)
|
||||
if errC != nil {
|
||||
return nil, newError(errC)
|
||||
}
|
||||
|
||||
certificates = append(certificates, issuerCert)
|
||||
}
|
||||
issuerCert := certificates[1]
|
||||
|
||||
ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader := bytes.NewReader(ocspReq)
|
||||
req, err := http.Post(issuedCert.OCSPServer[0], "application/ocsp-request", reader)
|
||||
if err != nil {
|
||||
return nil, newError(err)
|
||||
}
|
||||
defer req.Body.Close()
|
||||
ocspResBytes, err := ioutil.ReadAll(req.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, newError(err)
|
||||
}
|
||||
return ocspResBytes, nil
|
||||
|
||||
}
|
||||
|
||||
// parsePEMBundle parses a certificate bundle from top to bottom and returns
|
||||
// a slice of x509 certificates. This function will error if no certificates are found.
|
||||
func parsePEMBundle(bundle []byte) ([]*x509.Certificate, error) {
|
||||
var certificates []*x509.Certificate
|
||||
var certDERBlock *pem.Block
|
||||
|
||||
for {
|
||||
certDERBlock, bundle = pem.Decode(bundle)
|
||||
if certDERBlock == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if certDERBlock.Type == "CERTIFICATE" {
|
||||
cert, err := x509.ParseCertificate(certDERBlock.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certificates = append(certificates, cert)
|
||||
}
|
||||
}
|
||||
|
||||
if len(certificates) == 0 {
|
||||
return nil, newError("no certificates were found while parsing the bundle")
|
||||
}
|
||||
|
||||
return certificates, nil
|
||||
}
|
@@ -30,6 +30,7 @@ func GetAssetLocation(file string) string {
|
||||
defPath,
|
||||
filepath.Join("/usr/local/share/xray/", file),
|
||||
filepath.Join("/usr/share/xray/", file),
|
||||
filepath.Join("/opt/share/xray/", file),
|
||||
} {
|
||||
if _, err := os.Stat(p); os.IsNotExist(err) {
|
||||
continue
|
||||
|
@@ -55,7 +55,7 @@ func TestAddressReading(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)},
|
||||
Input: []byte{3, 9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 80},
|
||||
Input: []byte{3, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 0, 80},
|
||||
Address: net.DomainAddress("example.com"),
|
||||
Port: net.Port(80),
|
||||
},
|
||||
@@ -84,8 +84,9 @@ func TestAddressReading(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range data {
|
||||
b := buf.New()
|
||||
parser := NewAddressParser(tc.Options...)
|
||||
|
||||
b := buf.New()
|
||||
addr, port, err := parser.ReadAddressPort(b, bytes.NewReader(tc.Input))
|
||||
b.Release()
|
||||
if tc.Error {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.14.0
|
||||
// protoc v3.15.4
|
||||
// source: common/protocol/headers.proto
|
||||
|
||||
package protocol
|
||||
@@ -34,6 +34,7 @@ const (
|
||||
SecurityType_AES128_GCM SecurityType = 3
|
||||
SecurityType_CHACHA20_POLY1305 SecurityType = 4
|
||||
SecurityType_NONE SecurityType = 5
|
||||
SecurityType_ZERO SecurityType = 6
|
||||
)
|
||||
|
||||
// Enum value maps for SecurityType.
|
||||
@@ -45,6 +46,7 @@ var (
|
||||
3: "AES128_GCM",
|
||||
4: "CHACHA20_POLY1305",
|
||||
5: "NONE",
|
||||
6: "ZERO",
|
||||
}
|
||||
SecurityType_value = map[string]int32{
|
||||
"UNKNOWN": 0,
|
||||
@@ -53,6 +55,7 @@ var (
|
||||
"AES128_GCM": 3,
|
||||
"CHACHA20_POLY1305": 4,
|
||||
"NONE": 5,
|
||||
"ZERO": 6,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -141,19 +144,20 @@ var file_common_protocol_headers_proto_rawDesc = []byte{
|
||||
0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x63,
|
||||
0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a,
|
||||
0x62, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12,
|
||||
0x6c, 0x0a, 0x0c, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12,
|
||||
0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06,
|
||||
0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x55, 0x54, 0x4f,
|
||||
0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x45, 0x53, 0x31, 0x32, 0x38, 0x5f, 0x47, 0x43, 0x4d,
|
||||
0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x50,
|
||||
0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e,
|
||||
0x45, 0x10, 0x05, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x50,
|
||||
0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74,
|
||||
0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0xaa, 0x02, 0x14, 0x58,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x63, 0x6f, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x45, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x5a, 0x45, 0x52, 0x4f, 0x10, 0x06, 0x42, 0x5e, 0x0a,
|
||||
0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74,
|
||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f,
|
||||
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -13,6 +13,7 @@ enum SecurityType {
|
||||
AES128_GCM = 3;
|
||||
CHACHA20_POLY1305 = 4;
|
||||
NONE = 5;
|
||||
ZERO = 6;
|
||||
}
|
||||
|
||||
message SecurityConfig {
|
||||
|
@@ -102,7 +102,7 @@ func ReadClientHello(data []byte, h *SniffHeader) error {
|
||||
return errNotClientHello
|
||||
}
|
||||
if nameType == 0 {
|
||||
serverName := string(d[:nameLen])
|
||||
serverName := strings.ToLower(string(d[:nameLen]))
|
||||
// An SNI value may not include a
|
||||
// trailing dot. See
|
||||
// https://tools.ietf.org/html/rfc6066#section-3.
|
||||
|
@@ -8,7 +8,7 @@ import (
|
||||
// ToString serialize an arbitrary value into string.
|
||||
func ToString(v interface{}) string {
|
||||
if v == nil {
|
||||
return " "
|
||||
return ""
|
||||
}
|
||||
|
||||
switch value := v.(type) {
|
||||
|
@@ -60,6 +60,7 @@ type Outbound struct {
|
||||
|
||||
// SniffingRequest controls the behavior of content sniffing.
|
||||
type SniffingRequest struct {
|
||||
ExcludeForDomain []string
|
||||
OverrideDestinationForProtocol []string
|
||||
Enabled bool
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package uuid // import "github.com/xtls/xray-core/common/uuid"
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
@@ -49,6 +50,8 @@ func (u *UUID) Equals(another *UUID) bool {
|
||||
func New() UUID {
|
||||
var uuid UUID
|
||||
common.Must2(rand.Read(uuid.Bytes()))
|
||||
uuid[6] = (uuid[6] & 0x0f) | (4 << 4)
|
||||
uuid[8] = (uuid[8]&(0xff>>2) | (0x02 << 6))
|
||||
return uuid
|
||||
}
|
||||
|
||||
@@ -67,8 +70,18 @@ func ParseString(str string) (UUID, error) {
|
||||
var uuid UUID
|
||||
|
||||
text := []byte(str)
|
||||
if len(text) < 32 {
|
||||
return uuid, errors.New("invalid UUID: ", str)
|
||||
if l := len(text); l < 32 || l > 36 {
|
||||
if l == 0 || l > 30 {
|
||||
return uuid, errors.New("invalid UUID: ", str)
|
||||
}
|
||||
h := sha1.New()
|
||||
h.Write(uuid[:])
|
||||
h.Write(text)
|
||||
u := h.Sum(nil)[:16]
|
||||
u[6] = (u[6] & 0x0f) | (5 << 4)
|
||||
u[8] = (u[8]&(0xff>>2) | (0x02 << 6))
|
||||
copy(uuid[:], u)
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
b := uuid.Bytes()
|
||||
|
@@ -35,9 +35,10 @@ func TestParseString(t *testing.T) {
|
||||
t.Fatal(r)
|
||||
}
|
||||
|
||||
_, err = ParseString("2418d087")
|
||||
if err == nil {
|
||||
t.Fatal("Expect error but nil")
|
||||
u0, _ := ParseString("example")
|
||||
u5, _ := ParseString("feb54431-301b-52bb-a6dd-e1e93e81bb9e")
|
||||
if r := cmp.Diff(u0, u5); r != "" {
|
||||
t.Fatal(r)
|
||||
}
|
||||
|
||||
_, err = ParseString("2418d087-648k-4990-86e8-19dca1d006d3")
|
||||
|
137
common/xudp/xudp.go
Normal file
137
common/xudp/xudp.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package xudp
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
)
|
||||
|
||||
var addrParser = protocol.NewAddressParser(
|
||||
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4),
|
||||
protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain),
|
||||
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6),
|
||||
protocol.PortThenAddress(),
|
||||
)
|
||||
|
||||
func NewPacketWriter(writer buf.Writer, dest net.Destination) *PacketWriter {
|
||||
return &PacketWriter{
|
||||
Writer: writer,
|
||||
Dest: dest,
|
||||
}
|
||||
}
|
||||
|
||||
type PacketWriter struct {
|
||||
Writer buf.Writer
|
||||
Dest net.Destination
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
defer buf.ReleaseMulti(mb)
|
||||
mb2Write := make(buf.MultiBuffer, 0, len(mb))
|
||||
for _, b := range mb {
|
||||
length := b.Len()
|
||||
if length == 0 || length+666 > buf.Size {
|
||||
continue
|
||||
}
|
||||
|
||||
eb := buf.New()
|
||||
eb.Write([]byte{0, 0, 0, 0})
|
||||
if w.Dest.Network == net.Network_UDP {
|
||||
eb.WriteByte(1) // New
|
||||
eb.WriteByte(1) // Opt
|
||||
eb.WriteByte(2) // UDP
|
||||
addrParser.WriteAddressPort(eb, w.Dest.Address, w.Dest.Port)
|
||||
w.Dest.Network = net.Network_Unknown
|
||||
} else {
|
||||
eb.WriteByte(2) // Keep
|
||||
eb.WriteByte(1)
|
||||
if b.UDP != nil {
|
||||
eb.WriteByte(2)
|
||||
addrParser.WriteAddressPort(eb, b.UDP.Address, b.UDP.Port)
|
||||
}
|
||||
}
|
||||
l := eb.Len() - 2
|
||||
eb.SetByte(0, byte(l>>8))
|
||||
eb.SetByte(1, byte(l))
|
||||
eb.WriteByte(byte(length >> 8))
|
||||
eb.WriteByte(byte(length))
|
||||
eb.Write(b.Bytes())
|
||||
|
||||
mb2Write = append(mb2Write, eb)
|
||||
}
|
||||
if mb2Write.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
return w.Writer.WriteMultiBuffer(mb2Write)
|
||||
}
|
||||
|
||||
func NewPacketReader(reader io.Reader) *PacketReader {
|
||||
return &PacketReader{
|
||||
Reader: reader,
|
||||
cache: make([]byte, 2),
|
||||
}
|
||||
}
|
||||
|
||||
type PacketReader struct {
|
||||
Reader io.Reader
|
||||
cache []byte
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
for {
|
||||
if _, err := io.ReadFull(r.Reader, r.cache); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := int32(r.cache[0])<<8 | int32(r.cache[1])
|
||||
if l < 4 {
|
||||
return nil, io.EOF
|
||||
}
|
||||
b := buf.New()
|
||||
if _, err := b.ReadFullFrom(r.Reader, l); err != nil {
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
discard := false
|
||||
switch b.Byte(2) {
|
||||
case 2:
|
||||
if l != 4 {
|
||||
b.Advance(5)
|
||||
addr, port, err := addrParser.ReadAddressPort(nil, b)
|
||||
if err != nil {
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
b.UDP = &net.Destination{
|
||||
Network: net.Network_UDP,
|
||||
Address: addr,
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
discard = true
|
||||
default:
|
||||
b.Release()
|
||||
return nil, io.EOF
|
||||
}
|
||||
if b.Byte(3) == 1 {
|
||||
if _, err := io.ReadFull(r.Reader, r.cache); err != nil {
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
length := int32(r.cache[0])<<8 | int32(r.cache[1])
|
||||
if length > 0 {
|
||||
b.Clear()
|
||||
if _, err := b.ReadFullFrom(r.Reader, length); err != nil {
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
if !discard {
|
||||
return buf.MultiBuffer{b}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
b.Release()
|
||||
}
|
||||
}
|
@@ -22,9 +22,13 @@ type ConfigFormat struct {
|
||||
// ConfigLoader is a utility to load Xray config from external source.
|
||||
type ConfigLoader func(input interface{}) (*Config, error)
|
||||
|
||||
// ConfigBuilder is a builder to build core.Config from filenames and formats
|
||||
type ConfigBuilder func(files []string, formats []string) (*Config, error)
|
||||
|
||||
var (
|
||||
configLoaderByName = make(map[string]*ConfigFormat)
|
||||
configLoaderByExt = make(map[string]*ConfigFormat)
|
||||
configLoaderByName = make(map[string]*ConfigFormat)
|
||||
configLoaderByExt = make(map[string]*ConfigFormat)
|
||||
ConfigBuilderForFiles ConfigBuilder
|
||||
)
|
||||
|
||||
// RegisterConfigLoader add a new ConfigLoader.
|
||||
@@ -46,6 +50,21 @@ func RegisterConfigLoader(format *ConfigFormat) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetFormatByExtension(ext string) string {
|
||||
switch strings.ToLower(ext) {
|
||||
case "pb", "protobuf":
|
||||
return "protobuf"
|
||||
case "yaml", "yml":
|
||||
return "yaml"
|
||||
case "toml":
|
||||
return "toml"
|
||||
case "json":
|
||||
return "json"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func getExtension(filename string) string {
|
||||
idx := strings.LastIndexByte(filename, '.')
|
||||
if idx == -1 {
|
||||
@@ -54,23 +73,48 @@ func getExtension(filename string) string {
|
||||
return filename[idx+1:]
|
||||
}
|
||||
|
||||
// LoadConfig loads config with given format from given source.
|
||||
// input accepts 2 different types:
|
||||
// * []string slice of multiple filename/url(s) to open to read
|
||||
// * io.Reader that reads a config content (the original way)
|
||||
func LoadConfig(formatName string, filename string, input interface{}) (*Config, error) {
|
||||
ext := getExtension(filename)
|
||||
if len(ext) > 0 {
|
||||
if f, found := configLoaderByExt[ext]; found {
|
||||
return f.Loader(input)
|
||||
func getFormat(filename string) string {
|
||||
return GetFormatByExtension(getExtension(filename))
|
||||
}
|
||||
|
||||
func LoadConfig(formatName string, input interface{}) (*Config, error) {
|
||||
switch v := input.(type) {
|
||||
case cmdarg.Arg:
|
||||
|
||||
formats := make([]string, len(v))
|
||||
hasProtobuf := false
|
||||
for i, file := range v {
|
||||
f := getFormat(file)
|
||||
if f == "" {
|
||||
f = formatName
|
||||
}
|
||||
if f == "protobuf" {
|
||||
hasProtobuf = true
|
||||
}
|
||||
formats[i] = f
|
||||
}
|
||||
|
||||
// only one protobuf config file is allowed
|
||||
if hasProtobuf {
|
||||
if len(v) == 1 {
|
||||
return configLoaderByName["protobuf"].Loader(v)
|
||||
} else {
|
||||
return nil, newError("Only one protobuf config file is allowed").AtWarning()
|
||||
}
|
||||
}
|
||||
|
||||
// to avoid import cycle
|
||||
return ConfigBuilderForFiles(v, formats)
|
||||
|
||||
case io.Reader:
|
||||
if f, found := configLoaderByName[formatName]; found {
|
||||
return f.Loader(v)
|
||||
} else {
|
||||
return nil, newError("Unable to load config in", formatName).AtWarning()
|
||||
}
|
||||
}
|
||||
|
||||
if f, found := configLoaderByName[formatName]; found {
|
||||
return f.Loader(input)
|
||||
}
|
||||
|
||||
return nil, newError("Unable to load config in ", formatName).AtWarning()
|
||||
return nil, newError("Unable to load config").AtWarning()
|
||||
}
|
||||
|
||||
func loadProtobufConfig(data []byte) (*Config, error) {
|
||||
|
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "1.1.3"
|
||||
version = "1.3.1"
|
||||
build = "Custom"
|
||||
codename = "Xray, Penetrates Everything."
|
||||
intro = "A unified platform for anti-censorship."
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport/internet/udp"
|
||||
)
|
||||
@@ -24,7 +25,7 @@ func CreateObject(v *Instance, config interface{}) (interface{}, error) {
|
||||
//
|
||||
// xray:api:stable
|
||||
func StartInstance(configFormat string, configBytes []byte) (*Instance, error) {
|
||||
config, err := LoadConfig(configFormat, "", bytes.NewReader(configBytes))
|
||||
config, err := LoadConfig(configFormat, bytes.NewReader(configBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -53,13 +54,13 @@ func Dial(ctx context.Context, v *Instance, dest net.Destination) (net.Conn, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var readerOpt net.ConnectionOption
|
||||
var readerOpt cnc.ConnectionOption
|
||||
if dest.Network == net.Network_TCP {
|
||||
readerOpt = net.ConnectionOutputMulti(r.Reader)
|
||||
readerOpt = cnc.ConnectionOutputMulti(r.Reader)
|
||||
} else {
|
||||
readerOpt = net.ConnectionOutputMultiUDP(r.Reader)
|
||||
readerOpt = cnc.ConnectionOutputMultiUDP(r.Reader)
|
||||
}
|
||||
return net.NewConnection(net.ConnectionInputMulti(r.Writer), readerOpt), nil
|
||||
return cnc.NewConnection(cnc.ConnectionInputMulti(r.Writer), readerOpt), nil
|
||||
}
|
||||
|
||||
// DialUDP provides a way to exchange UDP packets through Xray instance to remote servers.
|
||||
|
@@ -2,6 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
@@ -179,6 +180,8 @@ func NewWithContext(ctx context.Context, config *Config) (*Instance, error) {
|
||||
}
|
||||
|
||||
func initInstanceWithConfig(config *Config, server *Instance) (bool, error) {
|
||||
server.ctx = context.WithValue(server.ctx, "cone", os.Getenv("XRAY_CONE_DISABLED") != "true")
|
||||
|
||||
if config.Transport != nil {
|
||||
features.PrintDeprecatedFeatureWarning("global transport settings")
|
||||
}
|
||||
|
29
go.mod
29
go.mod
@@ -1,25 +1,26 @@
|
||||
module github.com/xtls/xray-core
|
||||
|
||||
go 1.15
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 // indirect
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
|
||||
github.com/golang/mock v1.5.0
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/google/go-cmp v0.5.4
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/lucas-clemente/quic-go v0.19.3
|
||||
github.com/miekg/dns v1.1.35
|
||||
github.com/pires/go-proxyproto v0.3.3
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201009151232-afb285a456ab
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/miekg/dns v1.1.40
|
||||
github.com/pelletier/go-toml v1.8.1
|
||||
github.com/pires/go-proxyproto v0.4.2
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499
|
||||
go.starlark.net v0.0.0-20201210151846-e81fc95f7bd5
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||
golang.org/x/sys v0.0.0-20201211002650-1f0c578a6b29
|
||||
google.golang.org/grpc v1.34.0
|
||||
go.starlark.net v0.0.0-20210223155950-e043a3d3c984
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b
|
||||
google.golang.org/grpc v1.36.0
|
||||
google.golang.org/protobuf v1.25.0
|
||||
h12.io/socks v1.0.1
|
||||
h12.io/socks v1.0.2
|
||||
)
|
||||
|
66
go.sum
66
go.sum
@@ -19,7 +19,7 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -29,7 +29,7 @@ github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fp
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||
@@ -37,6 +37,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@@ -47,8 +49,9 @@ github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200j
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -67,6 +70,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
@@ -105,8 +109,8 @@ github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07Vxb
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
|
||||
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
|
||||
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
@@ -121,10 +125,12 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pires/go-proxyproto v0.3.3 h1:jOXGrsAfSQVFiD1hWg1aiHpLYsd6SJw/8cLN594sB7Q=
|
||||
github.com/pires/go-proxyproto v0.3.3/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||
github.com/pires/go-proxyproto v0.4.2 h1:VRAvsUCTrmiahoU5fqQqkbY0GWcJ1Q0F7b7CkFaipSU=
|
||||
github.com/pires/go-proxyproto v0.4.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -134,8 +140,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
||||
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/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201009151232-afb285a456ab h1:O43uBnD2Y6fo1oFsXY+Vqp1n3RFfxg1u3XATDGvUXgI=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201009151232-afb285a456ab/go.mod h1:ET5mVvNjwaGXRgZxO9UZr7X+8eAf87AfIYNwRSp9s4Y=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c h1:pqy40B3MQWYrza7YZXOXgl0Nf0QGFqrOC0BKae1UNAA=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||
@@ -165,8 +171,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
@@ -174,8 +181,8 @@ github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499 h1:QHESTXtfgc1ABV+ArlbPVqU
|
||||
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499/go.mod h1:5TB2+k58gx4A4g2Nf5miSHNDF6CuAzHKpWBooLAshTs=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.starlark.net v0.0.0-20201210151846-e81fc95f7bd5 h1:F1LaLz0cvAJWMa5r3bogEYXD7/5fgA9a9jOX4DAobN8=
|
||||
go.starlark.net v0.0.0-20201210151846-e81fc95f7bd5/go.mod h1:vxxlMsgCAPH7BR2LtxjJC4WhhZhCGd/b01+CIpj8H4k=
|
||||
go.starlark.net v0.0.0-20210223155950-e043a3d3c984 h1:xwwDQW5We85NaTk2APgoN9202w/l0DVGp+GZMfsrh7s=
|
||||
go.starlark.net v0.0.0-20210223155950-e043a3d3c984/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -184,14 +191,15 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604=
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -206,8 +214,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
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-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -219,8 +227,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -236,10 +244,10 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201211002650-1f0c578a6b29 h1:hAYi5mzhvBeCfkgaIHGZ8R+Q04WjSW5ZvQO3BZ94dHY=
|
||||
golang.org/x/sys v0.0.0-20201211002650-1f0c578a6b29/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b h1:kHlr0tATeLRMEiZJu5CknOw/E8V6h69sXXQFGoPtjcc=
|
||||
golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -258,10 +266,13 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
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-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
@@ -286,8 +297,8 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -310,11 +321,12 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
h12.io/socks v1.0.1 h1:bXESSI/+hbdrp+22vcc7/JiXjmLH4UWktKdYgGr3ShA=
|
||||
h12.io/socks v1.0.1/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck=
|
||||
h12.io/socks v1.0.2 h1:cZhhbV8+DE0Y1kotwhr1a3RC3kFO7AtuZ4GLr3qKSc8=
|
||||
h12.io/socks v1.0.2/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-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@@ -84,7 +84,7 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
|
||||
|
||||
geoipList, err := toCidrList(c.ExpectIPs)
|
||||
if err != nil {
|
||||
return nil, newError("invalid ip rule: ", c.ExpectIPs).Base(err)
|
||||
return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err)
|
||||
}
|
||||
|
||||
return &dns.NameServer{
|
||||
@@ -142,7 +142,7 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
|
||||
for _, server := range c.Servers {
|
||||
ns, err := server.Build()
|
||||
if err != nil {
|
||||
return nil, newError("failed to build name server").Base(err)
|
||||
return nil, newError("failed to build nameserver").Base(err)
|
||||
}
|
||||
config.NameServer = append(config.NameServer, ns)
|
||||
}
|
||||
@@ -159,15 +159,23 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
|
||||
var mappings []*dns.Config_HostMapping
|
||||
switch {
|
||||
case strings.HasPrefix(domain, "domain:"):
|
||||
domainName := domain[7:]
|
||||
if len(domainName) == 0 {
|
||||
return nil, newError("empty domain type of rule: ", domain)
|
||||
}
|
||||
mapping := getHostMapping(addr)
|
||||
mapping.Type = dns.DomainMatchingType_Subdomain
|
||||
mapping.Domain = domain[7:]
|
||||
mapping.Domain = domainName
|
||||
mappings = append(mappings, mapping)
|
||||
|
||||
case strings.HasPrefix(domain, "geosite:"):
|
||||
domains, err := loadGeositeWithAttr("geosite.dat", strings.ToUpper(domain[8:]))
|
||||
listName := domain[8:]
|
||||
if len(listName) == 0 {
|
||||
return nil, newError("empty geosite rule: ", domain)
|
||||
}
|
||||
domains, err := loadGeositeWithAttr("geosite.dat", listName)
|
||||
if err != nil {
|
||||
return nil, newError("invalid geosite settings: ", domain).Base(err)
|
||||
return nil, newError("failed to load geosite: ", listName).Base(err)
|
||||
}
|
||||
for _, d := range domains {
|
||||
mapping := getHostMapping(addr)
|
||||
@@ -177,21 +185,33 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
|
||||
}
|
||||
|
||||
case strings.HasPrefix(domain, "regexp:"):
|
||||
regexpVal := domain[7:]
|
||||
if len(regexpVal) == 0 {
|
||||
return nil, newError("empty regexp type of rule: ", domain)
|
||||
}
|
||||
mapping := getHostMapping(addr)
|
||||
mapping.Type = dns.DomainMatchingType_Regex
|
||||
mapping.Domain = domain[7:]
|
||||
mapping.Domain = regexpVal
|
||||
mappings = append(mappings, mapping)
|
||||
|
||||
case strings.HasPrefix(domain, "keyword:"):
|
||||
keywordVal := domain[8:]
|
||||
if len(keywordVal) == 0 {
|
||||
return nil, newError("empty keyword type of rule: ", domain)
|
||||
}
|
||||
mapping := getHostMapping(addr)
|
||||
mapping.Type = dns.DomainMatchingType_Keyword
|
||||
mapping.Domain = domain[8:]
|
||||
mapping.Domain = keywordVal
|
||||
mappings = append(mappings, mapping)
|
||||
|
||||
case strings.HasPrefix(domain, "full:"):
|
||||
fullVal := domain[5:]
|
||||
if len(fullVal) == 0 {
|
||||
return nil, newError("empty full domain type of rule: ", domain)
|
||||
}
|
||||
mapping := getHostMapping(addr)
|
||||
mapping.Type = dns.DomainMatchingType_Full
|
||||
mapping.Domain = domain[5:]
|
||||
mapping.Domain = fullVal
|
||||
mappings = append(mappings, mapping)
|
||||
|
||||
case strings.HasPrefix(domain, "dotless:"):
|
||||
@@ -213,10 +233,10 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
|
||||
return nil, newError("invalid external resource: ", domain)
|
||||
}
|
||||
filename := kv[0]
|
||||
country := kv[1]
|
||||
domains, err := loadGeositeWithAttr(filename, country)
|
||||
list := kv[1]
|
||||
domains, err := loadGeositeWithAttr(filename, list)
|
||||
if err != nil {
|
||||
return nil, newError("failed to load domains: ", country, " from ", filename).Base(err)
|
||||
return nil, newError("failed to load domain list: ", list, " from ", filename).Base(err)
|
||||
}
|
||||
for _, d := range domains {
|
||||
mapping := getHostMapping(addr)
|
||||
|
@@ -21,7 +21,7 @@ func init() {
|
||||
common.Must(err)
|
||||
|
||||
if _, err := os.Stat(platform.GetAssetLocation("geoip.dat")); err != nil && os.IsNotExist(err) {
|
||||
common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "release", "config", "geoip.dat")))
|
||||
common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "resources", "geoip.dat")))
|
||||
}
|
||||
|
||||
geositeFilePath := filepath.Join(wd, "geosite.dat")
|
||||
@@ -112,6 +112,11 @@ func TestDNSConfigParsing(t *testing.T) {
|
||||
Domain: "example.com",
|
||||
ProxiedDomain: "google.com",
|
||||
},
|
||||
{
|
||||
Type: dns.DomainMatchingType_Full,
|
||||
Domain: "example.com",
|
||||
Ip: [][]byte{{127, 0, 0, 1}},
|
||||
},
|
||||
{
|
||||
Type: dns.DomainMatchingType_Full,
|
||||
Domain: "example.com",
|
||||
@@ -127,11 +132,6 @@ func TestDNSConfigParsing(t *testing.T) {
|
||||
Domain: ".*\\.com",
|
||||
Ip: [][]byte{{8, 8, 4, 4}},
|
||||
},
|
||||
{
|
||||
Type: dns.DomainMatchingType_Full,
|
||||
Domain: "example.com",
|
||||
Ip: [][]byte{{127, 0, 0, 1}},
|
||||
},
|
||||
},
|
||||
ClientIp: []byte{10, 0, 0, 1},
|
||||
},
|
||||
|
@@ -2,6 +2,7 @@ package conf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -147,46 +148,109 @@ func ParseIP(s string) (*router.CIDR, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func loadGeoIP(country string) ([]*router.CIDR, error) {
|
||||
return loadIP("geoip.dat", country)
|
||||
func loadGeoIP(code string) ([]*router.CIDR, error) {
|
||||
return loadIP("geoip.dat", code)
|
||||
}
|
||||
|
||||
func loadIP(filename, country string) ([]*router.CIDR, error) {
|
||||
geoipBytes, err := filesystem.ReadAsset(filename)
|
||||
if err != nil {
|
||||
return nil, newError("failed to open file: ", filename).Base(err)
|
||||
}
|
||||
var geoipList router.GeoIPList
|
||||
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
FileCache = make(map[string][]byte)
|
||||
IPCache = make(map[string]*router.GeoIP)
|
||||
SiteCache = make(map[string]*router.GeoSite)
|
||||
)
|
||||
|
||||
for _, geoip := range geoipList.Entry {
|
||||
if geoip.CountryCode == country {
|
||||
return geoip.Cidr, nil
|
||||
func loadFile(file string) ([]byte, error) {
|
||||
if FileCache[file] == nil {
|
||||
bs, err := filesystem.ReadAsset(file)
|
||||
if err != nil {
|
||||
return nil, newError("failed to open file: ", file).Base(err)
|
||||
}
|
||||
if len(bs) == 0 {
|
||||
return nil, newError("empty file: ", file)
|
||||
}
|
||||
// Do not cache file, may save RAM when there
|
||||
// are many files, but consume CPU each time.
|
||||
return bs, nil
|
||||
FileCache[file] = bs
|
||||
}
|
||||
|
||||
return nil, newError("country not found in ", filename, ": ", country)
|
||||
return FileCache[file], nil
|
||||
}
|
||||
|
||||
func loadSite(filename, country string) ([]*router.Domain, error) {
|
||||
geositeBytes, err := filesystem.ReadAsset(filename)
|
||||
if err != nil {
|
||||
return nil, newError("failed to open file: ", filename).Base(err)
|
||||
}
|
||||
var geositeList router.GeoSiteList
|
||||
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, site := range geositeList.Entry {
|
||||
if site.CountryCode == country {
|
||||
return site.Domain, nil
|
||||
func loadIP(file, code string) ([]*router.CIDR, error) {
|
||||
index := file + ":" + code
|
||||
if IPCache[index] == nil {
|
||||
bs, err := loadFile(file)
|
||||
if err != nil {
|
||||
return nil, newError("failed to load file: ", file).Base(err)
|
||||
}
|
||||
bs = find(bs, []byte(code))
|
||||
if bs == nil {
|
||||
return nil, newError("code not found in ", file, ": ", code)
|
||||
}
|
||||
var geoip router.GeoIP
|
||||
if err := proto.Unmarshal(bs, &geoip); err != nil {
|
||||
return nil, newError("error unmarshal IP in ", file, ": ", code).Base(err)
|
||||
}
|
||||
defer runtime.GC() // or debug.FreeOSMemory()
|
||||
return geoip.Cidr, nil // do not cache geoip
|
||||
IPCache[index] = &geoip
|
||||
}
|
||||
return IPCache[index].Cidr, nil
|
||||
}
|
||||
|
||||
return nil, newError("list not found in ", filename, ": ", country)
|
||||
func loadSite(file, code string) ([]*router.Domain, error) {
|
||||
index := file + ":" + code
|
||||
if SiteCache[index] == nil {
|
||||
bs, err := loadFile(file)
|
||||
if err != nil {
|
||||
return nil, newError("failed to load file: ", file).Base(err)
|
||||
}
|
||||
bs = find(bs, []byte(code))
|
||||
if bs == nil {
|
||||
return nil, newError("list not found in ", file, ": ", code)
|
||||
}
|
||||
var geosite router.GeoSite
|
||||
if err := proto.Unmarshal(bs, &geosite); err != nil {
|
||||
return nil, newError("error unmarshal Site in ", file, ": ", code).Base(err)
|
||||
}
|
||||
defer runtime.GC() // or debug.FreeOSMemory()
|
||||
return geosite.Domain, nil // do not cache geosite
|
||||
SiteCache[index] = &geosite
|
||||
}
|
||||
return SiteCache[index].Domain, nil
|
||||
}
|
||||
|
||||
func find(data, code []byte) []byte {
|
||||
codeL := len(code)
|
||||
if codeL == 0 {
|
||||
return nil
|
||||
}
|
||||
for {
|
||||
dataL := len(data)
|
||||
if dataL < 2 {
|
||||
return nil
|
||||
}
|
||||
x, y := proto.DecodeVarint(data[1:])
|
||||
if x == 0 && y == 0 {
|
||||
return nil
|
||||
}
|
||||
headL, bodyL := 1+y, int(x)
|
||||
dataL -= headL
|
||||
if dataL < bodyL {
|
||||
return nil
|
||||
}
|
||||
data = data[headL:]
|
||||
if int(data[1]) == codeL {
|
||||
for i := 0; i < codeL && data[2+i] == code[i]; i++ {
|
||||
if i+1 == codeL {
|
||||
return data[:bodyL]
|
||||
}
|
||||
}
|
||||
}
|
||||
if dataL == bodyL {
|
||||
return nil
|
||||
}
|
||||
data = data[bodyL:]
|
||||
}
|
||||
}
|
||||
|
||||
type AttributeMatcher interface {
|
||||
@@ -396,6 +460,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
||||
type RawFieldRule struct {
|
||||
RouterRule
|
||||
Domain *StringList `json:"domain"`
|
||||
Domains *StringList `json:"domains"`
|
||||
IP *StringList `json:"ip"`
|
||||
Port *PortList `json:"port"`
|
||||
Network *NetworkList `json:"network"`
|
||||
@@ -436,6 +501,16 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if rawFieldRule.Domains != nil {
|
||||
for _, domain := range *rawFieldRule.Domains {
|
||||
rules, err := parseDomainRule(domain)
|
||||
if err != nil {
|
||||
return nil, newError("failed to parse domain rule: ", domain).Base(err)
|
||||
}
|
||||
rule.Domain = append(rule.Domain, rules...)
|
||||
}
|
||||
}
|
||||
|
||||
if rawFieldRule.IP != nil {
|
||||
geoipList, err := toCidrList(*rawFieldRule.IP)
|
||||
if err != nil {
|
||||
|
44
infra/conf/serial/builder.go
Normal file
44
infra/conf/serial/builder.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package serial
|
||||
|
||||
import (
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
"github.com/xtls/xray-core/main/confloader"
|
||||
"io"
|
||||
)
|
||||
|
||||
func BuildConfig(files []string, formats []string) (*core.Config, error) {
|
||||
|
||||
cf := &conf.Config{}
|
||||
for i, file := range files {
|
||||
newError("Reading config: ", file).AtInfo().WriteToLog()
|
||||
r, err := confloader.LoadConfig(file)
|
||||
if err != nil {
|
||||
return nil, newError("failed to read config: ", file).Base(err)
|
||||
}
|
||||
c, err := ReaderDecoderByFormat[formats[i]](r)
|
||||
if err != nil {
|
||||
return nil, newError("failed to decode config: ", file).Base(err)
|
||||
}
|
||||
if i == 0 {
|
||||
*cf = *c
|
||||
continue
|
||||
}
|
||||
cf.Override(c, file)
|
||||
}
|
||||
return cf.Build()
|
||||
}
|
||||
|
||||
type readerDecoder func(io.Reader) (*conf.Config, error)
|
||||
|
||||
var (
|
||||
ReaderDecoderByFormat = make(map[string]readerDecoder)
|
||||
)
|
||||
|
||||
func init() {
|
||||
ReaderDecoderByFormat["json"] = DecodeJSONConfig
|
||||
ReaderDecoderByFormat["yaml"] = DecodeYAMLConfig
|
||||
ReaderDecoderByFormat["toml"] = DecodeTOMLConfig
|
||||
|
||||
core.ConfigBuilderForFiles = BuildConfig
|
||||
}
|
@@ -4,6 +4,10 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/pelletier/go-toml"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/core"
|
||||
@@ -80,3 +84,68 @@ func LoadJSONConfig(reader io.Reader) (*core.Config, error) {
|
||||
|
||||
return pbConfig, nil
|
||||
}
|
||||
|
||||
// DecodeTOMLConfig reads from reader and decode the config into *conf.Config
|
||||
// using github.com/pelletier/go-toml and map to convert toml to json.
|
||||
func DecodeTOMLConfig(reader io.Reader) (*conf.Config, error) {
|
||||
tomlFile, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, newError("failed to read config file").Base(err)
|
||||
}
|
||||
|
||||
configMap := make(map[string]interface{})
|
||||
if err := toml.Unmarshal(tomlFile, &configMap); err != nil {
|
||||
return nil, newError("failed to convert toml to map").Base(err)
|
||||
}
|
||||
|
||||
jsonFile, err := json.Marshal(&configMap)
|
||||
if err != nil {
|
||||
return nil, newError("failed to convert map to json").Base(err)
|
||||
}
|
||||
|
||||
return DecodeJSONConfig(bytes.NewReader(jsonFile))
|
||||
}
|
||||
|
||||
func LoadTOMLConfig(reader io.Reader) (*core.Config, error) {
|
||||
tomlConfig, err := DecodeTOMLConfig(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pbConfig, err := tomlConfig.Build()
|
||||
if err != nil {
|
||||
return nil, newError("failed to parse toml config").Base(err)
|
||||
}
|
||||
|
||||
return pbConfig, nil
|
||||
}
|
||||
|
||||
// DecodeYAMLConfig reads from reader and decode the config into *conf.Config
|
||||
// using github.com/ghodss/yaml to convert yaml to json.
|
||||
func DecodeYAMLConfig(reader io.Reader) (*conf.Config, error) {
|
||||
yamlFile, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, newError("failed to read config file").Base(err)
|
||||
}
|
||||
|
||||
jsonFile, err := yaml.YAMLToJSON(yamlFile)
|
||||
if err != nil {
|
||||
return nil, newError("failed to convert yaml to json").Base(err)
|
||||
}
|
||||
|
||||
return DecodeJSONConfig(bytes.NewReader(jsonFile))
|
||||
}
|
||||
|
||||
func LoadYAMLConfig(reader io.Reader) (*core.Config, error) {
|
||||
yamlConfig, err := DecodeYAMLConfig(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pbConfig, err := yamlConfig.Build()
|
||||
if err != nil {
|
||||
return nil, newError("failed to parse yaml config").Base(err)
|
||||
}
|
||||
|
||||
return pbConfig, nil
|
||||
}
|
||||
|
@@ -33,35 +33,60 @@ func cipherFromString(c string) shadowsocks.CipherType {
|
||||
}
|
||||
}
|
||||
|
||||
type ShadowsocksUserConfig struct {
|
||||
Cipher string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
Level byte `json:"level"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type ShadowsocksServerConfig struct {
|
||||
Cipher string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
UDP bool `json:"udp"`
|
||||
Level byte `json:"level"`
|
||||
Email string `json:"email"`
|
||||
NetworkList *NetworkList `json:"network"`
|
||||
Cipher string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
Level byte `json:"level"`
|
||||
Email string `json:"email"`
|
||||
Users []*ShadowsocksUserConfig `json:"clients"`
|
||||
NetworkList *NetworkList `json:"network"`
|
||||
}
|
||||
|
||||
func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
|
||||
config := new(shadowsocks.ServerConfig)
|
||||
config.UdpEnabled = v.UDP
|
||||
config.Network = v.NetworkList.Build()
|
||||
|
||||
if v.Password == "" {
|
||||
return nil, newError("Shadowsocks password is not specified.")
|
||||
}
|
||||
account := &shadowsocks.Account{
|
||||
Password: v.Password,
|
||||
}
|
||||
account.CipherType = cipherFromString(v.Cipher)
|
||||
if account.CipherType == shadowsocks.CipherType_UNKNOWN {
|
||||
return nil, newError("unknown cipher method: ", v.Cipher)
|
||||
}
|
||||
|
||||
config.User = &protocol.User{
|
||||
Email: v.Email,
|
||||
Level: uint32(v.Level),
|
||||
Account: serial.ToTypedMessage(account),
|
||||
if v.Users != nil {
|
||||
for _, user := range v.Users {
|
||||
account := &shadowsocks.Account{
|
||||
Password: user.Password,
|
||||
CipherType: cipherFromString(user.Cipher),
|
||||
}
|
||||
if account.Password == "" {
|
||||
return nil, newError("Shadowsocks password is not specified.")
|
||||
}
|
||||
if account.CipherType < 5 || account.CipherType > 7 {
|
||||
return nil, newError("unsupported cipher method: ", user.Cipher)
|
||||
}
|
||||
config.Users = append(config.Users, &protocol.User{
|
||||
Email: user.Email,
|
||||
Level: uint32(user.Level),
|
||||
Account: serial.ToTypedMessage(account),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
account := &shadowsocks.Account{
|
||||
Password: v.Password,
|
||||
CipherType: cipherFromString(v.Cipher),
|
||||
}
|
||||
if account.Password == "" {
|
||||
return nil, newError("Shadowsocks password is not specified.")
|
||||
}
|
||||
if account.CipherType == shadowsocks.CipherType_UNKNOWN {
|
||||
return nil, newError("unknown cipher method: ", v.Cipher)
|
||||
}
|
||||
config.Users = append(config.Users, &protocol.User{
|
||||
Email: v.Email,
|
||||
Level: uint32(v.Level),
|
||||
Account: serial.ToTypedMessage(account),
|
||||
})
|
||||
}
|
||||
|
||||
return config, nil
|
||||
@@ -73,7 +98,6 @@ type ShadowsocksServerTarget struct {
|
||||
Cipher string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
Email string `json:"email"`
|
||||
Ota bool `json:"ota"`
|
||||
Level byte `json:"level"`
|
||||
}
|
||||
|
||||
|
@@ -18,17 +18,17 @@ func TestShadowsocksServerConfigParsing(t *testing.T) {
|
||||
runMultiTestCase(t, []TestCase{
|
||||
{
|
||||
Input: `{
|
||||
"method": "aes-128-cfb",
|
||||
"method": "aes-128-gcm",
|
||||
"password": "xray-password"
|
||||
}`,
|
||||
Parser: loadJSON(creator),
|
||||
Output: &shadowsocks.ServerConfig{
|
||||
User: &protocol.User{
|
||||
Users: []*protocol.User{{
|
||||
Account: serial.ToTypedMessage(&shadowsocks.Account{
|
||||
CipherType: shadowsocks.CipherType_AES_128_CFB,
|
||||
CipherType: shadowsocks.CipherType_AES_128_GCM,
|
||||
Password: "xray-password",
|
||||
}),
|
||||
},
|
||||
}},
|
||||
Network: []net.Network{net.Network_TCP},
|
||||
},
|
||||
},
|
||||
|
@@ -247,11 +247,13 @@ func readFileOrString(f string, s []string) ([]byte, error) {
|
||||
}
|
||||
|
||||
type TLSCertConfig struct {
|
||||
CertFile string `json:"certificateFile"`
|
||||
CertStr []string `json:"certificate"`
|
||||
KeyFile string `json:"keyFile"`
|
||||
KeyStr []string `json:"key"`
|
||||
Usage string `json:"usage"`
|
||||
CertFile string `json:"certificateFile"`
|
||||
CertStr []string `json:"certificate"`
|
||||
KeyFile string `json:"keyFile"`
|
||||
KeyStr []string `json:"key"`
|
||||
Usage string `json:"usage"`
|
||||
OcspStapling uint64 `json:"ocspStapling"`
|
||||
OneTimeLoading bool `json:"oneTimeLoading"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -263,6 +265,7 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
|
||||
return nil, newError("failed to parse certificate").Base(err)
|
||||
}
|
||||
certificate.Certificate = cert
|
||||
certificate.CertificatePath = c.CertFile
|
||||
|
||||
if len(c.KeyFile) > 0 || len(c.KeyStr) > 0 {
|
||||
key, err := readFileOrString(c.KeyFile, c.KeyStr)
|
||||
@@ -270,6 +273,7 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
|
||||
return nil, newError("failed to parse key").Base(err)
|
||||
}
|
||||
certificate.Key = key
|
||||
certificate.KeyPath = c.KeyFile
|
||||
}
|
||||
|
||||
switch strings.ToLower(c.Usage) {
|
||||
@@ -282,18 +286,27 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
|
||||
default:
|
||||
certificate.Usage = tls.Certificate_ENCIPHERMENT
|
||||
}
|
||||
if certificate.KeyPath == "" && certificate.CertificatePath == "" {
|
||||
certificate.OneTimeLoading = true
|
||||
} else {
|
||||
certificate.OneTimeLoading = c.OneTimeLoading
|
||||
}
|
||||
certificate.OcspStapling = c.OcspStapling
|
||||
|
||||
return certificate, nil
|
||||
}
|
||||
|
||||
type TLSConfig struct {
|
||||
Insecure bool `json:"allowInsecure"`
|
||||
InsecureCiphers bool `json:"allowInsecureCiphers"`
|
||||
Certs []*TLSCertConfig `json:"certificates"`
|
||||
ServerName string `json:"serverName"`
|
||||
ALPN *StringList `json:"alpn"`
|
||||
DisableSessionResumption bool `json:"disableSessionResumption"`
|
||||
EnableSessionResumption bool `json:"enableSessionResumption"`
|
||||
DisableSystemRoot bool `json:"disableSystemRoot"`
|
||||
MinVersion string `json:"minVersion"`
|
||||
MaxVersion string `json:"maxVersion"`
|
||||
CipherSuites string `json:"cipherSuites"`
|
||||
PreferServerCipherSuites bool `json:"preferServerCipherSuites"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -309,35 +322,40 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
serverName := c.ServerName
|
||||
config.AllowInsecure = c.Insecure
|
||||
config.AllowInsecureCiphers = c.InsecureCiphers
|
||||
if len(c.ServerName) > 0 {
|
||||
config.ServerName = serverName
|
||||
}
|
||||
if c.ALPN != nil && len(*c.ALPN) > 0 {
|
||||
config.NextProtocol = []string(*c.ALPN)
|
||||
}
|
||||
config.DisableSessionResumption = c.DisableSessionResumption
|
||||
config.EnableSessionResumption = c.EnableSessionResumption
|
||||
config.DisableSystemRoot = c.DisableSystemRoot
|
||||
config.MinVersion = c.MinVersion
|
||||
config.MaxVersion = c.MaxVersion
|
||||
config.CipherSuites = c.CipherSuites
|
||||
config.PreferServerCipherSuites = c.PreferServerCipherSuites
|
||||
return config, nil
|
||||
}
|
||||
|
||||
type XTLSCertConfig struct {
|
||||
CertFile string `json:"certificateFile"`
|
||||
CertStr []string `json:"certificate"`
|
||||
KeyFile string `json:"keyFile"`
|
||||
KeyStr []string `json:"key"`
|
||||
Usage string `json:"usage"`
|
||||
CertFile string `json:"certificateFile"`
|
||||
CertStr []string `json:"certificate"`
|
||||
KeyFile string `json:"keyFile"`
|
||||
KeyStr []string `json:"key"`
|
||||
Usage string `json:"usage"`
|
||||
OcspStapling uint64 `json:"ocspStapling"`
|
||||
OneTimeLoading bool `json:"oneTimeLoading"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
func (c *XTLSCertConfig) Build() (*xtls.Certificate, error) {
|
||||
certificate := new(xtls.Certificate)
|
||||
|
||||
cert, err := readFileOrString(c.CertFile, c.CertStr)
|
||||
if err != nil {
|
||||
return nil, newError("failed to parse certificate").Base(err)
|
||||
}
|
||||
certificate.Certificate = cert
|
||||
certificate.CertificatePath = c.CertFile
|
||||
|
||||
if len(c.KeyFile) > 0 || len(c.KeyStr) > 0 {
|
||||
key, err := readFileOrString(c.KeyFile, c.KeyStr)
|
||||
@@ -345,6 +363,7 @@ func (c *XTLSCertConfig) Build() (*xtls.Certificate, error) {
|
||||
return nil, newError("failed to parse key").Base(err)
|
||||
}
|
||||
certificate.Key = key
|
||||
certificate.KeyPath = c.KeyFile
|
||||
}
|
||||
|
||||
switch strings.ToLower(c.Usage) {
|
||||
@@ -357,18 +376,27 @@ func (c *XTLSCertConfig) Build() (*xtls.Certificate, error) {
|
||||
default:
|
||||
certificate.Usage = xtls.Certificate_ENCIPHERMENT
|
||||
}
|
||||
if certificate.KeyPath == "" && certificate.CertificatePath == "" {
|
||||
certificate.OneTimeLoading = true
|
||||
} else {
|
||||
certificate.OneTimeLoading = c.OneTimeLoading
|
||||
}
|
||||
certificate.OcspStapling = c.OcspStapling
|
||||
|
||||
return certificate, nil
|
||||
}
|
||||
|
||||
type XTLSConfig struct {
|
||||
Insecure bool `json:"allowInsecure"`
|
||||
InsecureCiphers bool `json:"allowInsecureCiphers"`
|
||||
Certs []*XTLSCertConfig `json:"certificates"`
|
||||
ServerName string `json:"serverName"`
|
||||
ALPN *StringList `json:"alpn"`
|
||||
DisableSessionResumption bool `json:"disableSessionResumption"`
|
||||
EnableSessionResumption bool `json:"enableSessionResumption"`
|
||||
DisableSystemRoot bool `json:"disableSystemRoot"`
|
||||
MinVersion string `json:"minVersion"`
|
||||
MaxVersion string `json:"maxVersion"`
|
||||
CipherSuites string `json:"cipherSuites"`
|
||||
PreferServerCipherSuites bool `json:"preferServerCipherSuites"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -384,15 +412,18 @@ func (c *XTLSConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
serverName := c.ServerName
|
||||
config.AllowInsecure = c.Insecure
|
||||
config.AllowInsecureCiphers = c.InsecureCiphers
|
||||
if len(c.ServerName) > 0 {
|
||||
config.ServerName = serverName
|
||||
}
|
||||
if c.ALPN != nil && len(*c.ALPN) > 0 {
|
||||
config.NextProtocol = []string(*c.ALPN)
|
||||
}
|
||||
config.DisableSessionResumption = c.DisableSessionResumption
|
||||
config.EnableSessionResumption = c.EnableSessionResumption
|
||||
config.DisableSystemRoot = c.DisableSystemRoot
|
||||
config.MinVersion = c.MinVersion
|
||||
config.MaxVersion = c.MaxVersion
|
||||
config.CipherSuites = c.CipherSuites
|
||||
config.PreferServerCipherSuites = c.PreferServerCipherSuites
|
||||
return config, nil
|
||||
}
|
||||
|
||||
|
@@ -52,6 +52,17 @@ func (c *TrojanClientConfig) Build() (proto.Message, error) {
|
||||
Password: rec.Password,
|
||||
Flow: rec.Flow,
|
||||
}
|
||||
|
||||
switch account.Flow {
|
||||
case "", "xtls-rprx-origin", "xtls-rprx-origin-udp443", "xtls-rprx-direct", "xtls-rprx-direct-udp443":
|
||||
case "xtls-rprx-splice", "xtls-rprx-splice-udp443":
|
||||
if runtime.GOOS != "linux" && runtime.GOOS != "android" {
|
||||
return nil, newError(`Trojan servers: "` + account.Flow + `" only support linux in this version`)
|
||||
}
|
||||
default:
|
||||
return nil, newError(`Trojan servers: "flow" doesn't support "` + account.Flow + `" in this version`)
|
||||
}
|
||||
|
||||
trojan := &protocol.ServerEndpoint{
|
||||
Address: rec.Address.Build(),
|
||||
Port: uint32(rec.Port),
|
||||
@@ -74,6 +85,7 @@ func (c *TrojanClientConfig) Build() (proto.Message, error) {
|
||||
|
||||
// TrojanInboundFallback is fallback configuration
|
||||
type TrojanInboundFallback struct {
|
||||
Name string `json:"name"`
|
||||
Alpn string `json:"alpn"`
|
||||
Path string `json:"path"`
|
||||
Type string `json:"type"`
|
||||
@@ -107,6 +119,14 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) {
|
||||
Flow: rawUser.Flow,
|
||||
}
|
||||
|
||||
switch account.Flow {
|
||||
case "", "xtls-rprx-origin", "xtls-rprx-direct":
|
||||
case "xtls-rprx-splice":
|
||||
return nil, newError(`Trojan clients: inbound doesn't support "xtls-rprx-splice" in this version, please use "xtls-rprx-direct" instead`)
|
||||
default:
|
||||
return nil, newError(`Trojan clients: "flow" doesn't support "` + account.Flow + `" in this version`)
|
||||
}
|
||||
|
||||
user.Email = rawUser.Email
|
||||
user.Level = uint32(rawUser.Level)
|
||||
user.Account = serial.ToTypedMessage(account)
|
||||
@@ -125,6 +145,7 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) {
|
||||
_ = json.Unmarshal(fb.Dest, &s)
|
||||
}
|
||||
config.Fallbacks = append(config.Fallbacks, &trojan.Fallback{
|
||||
Name: fb.Name,
|
||||
Alpn: fb.Alpn,
|
||||
Path: fb.Path,
|
||||
Type: fb.Type,
|
||||
@@ -148,7 +169,7 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) {
|
||||
switch fb.Dest[0] {
|
||||
case '@', '/':
|
||||
fb.Type = "unix"
|
||||
if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && runtime.GOOS == "linux" {
|
||||
if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && (runtime.GOOS == "linux" || runtime.GOOS == "android") {
|
||||
fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy
|
||||
copy(fullAddr, fb.Dest[1:])
|
||||
fb.Dest = string(fullAddr)
|
||||
|
@@ -11,12 +11,14 @@ import (
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/common/uuid"
|
||||
"github.com/xtls/xray-core/proxy/vless"
|
||||
"github.com/xtls/xray-core/proxy/vless/inbound"
|
||||
"github.com/xtls/xray-core/proxy/vless/outbound"
|
||||
)
|
||||
|
||||
type VLessInboundFallback struct {
|
||||
Name string `json:"name"`
|
||||
Alpn string `json:"alpn"`
|
||||
Path string `json:"path"`
|
||||
Type string `json:"type"`
|
||||
@@ -45,6 +47,12 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
return nil, newError(`VLESS clients: invalid user`).Base(err)
|
||||
}
|
||||
|
||||
u, err := uuid.ParseString(account.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
account.Id = u.String()
|
||||
|
||||
switch account.Flow {
|
||||
case "", "xtls-rprx-origin", "xtls-rprx-direct":
|
||||
case "xtls-rprx-splice":
|
||||
@@ -78,6 +86,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
_ = json.Unmarshal(fb.Dest, &s)
|
||||
}
|
||||
config.Fallbacks = append(config.Fallbacks, &inbound.Fallback{
|
||||
Name: fb.Name,
|
||||
Alpn: fb.Alpn,
|
||||
Path: fb.Path,
|
||||
Type: fb.Type,
|
||||
@@ -101,7 +110,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||
switch fb.Dest[0] {
|
||||
case '@', '/':
|
||||
fb.Type = "unix"
|
||||
if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && runtime.GOOS == "linux" {
|
||||
if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && (runtime.GOOS == "linux" || runtime.GOOS == "android") {
|
||||
fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy
|
||||
copy(fullAddr, fb.Dest[1:])
|
||||
fb.Dest = string(fullAddr)
|
||||
@@ -167,6 +176,12 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
||||
return nil, newError(`VLESS users: invalid user`).Base(err)
|
||||
}
|
||||
|
||||
u, err := uuid.ParseString(account.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
account.Id = u.String()
|
||||
|
||||
switch account.Flow {
|
||||
case "", "xtls-rprx-origin", "xtls-rprx-origin-udp443", "xtls-rprx-direct", "xtls-rprx-direct-udp443":
|
||||
case "xtls-rprx-splice", "xtls-rprx-splice-udp443":
|
||||
|
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/common/uuid"
|
||||
"github.com/xtls/xray-core/proxy/vmess"
|
||||
"github.com/xtls/xray-core/proxy/vmess/inbound"
|
||||
"github.com/xtls/xray-core/proxy/vmess/outbound"
|
||||
@@ -31,6 +32,8 @@ func (a *VMessAccount) Build() *vmess.Account {
|
||||
st = protocol.SecurityType_AUTO
|
||||
case "none":
|
||||
st = protocol.SecurityType_NONE
|
||||
case "zero":
|
||||
st = protocol.SecurityType_ZERO
|
||||
default:
|
||||
st = protocol.SecurityType_AUTO
|
||||
}
|
||||
@@ -105,6 +108,13 @@ func (c *VMessInboundConfig) Build() (proto.Message, error) {
|
||||
if err := json.Unmarshal(rawData, account); err != nil {
|
||||
return nil, newError("invalid VMess user").Base(err)
|
||||
}
|
||||
|
||||
u, err := uuid.ParseString(account.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
account.ID = u.String()
|
||||
|
||||
user.Account = serial.ToTypedMessage(account.Build())
|
||||
config.User[idx] = user
|
||||
}
|
||||
@@ -149,6 +159,13 @@ func (c *VMessOutboundConfig) Build() (proto.Message, error) {
|
||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
||||
return nil, newError("invalid VMess user").Base(err)
|
||||
}
|
||||
|
||||
u, err := uuid.ParseString(account.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
account.ID = u.String()
|
||||
|
||||
user.Account = serial.ToTypedMessage(account.Build())
|
||||
spec.User = append(spec.User, user)
|
||||
}
|
||||
|
@@ -58,29 +58,38 @@ func toProtocolList(s []string) ([]proxyman.KnownProtocols, error) {
|
||||
}
|
||||
|
||||
type SniffingConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
DestOverride *StringList `json:"destOverride"`
|
||||
Enabled bool `json:"enabled"`
|
||||
DestOverride *StringList `json:"destOverride"`
|
||||
DomainsExcluded *StringList `json:"domainsExcluded"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
|
||||
var p []string
|
||||
if c.DestOverride != nil {
|
||||
for _, domainOverride := range *c.DestOverride {
|
||||
switch strings.ToLower(domainOverride) {
|
||||
for _, protocol := range *c.DestOverride {
|
||||
switch strings.ToLower(protocol) {
|
||||
case "http":
|
||||
p = append(p, "http")
|
||||
case "tls", "https", "ssl":
|
||||
p = append(p, "tls")
|
||||
default:
|
||||
return nil, newError("unknown protocol: ", domainOverride)
|
||||
return nil, newError("unknown protocol: ", protocol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var d []string
|
||||
if c.DomainsExcluded != nil {
|
||||
for _, domain := range *c.DomainsExcluded {
|
||||
d = append(d, strings.ToLower(domain))
|
||||
}
|
||||
}
|
||||
|
||||
return &proxyman.SniffingConfig{
|
||||
Enabled: c.Enabled,
|
||||
DestinationOverride: p,
|
||||
DomainsExcluded: d,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@@ -8,15 +8,33 @@ import (
|
||||
)
|
||||
|
||||
var cmdUUID = &base.Command{
|
||||
UsageLine: "{{.Exec}} uuid",
|
||||
Short: "Generate new UUIDs",
|
||||
UsageLine: `{{.Exec}} uuid [-i "example"]`,
|
||||
Short: `Generate UUIDv4 or UUIDv5`,
|
||||
Long: `
|
||||
Generate new UUIDs.
|
||||
`,
|
||||
Run: executeUUID,
|
||||
Generate UUIDv4 or UUIDv5.
|
||||
|
||||
UUIDv4 (random): {{.Exec}} uuid
|
||||
|
||||
UUIDv5 (from input): {{.Exec}} uuid -i "example"
|
||||
`,
|
||||
}
|
||||
|
||||
func executeUUID(cmd *base.Command, args []string) {
|
||||
u := uuid.New()
|
||||
fmt.Println(u.String())
|
||||
func init() {
|
||||
cmdUUID.Run = executeUUID // break init loop
|
||||
}
|
||||
|
||||
var input = cmdUUID.Flag.String("i", "", "")
|
||||
|
||||
func executeUUID(cmd *base.Command, args []string) {
|
||||
var output string
|
||||
if l := len(*input); l == 0 {
|
||||
u := uuid.New()
|
||||
output = u.String()
|
||||
} else if l <= 30 {
|
||||
u, _ := uuid.ParseString(*input)
|
||||
output = u.String()
|
||||
} else {
|
||||
output = "Input must be within 30 bytes."
|
||||
}
|
||||
fmt.Println(output)
|
||||
}
|
||||
|
@@ -57,15 +57,14 @@ import (
|
||||
_ "github.com/xtls/xray-core/transport/internet/headers/wechat"
|
||||
_ "github.com/xtls/xray-core/transport/internet/headers/wireguard"
|
||||
|
||||
// JSON config support. Choose only one from the two below.
|
||||
// The following line loads JSON from xctl
|
||||
// _ "github.com/xtls/xray-core/main/json"
|
||||
// The following line loads JSON internally
|
||||
_ "github.com/xtls/xray-core/main/jsonem"
|
||||
// JSON & TOML & YAML
|
||||
_ "github.com/xtls/xray-core/main/json"
|
||||
_ "github.com/xtls/xray-core/main/toml"
|
||||
_ "github.com/xtls/xray-core/main/yaml"
|
||||
|
||||
// Load config from file or http(s)
|
||||
_ "github.com/xtls/xray-core/main/confloader/external"
|
||||
|
||||
// commands
|
||||
// Commands
|
||||
_ "github.com/xtls/xray-core/main/commands/all"
|
||||
)
|
||||
|
@@ -1,38 +0,0 @@
|
||||
package json
|
||||
|
||||
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/cmdarg"
|
||||
core "github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/main/confloader"
|
||||
)
|
||||
|
||||
func init() {
|
||||
common.Must(core.RegisterConfigLoader(&core.ConfigFormat{
|
||||
Name: "JSON",
|
||||
Extension: []string{"json"},
|
||||
Loader: func(input interface{}) (*core.Config, error) {
|
||||
switch v := input.(type) {
|
||||
case cmdarg.Arg:
|
||||
r, err := confloader.LoadExtConfig(v, os.Stdin)
|
||||
if err != nil {
|
||||
return nil, newError("failed to execute xctl to convert config file.").Base(err).AtWarning()
|
||||
}
|
||||
return core.LoadConfig("protobuf", "", r)
|
||||
case io.Reader:
|
||||
r, err := confloader.LoadExtConfig([]string{"stdin:"}, os.Stdin)
|
||||
if err != nil {
|
||||
return nil, newError("failed to execute xctl to convert config file.").Base(err).AtWarning()
|
||||
}
|
||||
return core.LoadConfig("protobuf", "", r)
|
||||
default:
|
||||
return nil, newError("unknown type")
|
||||
}
|
||||
},
|
||||
}))
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package jsonem
|
||||
package json
|
||||
|
||||
import (
|
||||
"io"
|
||||
@@ -22,9 +22,13 @@ func init() {
|
||||
for i, arg := range v {
|
||||
newError("Reading config: ", arg).AtInfo().WriteToLog()
|
||||
r, err := confloader.LoadConfig(arg)
|
||||
common.Must(err)
|
||||
if err != nil {
|
||||
return nil, newError("failed to read config: ", arg).Base(err)
|
||||
}
|
||||
c, err := serial.DecodeJSONConfig(r)
|
||||
common.Must(err)
|
||||
if err != nil {
|
||||
return nil, newError("failed to decode config: ", arg).Base(err)
|
||||
}
|
||||
if i == 0 {
|
||||
// This ensure even if the muti-json parser do not support a setting,
|
||||
// It is still respected automatically for the first configure file
|
44
main/run.go
44
main/run.go
@@ -8,8 +8,9 @@ import (
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"runtime/debug"
|
||||
"syscall"
|
||||
|
||||
"github.com/xtls/xray-core/common/cmdarg"
|
||||
@@ -64,22 +65,31 @@ func executeRun(cmd *base.Command, args []string) {
|
||||
printVersion()
|
||||
server, err := startXray()
|
||||
if err != nil {
|
||||
base.Fatalf("Failed to start: %s", err)
|
||||
fmt.Println("Failed to start:", err)
|
||||
// Configuration error. Exit with a special value to prevent systemd from restarting.
|
||||
os.Exit(23)
|
||||
}
|
||||
|
||||
if *test {
|
||||
fmt.Println("Configuration OK.")
|
||||
base.SetExitStatus(0)
|
||||
base.Exit()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if err := server.Start(); err != nil {
|
||||
base.Fatalf("Failed to start: %s", err)
|
||||
fmt.Println("Failed to start:", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
/*
|
||||
conf.FileCache = nil
|
||||
conf.IPCache = nil
|
||||
conf.SiteCache = nil
|
||||
*/
|
||||
|
||||
// Explicitly triggering GC to remove garbage from config loading.
|
||||
runtime.GC()
|
||||
debug.FreeOSMemory()
|
||||
|
||||
{
|
||||
osSignals := make(chan os.Signal, 1)
|
||||
@@ -107,7 +117,11 @@ func readConfDir(dirPath string) {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
for _, f := range confs {
|
||||
if strings.HasSuffix(f.Name(), ".json") {
|
||||
matched, err := regexp.MatchString(`^.+\.(json|toml|yaml|yml)$`, f.Name())
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if matched {
|
||||
configFiles.Set(path.Join(dirPath, f.Name()))
|
||||
}
|
||||
}
|
||||
@@ -144,23 +158,25 @@ func getConfigFilePath() cmdarg.Arg {
|
||||
}
|
||||
|
||||
func getConfigFormat() string {
|
||||
switch strings.ToLower(*format) {
|
||||
case "pb", "protobuf":
|
||||
return "protobuf"
|
||||
default:
|
||||
return "json"
|
||||
f := core.GetFormatByExtension(*format)
|
||||
if f == "" {
|
||||
f = "json"
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func startXray() (core.Server, error) {
|
||||
configFiles := getConfigFilePath()
|
||||
|
||||
config, err := core.LoadConfig(getConfigFormat(), configFiles[0], configFiles)
|
||||
//config, err := core.LoadConfig(getConfigFormat(), configFiles[0], configFiles)
|
||||
|
||||
c, err := core.LoadConfig(getConfigFormat(), configFiles)
|
||||
|
||||
if err != nil {
|
||||
return nil, newError("failed to read config files: [", configFiles.String(), "]").Base(err)
|
||||
return nil, newError("failed to load config files: [", configFiles.String(), "]").Base(err)
|
||||
}
|
||||
|
||||
server, err := core.New(config)
|
||||
server, err := core.New(c)
|
||||
if err != nil {
|
||||
return nil, newError("failed to create server").Base(err)
|
||||
}
|
||||
|
9
main/toml/errors.generated.go
Normal file
9
main/toml/errors.generated.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package toml
|
||||
|
||||
import "github.com/xtls/xray-core/common/errors"
|
||||
|
||||
type errPathObjHolder struct{}
|
||||
|
||||
func newError(values ...interface{}) *errors.Error {
|
||||
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||
}
|
48
main/toml/toml.go
Normal file
48
main/toml/toml.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package toml
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/cmdarg"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
"github.com/xtls/xray-core/infra/conf/serial"
|
||||
"github.com/xtls/xray-core/main/confloader"
|
||||
)
|
||||
|
||||
func init() {
|
||||
common.Must(core.RegisterConfigLoader(&core.ConfigFormat{
|
||||
Name: "TOML",
|
||||
Extension: []string{"toml"},
|
||||
Loader: func(input interface{}) (*core.Config, error) {
|
||||
switch v := input.(type) {
|
||||
case cmdarg.Arg:
|
||||
cf := &conf.Config{}
|
||||
for i, arg := range v {
|
||||
newError("Reading config: ", arg).AtInfo().WriteToLog()
|
||||
r, err := confloader.LoadConfig(arg)
|
||||
if err != nil {
|
||||
return nil, newError("failed to read config: ", arg).Base(err)
|
||||
}
|
||||
c, err := serial.DecodeTOMLConfig(r)
|
||||
if err != nil {
|
||||
return nil, newError("failed to decode config: ", arg).Base(err)
|
||||
}
|
||||
if i == 0 {
|
||||
// This ensure even if the muti-json parser do not support a setting,
|
||||
// It is still respected automatically for the first configure file
|
||||
*cf = *c
|
||||
continue
|
||||
}
|
||||
cf.Override(c, arg)
|
||||
}
|
||||
return cf.Build()
|
||||
case io.Reader:
|
||||
return serial.LoadTOMLConfig(v)
|
||||
default:
|
||||
return nil, newError("unknow type")
|
||||
}
|
||||
},
|
||||
}))
|
||||
}
|
9
main/yaml/errors.generated.go
Normal file
9
main/yaml/errors.generated.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package yaml
|
||||
|
||||
import "github.com/xtls/xray-core/common/errors"
|
||||
|
||||
type errPathObjHolder struct{}
|
||||
|
||||
func newError(values ...interface{}) *errors.Error {
|
||||
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||
}
|
48
main/yaml/yaml.go
Normal file
48
main/yaml/yaml.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/cmdarg"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
"github.com/xtls/xray-core/infra/conf/serial"
|
||||
"github.com/xtls/xray-core/main/confloader"
|
||||
)
|
||||
|
||||
func init() {
|
||||
common.Must(core.RegisterConfigLoader(&core.ConfigFormat{
|
||||
Name: "YAML",
|
||||
Extension: []string{"yaml", "yml"},
|
||||
Loader: func(input interface{}) (*core.Config, error) {
|
||||
switch v := input.(type) {
|
||||
case cmdarg.Arg:
|
||||
cf := &conf.Config{}
|
||||
for i, arg := range v {
|
||||
newError("Reading config: ", arg).AtInfo().WriteToLog()
|
||||
r, err := confloader.LoadConfig(arg)
|
||||
if err != nil {
|
||||
return nil, newError("failed to read config: ", arg).Base(err)
|
||||
}
|
||||
c, err := serial.DecodeYAMLConfig(r)
|
||||
if err != nil {
|
||||
return nil, newError("failed to decode config: ", arg).Base(err)
|
||||
}
|
||||
if i == 0 {
|
||||
// This ensure even if the muti-json parser do not support a setting,
|
||||
// It is still respected automatically for the first configure file
|
||||
*cf = *c
|
||||
continue
|
||||
}
|
||||
cf.Override(c, arg)
|
||||
}
|
||||
return cf.Build()
|
||||
case io.Reader:
|
||||
return serial.LoadYAMLConfig(v)
|
||||
default:
|
||||
return nil, newError("unknow type")
|
||||
}
|
||||
},
|
||||
}))
|
||||
}
|
@@ -163,36 +163,60 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in
|
||||
if !destinationOverridden {
|
||||
writer = &buf.SequentialWriter{Writer: conn}
|
||||
} else {
|
||||
sockopt := &internet.SocketConfig{
|
||||
Tproxy: internet.SocketConfig_TProxy,
|
||||
back := conn.RemoteAddr().(*net.UDPAddr)
|
||||
if !dest.Address.Family().IsIP() {
|
||||
if len(back.IP) == 4 {
|
||||
dest.Address = net.AnyIP
|
||||
} else {
|
||||
dest.Address = net.AnyIPv6
|
||||
}
|
||||
}
|
||||
if dest.Address.Family().IsIP() {
|
||||
sockopt.BindAddress = dest.Address.IP()
|
||||
sockopt.BindPort = uint32(dest.Port)
|
||||
addr := &net.UDPAddr{
|
||||
IP: dest.Address.IP(),
|
||||
Port: int(dest.Port),
|
||||
}
|
||||
var mark int
|
||||
if d.sockopt != nil {
|
||||
sockopt.Mark = d.sockopt.Mark
|
||||
mark = int(d.sockopt.Mark)
|
||||
}
|
||||
tConn, err := internet.DialSystem(ctx, net.DestinationFromAddr(conn.RemoteAddr()), sockopt)
|
||||
pConn, err := FakeUDP(addr, mark)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tConn.Close()
|
||||
|
||||
writer = &buf.SequentialWriter{Writer: tConn}
|
||||
tReader := buf.NewPacketReader(tConn)
|
||||
requestCount++
|
||||
tproxyRequest = func() error {
|
||||
defer func() {
|
||||
if atomic.AddInt32(&requestCount, -1) == 0 {
|
||||
timer.SetTimeout(plcy.Timeouts.DownlinkOnly)
|
||||
}
|
||||
}()
|
||||
if err := buf.Copy(tReader, link.Writer, buf.UpdateActivity(timer)); err != nil {
|
||||
return newError("failed to transport request (TPROXY conn)").Base(err)
|
||||
writer = NewPacketWriter(pConn, &dest, mark, back)
|
||||
defer writer.(*PacketWriter).Close()
|
||||
/*
|
||||
sockopt := &internet.SocketConfig{
|
||||
Tproxy: internet.SocketConfig_TProxy,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if dest.Address.Family().IsIP() {
|
||||
sockopt.BindAddress = dest.Address.IP()
|
||||
sockopt.BindPort = uint32(dest.Port)
|
||||
}
|
||||
if d.sockopt != nil {
|
||||
sockopt.Mark = d.sockopt.Mark
|
||||
}
|
||||
tConn, err := internet.DialSystem(ctx, net.DestinationFromAddr(conn.RemoteAddr()), sockopt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tConn.Close()
|
||||
|
||||
writer = &buf.SequentialWriter{Writer: tConn}
|
||||
tReader := buf.NewPacketReader(tConn)
|
||||
requestCount++
|
||||
tproxyRequest = func() error {
|
||||
defer func() {
|
||||
if atomic.AddInt32(&requestCount, -1) == 0 {
|
||||
timer.SetTimeout(plcy.Timeouts.DownlinkOnly)
|
||||
}
|
||||
}()
|
||||
if err := buf.Copy(tReader, link.Writer, buf.UpdateActivity(timer)); err != nil {
|
||||
return newError("failed to transport request (TPROXY conn)").Base(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,3 +239,74 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewPacketWriter(conn net.PacketConn, d *net.Destination, mark int, back *net.UDPAddr) buf.Writer {
|
||||
writer := &PacketWriter{
|
||||
conn: conn,
|
||||
conns: make(map[net.Destination]net.PacketConn),
|
||||
mark: mark,
|
||||
back: back,
|
||||
}
|
||||
writer.conns[*d] = conn
|
||||
return writer
|
||||
}
|
||||
|
||||
type PacketWriter struct {
|
||||
conn net.PacketConn
|
||||
conns map[net.Destination]net.PacketConn
|
||||
mark int
|
||||
back *net.UDPAddr
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
for {
|
||||
mb2, b := buf.SplitFirst(mb)
|
||||
mb = mb2
|
||||
if b == nil {
|
||||
break
|
||||
}
|
||||
var err error
|
||||
if b.UDP != nil && b.UDP.Address.Family().IsIP() {
|
||||
conn := w.conns[*b.UDP]
|
||||
if conn == nil {
|
||||
conn, err = FakeUDP(
|
||||
&net.UDPAddr{
|
||||
IP: b.UDP.Address.IP(),
|
||||
Port: int(b.UDP.Port),
|
||||
},
|
||||
w.mark,
|
||||
)
|
||||
if err != nil {
|
||||
newError(err).WriteToLog()
|
||||
b.Release()
|
||||
continue
|
||||
}
|
||||
w.conns[*b.UDP] = conn
|
||||
}
|
||||
_, err = conn.WriteTo(b.Bytes(), w.back)
|
||||
if err != nil {
|
||||
newError(err).WriteToLog()
|
||||
w.conns[*b.UDP] = nil
|
||||
conn.Close()
|
||||
}
|
||||
b.Release()
|
||||
} else {
|
||||
_, err = w.conn.WriteTo(b.Bytes(), w.back)
|
||||
b.Release()
|
||||
if err != nil {
|
||||
buf.ReleaseMulti(mb)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *PacketWriter) Close() error {
|
||||
for _, conn := range w.conns {
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
66
proxy/dokodemo/fakeudp_linux.go
Normal file
66
proxy/dokodemo/fakeudp_linux.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// +build linux
|
||||
|
||||
package dokodemo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func FakeUDP(addr *net.UDPAddr, mark int) (net.PacketConn, error) {
|
||||
var af int
|
||||
var sockaddr syscall.Sockaddr
|
||||
|
||||
if len(addr.IP) == 4 {
|
||||
af = syscall.AF_INET
|
||||
sockaddr = &syscall.SockaddrInet4{Port: addr.Port}
|
||||
copy(sockaddr.(*syscall.SockaddrInet4).Addr[:], addr.IP)
|
||||
} else {
|
||||
af = syscall.AF_INET6
|
||||
sockaddr = &syscall.SockaddrInet6{Port: addr.Port}
|
||||
copy(sockaddr.(*syscall.SockaddrInet6).Addr[:], addr.IP)
|
||||
}
|
||||
|
||||
var fd int
|
||||
var err error
|
||||
|
||||
if fd, err = syscall.Socket(af, syscall.SOCK_DGRAM, 0); err != nil {
|
||||
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("socket open: %s", err)}
|
||||
}
|
||||
|
||||
if mark != 0 {
|
||||
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_MARK, mark); err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("set socket option: SO_MARK: %s", err)}
|
||||
}
|
||||
}
|
||||
|
||||
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)}
|
||||
}
|
||||
|
||||
syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
||||
|
||||
syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1)
|
||||
|
||||
if err = syscall.Bind(fd, sockaddr); err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("socket bind: %s", err)}
|
||||
}
|
||||
|
||||
fdFile := os.NewFile(uintptr(fd), fmt.Sprintf("net-udp-fake-%s", addr.String()))
|
||||
defer fdFile.Close()
|
||||
|
||||
packetConn, err := net.FilePacketConn(fdFile)
|
||||
if err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("convert file descriptor to connection: %s", err)}
|
||||
}
|
||||
|
||||
return packetConn, nil
|
||||
}
|
12
proxy/dokodemo/fakeudp_other.go
Normal file
12
proxy/dokodemo/fakeudp_other.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// +build !linux
|
||||
|
||||
package dokodemo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
func FakeUDP(addr *net.UDPAddr, mark int) (net.PacketConn, error) {
|
||||
return nil, &net.OpError{Op: "fake", Err: fmt.Errorf("!linux")}
|
||||
}
|
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/dns"
|
||||
"github.com/xtls/xray-core/features/policy"
|
||||
"github.com/xtls/xray-core/features/stats"
|
||||
"github.com/xtls/xray-core/transport"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
@@ -96,13 +97,16 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
return newError("target not specified.")
|
||||
}
|
||||
destination := outbound.Target
|
||||
UDPOverride := net.UDPDestination(nil, 0)
|
||||
if h.config.DestinationOverride != nil {
|
||||
server := h.config.DestinationOverride.Server
|
||||
if isValidAddress(server.Address) {
|
||||
destination.Address = server.Address.AsAddress()
|
||||
UDPOverride.Address = destination.Address
|
||||
}
|
||||
if server.Port != 0 {
|
||||
destination.Port = net.Port(server.Port)
|
||||
UDPOverride.Port = destination.Port
|
||||
}
|
||||
}
|
||||
newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx))
|
||||
@@ -148,7 +152,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
if destination.Network == net.Network_TCP {
|
||||
writer = buf.NewWriter(conn)
|
||||
} else {
|
||||
writer = &buf.SequentialWriter{Writer: conn}
|
||||
writer = NewPacketWriter(conn, h, ctx, UDPOverride)
|
||||
}
|
||||
|
||||
if err := buf.Copy(input, writer, buf.UpdateActivity(timer)); err != nil {
|
||||
@@ -165,7 +169,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
if destination.Network == net.Network_TCP {
|
||||
reader = buf.NewReader(conn)
|
||||
} else {
|
||||
reader = buf.NewPacketReader(conn)
|
||||
reader = NewPacketReader(conn, UDPOverride)
|
||||
}
|
||||
if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
|
||||
return newError("failed to process response").Base(err)
|
||||
@@ -180,3 +184,120 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader {
|
||||
iConn := conn
|
||||
statConn, ok := iConn.(*internet.StatCouterConnection)
|
||||
if ok {
|
||||
iConn = statConn.Connection
|
||||
}
|
||||
var counter stats.Counter
|
||||
if statConn != nil {
|
||||
counter = statConn.ReadCounter
|
||||
}
|
||||
if c, ok := iConn.(*internet.PacketConnWrapper); ok && UDPOverride.Address == nil && UDPOverride.Port == 0 {
|
||||
return &PacketReader{
|
||||
PacketConnWrapper: c,
|
||||
Counter: counter,
|
||||
}
|
||||
}
|
||||
return &buf.PacketReader{Reader: conn}
|
||||
}
|
||||
|
||||
type PacketReader struct {
|
||||
*internet.PacketConnWrapper
|
||||
stats.Counter
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
b := buf.New()
|
||||
b.Resize(0, buf.Size)
|
||||
n, d, err := r.PacketConnWrapper.ReadFrom(b.Bytes())
|
||||
if err != nil {
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
b.Resize(0, int32(n))
|
||||
b.UDP = &net.Destination{
|
||||
Address: net.IPAddress(d.(*net.UDPAddr).IP),
|
||||
Port: net.Port(d.(*net.UDPAddr).Port),
|
||||
Network: net.Network_UDP,
|
||||
}
|
||||
if r.Counter != nil {
|
||||
r.Counter.Add(int64(n))
|
||||
}
|
||||
return buf.MultiBuffer{b}, nil
|
||||
}
|
||||
|
||||
func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context, UDPOverride net.Destination) buf.Writer {
|
||||
iConn := conn
|
||||
statConn, ok := iConn.(*internet.StatCouterConnection)
|
||||
if ok {
|
||||
iConn = statConn.Connection
|
||||
}
|
||||
var counter stats.Counter
|
||||
if statConn != nil {
|
||||
counter = statConn.WriteCounter
|
||||
}
|
||||
if c, ok := iConn.(*internet.PacketConnWrapper); ok {
|
||||
return &PacketWriter{
|
||||
PacketConnWrapper: c,
|
||||
Counter: counter,
|
||||
Handler: h,
|
||||
Context: ctx,
|
||||
UDPOverride: UDPOverride,
|
||||
}
|
||||
}
|
||||
return &buf.SequentialWriter{Writer: conn}
|
||||
}
|
||||
|
||||
type PacketWriter struct {
|
||||
*internet.PacketConnWrapper
|
||||
stats.Counter
|
||||
*Handler
|
||||
context.Context
|
||||
UDPOverride net.Destination
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
for {
|
||||
mb2, b := buf.SplitFirst(mb)
|
||||
mb = mb2
|
||||
if b == nil {
|
||||
break
|
||||
}
|
||||
var n int
|
||||
var err error
|
||||
if b.UDP != nil {
|
||||
if w.UDPOverride.Address != nil {
|
||||
b.UDP.Address = w.UDPOverride.Address
|
||||
}
|
||||
if w.UDPOverride.Port != 0 {
|
||||
b.UDP.Port = w.UDPOverride.Port
|
||||
}
|
||||
if w.Handler.config.useIP() && b.UDP.Address.Family().IsDomain() {
|
||||
ip := w.Handler.resolveIP(w.Context, b.UDP.Address.Domain(), nil)
|
||||
if ip != nil {
|
||||
b.UDP.Address = ip
|
||||
}
|
||||
}
|
||||
destAddr, _ := net.ResolveUDPAddr("udp", b.UDP.NetAddr())
|
||||
if destAddr == nil {
|
||||
b.Release()
|
||||
continue
|
||||
}
|
||||
n, err = w.PacketConnWrapper.WriteTo(b.Bytes(), destAddr)
|
||||
} else {
|
||||
n, err = w.PacketConnWrapper.Write(b.Bytes())
|
||||
}
|
||||
b.Release()
|
||||
if err != nil {
|
||||
buf.ReleaseMulti(mb)
|
||||
return err
|
||||
}
|
||||
if w.Counter != nil {
|
||||
w.Counter.Add(int64(n))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -168,6 +168,7 @@ func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, u
|
||||
rawConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
rawConn.Close()
|
||||
|
@@ -293,6 +293,7 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri
|
||||
response.Close = true
|
||||
result = nil
|
||||
}
|
||||
defer response.Body.Close()
|
||||
} else {
|
||||
newError("failed to read response from ", request.Host).Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
||||
response = &http.Response{
|
||||
|
@@ -134,14 +134,15 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
||||
}
|
||||
|
||||
if request.Command == protocol.RequestCommandUDP {
|
||||
writer := &buf.SequentialWriter{Writer: &UDPWriter{
|
||||
Writer: conn,
|
||||
Request: request,
|
||||
}}
|
||||
|
||||
requestDone := func() error {
|
||||
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
|
||||
|
||||
writer := &UDPWriter{
|
||||
Writer: conn,
|
||||
Request: request,
|
||||
}
|
||||
|
||||
if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil {
|
||||
return newError("failed to transport all UDP request").Base(err)
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@ import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
@@ -31,6 +33,31 @@ func (a *MemoryAccount) Equals(another protocol.Account) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *MemoryAccount) GetCipherName() string {
|
||||
switch a.Cipher.(type) {
|
||||
case *AesCfb:
|
||||
keyBytes := a.Cipher.(*AesCfb).KeyBytes
|
||||
return "AES_" + strconv.FormatInt(int64(keyBytes*8), 10) + "_CFB"
|
||||
case *ChaCha20:
|
||||
if a.Cipher.(*ChaCha20).IVBytes == 8 {
|
||||
return "CHACHA20"
|
||||
}
|
||||
return "CHACHA20_IETF"
|
||||
case *AEADCipher:
|
||||
switch reflect.ValueOf(a.Cipher.(*AEADCipher).AEADAuthCreator).Pointer() {
|
||||
case reflect.ValueOf(createAesGcm).Pointer():
|
||||
keyBytes := a.Cipher.(*AEADCipher).KeyBytes
|
||||
return "AES_" + strconv.FormatInt(int64(keyBytes*8), 10) + "_GCM"
|
||||
case reflect.ValueOf(createChacha20Poly1305).Pointer():
|
||||
return "CHACHA20_POLY1305"
|
||||
}
|
||||
case *NoneCipher:
|
||||
return "NONE"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func createAesGcm(key []byte) cipher.AEAD {
|
||||
block, err := aes.NewCipher(key)
|
||||
common.Must(err)
|
||||
|
@@ -154,13 +154,8 @@ type ServerConfig struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// UdpEnabled specified whether or not to enable UDP for Shadowsocks.
|
||||
// Deprecated. Use 'network' field.
|
||||
//
|
||||
// Deprecated: Do not use.
|
||||
UdpEnabled bool `protobuf:"varint,1,opt,name=udp_enabled,json=udpEnabled,proto3" json:"udp_enabled,omitempty"`
|
||||
User *protocol.User `protobuf:"bytes,2,opt,name=user,proto3" json:"user,omitempty"`
|
||||
Network []net.Network `protobuf:"varint,3,rep,packed,name=network,proto3,enum=xray.common.net.Network" json:"network,omitempty"`
|
||||
Users []*protocol.User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"`
|
||||
Network []net.Network `protobuf:"varint,2,rep,packed,name=network,proto3,enum=xray.common.net.Network" json:"network,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ServerConfig) Reset() {
|
||||
@@ -195,17 +190,9 @@ func (*ServerConfig) Descriptor() ([]byte, []int) {
|
||||
return file_proxy_shadowsocks_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
// Deprecated: Do not use.
|
||||
func (x *ServerConfig) GetUdpEnabled() bool {
|
||||
func (x *ServerConfig) GetUsers() []*protocol.User {
|
||||
if x != nil {
|
||||
return x.UdpEnabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *ServerConfig) GetUser() *protocol.User {
|
||||
if x != nil {
|
||||
return x.User
|
||||
return x.Users
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -282,39 +269,37 @@ var file_proxy_shadowsocks_config_proto_rawDesc = []byte{
|
||||
0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f,
|
||||
0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x54, 0x79, 0x70,
|
||||
0x65, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x22, 0x97, 0x01,
|
||||
0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23,
|
||||
0x0a, 0x0b, 0x75, 0x64, 0x70, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0a, 0x75, 0x64, 0x70, 0x45, 0x6e, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75,
|
||||
0x73, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x03,
|
||||
0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
|
||||
0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07,
|
||||
0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x22, 0x4c, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e,
|
||||
0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65,
|
||||
0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
|
||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53,
|
||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73,
|
||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x2a, 0x9f, 0x01, 0x0a, 0x0a, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72,
|
||||
0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
|
||||
0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x32, 0x38, 0x5f, 0x43, 0x46, 0x42,
|
||||
0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x32, 0x35, 0x36, 0x5f, 0x43, 0x46,
|
||||
0x42, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x10,
|
||||
0x03, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x49, 0x45,
|
||||
0x54, 0x46, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x32, 0x38, 0x5f,
|
||||
0x47, 0x43, 0x4d, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x32, 0x35, 0x36,
|
||||
0x5f, 0x47, 0x43, 0x4d, 0x10, 0x06, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41,
|
||||
0x32, 0x30, 0x5f, 0x50, 0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x10, 0x07, 0x12, 0x08, 0x0a,
|
||||
0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x08, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77,
|
||||
0x73, 0x6f, 0x63, 0x6b, 0x73, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73,
|
||||
0x6f, 0x63, 0x6b, 0x73, 0xaa, 0x02, 0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78,
|
||||
0x79, 0x2e, 0x53, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x65, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x22, 0x74, 0x0a,
|
||||
0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x30, 0x0a,
|
||||
0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78,
|
||||
0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x12,
|
||||
0x32, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e,
|
||||
0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e,
|
||||
0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77,
|
||||
0x6f, 0x72, 0x6b, 0x22, 0x4c, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
|
||||
0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65,
|
||||
0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65,
|
||||
0x72, 0x2a, 0x9f, 0x01, 0x0a, 0x0a, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65,
|
||||
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a,
|
||||
0x0b, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x32, 0x38, 0x5f, 0x43, 0x46, 0x42, 0x10, 0x01, 0x12, 0x0f,
|
||||
0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x32, 0x35, 0x36, 0x5f, 0x43, 0x46, 0x42, 0x10, 0x02, 0x12,
|
||||
0x0c, 0x0a, 0x08, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x10, 0x03, 0x12, 0x11, 0x0a,
|
||||
0x0d, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x49, 0x45, 0x54, 0x46, 0x10, 0x04,
|
||||
0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x32, 0x38, 0x5f, 0x47, 0x43, 0x4d, 0x10,
|
||||
0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x32, 0x35, 0x36, 0x5f, 0x47, 0x43, 0x4d,
|
||||
0x10, 0x06, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x50,
|
||||
0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x10, 0x07, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e,
|
||||
0x45, 0x10, 0x08, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b,
|
||||
0x73, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
||||
0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70,
|
||||
0x72, 0x6f, 0x78, 0x79, 0x2f, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73,
|
||||
0xaa, 0x02, 0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x53, 0x68,
|
||||
0x61, 0x64, 0x6f, 0x77, 0x73, 0x6f, 0x63, 0x6b, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -342,7 +327,7 @@ var file_proxy_shadowsocks_config_proto_goTypes = []interface{}{
|
||||
}
|
||||
var file_proxy_shadowsocks_config_proto_depIdxs = []int32{
|
||||
0, // 0: xray.proxy.shadowsocks.Account.cipher_type:type_name -> xray.proxy.shadowsocks.CipherType
|
||||
4, // 1: xray.proxy.shadowsocks.ServerConfig.user:type_name -> xray.common.protocol.User
|
||||
4, // 1: xray.proxy.shadowsocks.ServerConfig.users:type_name -> xray.common.protocol.User
|
||||
5, // 2: xray.proxy.shadowsocks.ServerConfig.network:type_name -> xray.common.net.Network
|
||||
6, // 3: xray.proxy.shadowsocks.ClientConfig.server:type_name -> xray.common.protocol.ServerEndpoint
|
||||
4, // [4:4] is the sub-list for method output_type
|
||||
|
@@ -28,11 +28,8 @@ enum CipherType {
|
||||
}
|
||||
|
||||
message ServerConfig {
|
||||
// UdpEnabled specified whether or not to enable UDP for Shadowsocks.
|
||||
// Deprecated. Use 'network' field.
|
||||
bool udp_enabled = 1 [deprecated = true];
|
||||
xray.common.protocol.User user = 2;
|
||||
repeated xray.common.net.Network network = 3;
|
||||
repeated xray.common.protocol.User users = 1;
|
||||
repeated xray.common.net.Network network = 2;
|
||||
}
|
||||
|
||||
message ClientConfig {
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package shadowsocks
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
@@ -28,12 +30,33 @@ var addrParser = protocol.NewAddressParser(
|
||||
}),
|
||||
)
|
||||
|
||||
type FullReader struct {
|
||||
reader io.Reader
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
func (r *FullReader) Read(p []byte) (n int, err error) {
|
||||
if r.buffer != nil {
|
||||
n := copy(p, r.buffer)
|
||||
if n == len(r.buffer) {
|
||||
r.buffer = nil
|
||||
} else {
|
||||
r.buffer = r.buffer[n:]
|
||||
}
|
||||
if n == len(p) {
|
||||
return n, nil
|
||||
} else {
|
||||
m, err := r.reader.Read(p[n:])
|
||||
return n + m, err
|
||||
}
|
||||
}
|
||||
return r.reader.Read(p)
|
||||
}
|
||||
|
||||
// ReadTCPSession reads a Shadowsocks TCP session from the given reader, returns its header and remaining parts.
|
||||
func ReadTCPSession(user *protocol.MemoryUser, reader io.Reader) (*protocol.RequestHeader, buf.Reader, error) {
|
||||
account := user.Account.(*MemoryAccount)
|
||||
func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHeader, buf.Reader, error) {
|
||||
|
||||
hashkdf := hmac.New(sha256.New, []byte("SSBSKDF"))
|
||||
hashkdf.Write(account.Key)
|
||||
|
||||
behaviorSeed := crc32.ChecksumIEEE(hashkdf.Sum(nil))
|
||||
|
||||
@@ -44,28 +67,69 @@ func ReadTCPSession(user *protocol.MemoryUser, reader io.Reader) (*protocol.Requ
|
||||
DrainSize := BaseDrainSize + 16 + 38 + RandDrainRolled
|
||||
readSizeRemain := DrainSize
|
||||
|
||||
var r2 buf.Reader
|
||||
buffer := buf.New()
|
||||
defer buffer.Release()
|
||||
|
||||
ivLen := account.Cipher.IVSize()
|
||||
var iv []byte
|
||||
if ivLen > 0 {
|
||||
if _, err := buffer.ReadFullFrom(reader, ivLen); err != nil {
|
||||
readSizeRemain -= int(buffer.Len())
|
||||
DrainConnN(reader, readSizeRemain)
|
||||
return nil, nil, newError("failed to read IV").Base(err)
|
||||
}
|
||||
var user *protocol.MemoryUser
|
||||
var ivLen int32
|
||||
var err error
|
||||
|
||||
iv = append([]byte(nil), buffer.BytesTo(ivLen)...)
|
||||
}
|
||||
|
||||
r, err := account.Cipher.NewDecryptionReader(account.Key, iv, reader)
|
||||
if err != nil {
|
||||
count := validator.Count()
|
||||
if count == 0 {
|
||||
readSizeRemain -= int(buffer.Len())
|
||||
DrainConnN(reader, readSizeRemain)
|
||||
return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError()
|
||||
return nil, nil, newError("invalid user")
|
||||
} else if count > 1 {
|
||||
var aead cipher.AEAD
|
||||
|
||||
if _, err := buffer.ReadFullFrom(reader, 50); err != nil {
|
||||
readSizeRemain -= int(buffer.Len())
|
||||
DrainConnN(reader, readSizeRemain)
|
||||
return nil, nil, newError("failed to read 50 bytes").Base(err)
|
||||
}
|
||||
|
||||
bs := buffer.Bytes()
|
||||
user, aead, _, ivLen, err = validator.Get(bs, protocol.RequestCommandTCP)
|
||||
|
||||
if user != nil {
|
||||
reader = &FullReader{reader, bs[ivLen:]}
|
||||
auth := &crypto.AEADAuthenticator{
|
||||
AEAD: aead,
|
||||
NonceGenerator: crypto.GenerateInitialAEADNonce(),
|
||||
}
|
||||
r2 = crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{
|
||||
Auth: auth,
|
||||
}, reader, protocol.TransferTypeStream, nil)
|
||||
} else {
|
||||
readSizeRemain -= int(buffer.Len())
|
||||
DrainConnN(reader, readSizeRemain)
|
||||
return nil, nil, newError("failed to match an user").Base(err)
|
||||
}
|
||||
} else {
|
||||
user, ivLen = validator.GetOnlyUser()
|
||||
account := user.Account.(*MemoryAccount)
|
||||
hashkdf.Write(account.Key)
|
||||
var iv []byte
|
||||
if ivLen > 0 {
|
||||
if _, err := buffer.ReadFullFrom(reader, ivLen); err != nil {
|
||||
readSizeRemain -= int(buffer.Len())
|
||||
DrainConnN(reader, readSizeRemain)
|
||||
return nil, nil, newError("failed to read IV").Base(err)
|
||||
}
|
||||
iv = append([]byte(nil), buffer.BytesTo(ivLen)...)
|
||||
}
|
||||
|
||||
r, err := account.Cipher.NewDecryptionReader(account.Key, iv, reader)
|
||||
if err != nil {
|
||||
readSizeRemain -= int(buffer.Len())
|
||||
DrainConnN(reader, readSizeRemain)
|
||||
return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError()
|
||||
}
|
||||
r2 = r
|
||||
}
|
||||
br := &buf.BufferedReader{Reader: r}
|
||||
|
||||
br := &buf.BufferedReader{Reader: r2}
|
||||
|
||||
request := &protocol.RequestHeader{
|
||||
Version: Version,
|
||||
@@ -185,18 +249,41 @@ func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buff
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
func DecodeUDPPacket(user *protocol.MemoryUser, payload *buf.Buffer) (*protocol.RequestHeader, *buf.Buffer, error) {
|
||||
account := user.Account.(*MemoryAccount)
|
||||
|
||||
var iv []byte
|
||||
if !account.Cipher.IsAEAD() && account.Cipher.IVSize() > 0 {
|
||||
// Keep track of IV as it gets removed from payload in DecodePacket.
|
||||
iv = make([]byte, account.Cipher.IVSize())
|
||||
copy(iv, payload.BytesTo(account.Cipher.IVSize()))
|
||||
func DecodeUDPPacket(validator *Validator, payload *buf.Buffer) (*protocol.RequestHeader, *buf.Buffer, error) {
|
||||
bs := payload.Bytes()
|
||||
if len(bs) <= 32 {
|
||||
return nil, nil, newError("len(bs) <= 32")
|
||||
}
|
||||
|
||||
if err := account.Cipher.DecodePacket(account.Key, payload); err != nil {
|
||||
return nil, nil, newError("failed to decrypt UDP payload").Base(err)
|
||||
var user *protocol.MemoryUser
|
||||
var err error
|
||||
|
||||
count := validator.Count()
|
||||
if count == 0 {
|
||||
return nil, nil, newError("invalid user")
|
||||
} else if count > 1 {
|
||||
var d []byte
|
||||
user, _, d, _, err = validator.Get(bs, protocol.RequestCommandUDP)
|
||||
|
||||
if user != nil {
|
||||
payload.Clear()
|
||||
payload.Write(d)
|
||||
} else {
|
||||
return nil, nil, newError("failed to decrypt UDP payload").Base(err)
|
||||
}
|
||||
} else {
|
||||
user, _ = validator.GetOnlyUser()
|
||||
account := user.Account.(*MemoryAccount)
|
||||
|
||||
var iv []byte
|
||||
if !account.Cipher.IsAEAD() && account.Cipher.IVSize() > 0 {
|
||||
// Keep track of IV as it gets removed from payload in DecodePacket.
|
||||
iv = make([]byte, account.Cipher.IVSize())
|
||||
copy(iv, payload.BytesTo(account.Cipher.IVSize()))
|
||||
}
|
||||
if err = account.Cipher.DecodePacket(account.Key, payload); err != nil {
|
||||
return nil, nil, newError("failed to decrypt UDP payload").Base(err)
|
||||
}
|
||||
}
|
||||
|
||||
request := &protocol.RequestHeader{
|
||||
@@ -230,11 +317,16 @@ func (v *UDPReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
buffer.Release()
|
||||
return nil, err
|
||||
}
|
||||
_, payload, err := DecodeUDPPacket(v.User, buffer)
|
||||
validator := new(Validator)
|
||||
validator.Add(v.User)
|
||||
|
||||
u, payload, err := DecodeUDPPacket(validator, buffer)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return nil, err
|
||||
}
|
||||
dest := u.Destination()
|
||||
payload.UDP = &dest
|
||||
return buf.MultiBuffer{payload}, nil
|
||||
}
|
||||
|
||||
@@ -243,13 +335,33 @@ type UDPWriter struct {
|
||||
Request *protocol.RequestHeader
|
||||
}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (w *UDPWriter) Write(payload []byte) (int, error) {
|
||||
packet, err := EncodeUDPPacket(w.Request, payload)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
func (w *UDPWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
for {
|
||||
mb2, b := buf.SplitFirst(mb)
|
||||
mb = mb2
|
||||
if b == nil {
|
||||
break
|
||||
}
|
||||
request := w.Request
|
||||
if b.UDP != nil {
|
||||
request = &protocol.RequestHeader{
|
||||
User: w.Request.User,
|
||||
Address: b.UDP.Address,
|
||||
Port: b.UDP.Port,
|
||||
}
|
||||
}
|
||||
packet, err := EncodeUDPPacket(request, b.Bytes())
|
||||
b.Release()
|
||||
if err != nil {
|
||||
buf.ReleaseMulti(mb)
|
||||
return err
|
||||
}
|
||||
_, err = w.Writer.Write(packet.Bytes())
|
||||
packet.Release()
|
||||
if err != nil {
|
||||
buf.ReleaseMulti(mb)
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = w.Writer.Write(packet.Bytes())
|
||||
packet.Release()
|
||||
return len(payload), err
|
||||
return nil
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ func TestUDPEncoding(t *testing.T) {
|
||||
Email: "love@example.com",
|
||||
Account: toAccount(&Account{
|
||||
Password: "shadowsocks-password",
|
||||
CipherType: CipherType_AES_128_CFB,
|
||||
CipherType: CipherType_AES_128_GCM,
|
||||
}),
|
||||
},
|
||||
}
|
||||
@@ -38,14 +38,16 @@ func TestUDPEncoding(t *testing.T) {
|
||||
encodedData, err := EncodeUDPPacket(request, data.Bytes())
|
||||
common.Must(err)
|
||||
|
||||
decodedRequest, decodedData, err := DecodeUDPPacket(request.User, encodedData)
|
||||
validator := new(Validator)
|
||||
validator.Add(request.User)
|
||||
decodedRequest, decodedData, err := DecodeUDPPacket(validator, encodedData)
|
||||
common.Must(err)
|
||||
|
||||
if r := cmp.Diff(decodedData.Bytes(), data.Bytes()); r != "" {
|
||||
t.Error("data: ", r)
|
||||
}
|
||||
|
||||
if r := cmp.Diff(decodedRequest, request); r != "" {
|
||||
if r := cmp.Diff(decodedRequest, request, cmp.Comparer(func(a1, a2 protocol.Account) bool { return a1.Equals(a2) })); r != "" {
|
||||
t.Error("request: ", r)
|
||||
}
|
||||
}
|
||||
@@ -65,7 +67,7 @@ func TestTCPRequest(t *testing.T) {
|
||||
Email: "love@example.com",
|
||||
Account: toAccount(&Account{
|
||||
Password: "tcp-password",
|
||||
CipherType: CipherType_CHACHA20,
|
||||
CipherType: CipherType_CHACHA20_POLY1305,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -81,7 +83,7 @@ func TestTCPRequest(t *testing.T) {
|
||||
Email: "love@example.com",
|
||||
Account: toAccount(&Account{
|
||||
Password: "password",
|
||||
CipherType: CipherType_AES_256_CFB,
|
||||
CipherType: CipherType_AES_256_GCM,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -97,7 +99,7 @@ func TestTCPRequest(t *testing.T) {
|
||||
Email: "love@example.com",
|
||||
Account: toAccount(&Account{
|
||||
Password: "password",
|
||||
CipherType: CipherType_CHACHA20_IETF,
|
||||
CipherType: CipherType_AES_128_GCM,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -117,9 +119,11 @@ func TestTCPRequest(t *testing.T) {
|
||||
|
||||
common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{data}))
|
||||
|
||||
decodedRequest, reader, err := ReadTCPSession(request.User, cache)
|
||||
validator := new(Validator)
|
||||
validator.Add(request.User)
|
||||
decodedRequest, reader, err := ReadTCPSession(validator, cache)
|
||||
common.Must(err)
|
||||
if r := cmp.Diff(decodedRequest, request); r != "" {
|
||||
if r := cmp.Diff(decodedRequest, request, cmp.Comparer(func(a1, a2 protocol.Account) bool { return a1.Equals(a2) })); r != "" {
|
||||
t.Error("request: ", r)
|
||||
}
|
||||
|
||||
@@ -139,13 +143,13 @@ func TestUDPReaderWriter(t *testing.T) {
|
||||
user := &protocol.MemoryUser{
|
||||
Account: toAccount(&Account{
|
||||
Password: "test-password",
|
||||
CipherType: CipherType_CHACHA20_IETF,
|
||||
CipherType: CipherType_CHACHA20_POLY1305,
|
||||
}),
|
||||
}
|
||||
cache := buf.New()
|
||||
defer cache.Release()
|
||||
|
||||
writer := &buf.SequentialWriter{Writer: &UDPWriter{
|
||||
writer := &UDPWriter{
|
||||
Writer: cache,
|
||||
Request: &protocol.RequestHeader{
|
||||
Version: Version,
|
||||
@@ -153,7 +157,7 @@ func TestUDPReaderWriter(t *testing.T) {
|
||||
Port: 123,
|
||||
User: user,
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
reader := &UDPReader{
|
||||
Reader: cache,
|
||||
|
@@ -22,39 +22,51 @@ import (
|
||||
|
||||
type Server struct {
|
||||
config *ServerConfig
|
||||
user *protocol.MemoryUser
|
||||
validator *Validator
|
||||
policyManager policy.Manager
|
||||
cone bool
|
||||
}
|
||||
|
||||
// NewServer create a new Shadowsocks server.
|
||||
func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
|
||||
if config.GetUser() == nil {
|
||||
return nil, newError("user is not specified")
|
||||
}
|
||||
validator := new(Validator)
|
||||
for _, user := range config.Users {
|
||||
u, err := user.ToMemoryUser()
|
||||
if err != nil {
|
||||
return nil, newError("failed to get shadowsocks user").Base(err).AtError()
|
||||
}
|
||||
|
||||
mUser, err := config.User.ToMemoryUser()
|
||||
if err != nil {
|
||||
return nil, newError("failed to parse user account").Base(err)
|
||||
if err := validator.Add(u); err != nil {
|
||||
return nil, newError("failed to add user").Base(err).AtError()
|
||||
}
|
||||
}
|
||||
|
||||
v := core.MustFromContext(ctx)
|
||||
s := &Server{
|
||||
config: config,
|
||||
user: mUser,
|
||||
validator: validator,
|
||||
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
||||
cone: ctx.Value("cone").(bool),
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// AddUser implements proxy.UserManager.AddUser().
|
||||
func (s *Server) AddUser(ctx context.Context, u *protocol.MemoryUser) error {
|
||||
return s.validator.Add(u)
|
||||
}
|
||||
|
||||
// RemoveUser implements proxy.UserManager.RemoveUser().
|
||||
func (s *Server) RemoveUser(ctx context.Context, e string) error {
|
||||
return s.validator.Del(e)
|
||||
}
|
||||
|
||||
func (s *Server) Network() []net.Network {
|
||||
list := s.config.Network
|
||||
if len(list) == 0 {
|
||||
list = append(list, net.Network_TCP)
|
||||
}
|
||||
if s.config.UdpEnabled {
|
||||
list = append(list, net.Network_UDP)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
@@ -63,13 +75,13 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn internet
|
||||
case net.Network_TCP:
|
||||
return s.handleConnection(ctx, conn, dispatcher)
|
||||
case net.Network_UDP:
|
||||
return s.handlerUDPPayload(ctx, conn, dispatcher)
|
||||
return s.handleUDPPayload(ctx, conn, dispatcher)
|
||||
default:
|
||||
return newError("unknown network: ", network)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error {
|
||||
func (s *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error {
|
||||
udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
||||
request := protocol.RequestHeaderFromContext(ctx)
|
||||
if request == nil {
|
||||
@@ -77,6 +89,15 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection
|
||||
}
|
||||
|
||||
payload := packet.Payload
|
||||
|
||||
if payload.UDP != nil {
|
||||
request = &protocol.RequestHeader{
|
||||
User: request.User,
|
||||
Address: payload.UDP.Address,
|
||||
Port: payload.UDP.Port,
|
||||
}
|
||||
}
|
||||
|
||||
data, err := EncodeUDPPacket(request, payload.Bytes())
|
||||
payload.Release()
|
||||
if err != nil {
|
||||
@@ -92,7 +113,12 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection
|
||||
if inbound == nil {
|
||||
panic("no inbound metadata")
|
||||
}
|
||||
inbound.User = s.user
|
||||
|
||||
if s.validator.Count() == 1 {
|
||||
inbound.User, _ = s.validator.GetOnlyUser()
|
||||
}
|
||||
|
||||
var dest *net.Destination
|
||||
|
||||
reader := buf.NewPacketReader(conn)
|
||||
for {
|
||||
@@ -102,9 +128,23 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection
|
||||
}
|
||||
|
||||
for _, payload := range mpayload {
|
||||
request, data, err := DecodeUDPPacket(s.user, payload)
|
||||
var request *protocol.RequestHeader
|
||||
var data *buf.Buffer
|
||||
var err error
|
||||
|
||||
if inbound.User != nil {
|
||||
validator := new(Validator)
|
||||
validator.Add(inbound.User)
|
||||
request, data, err = DecodeUDPPacket(validator, payload)
|
||||
} else {
|
||||
request, data, err = DecodeUDPPacket(s.validator, payload)
|
||||
if err == nil {
|
||||
inbound.User = request.User
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Source.IsValid() {
|
||||
if inbound.Source.IsValid() {
|
||||
newError("dropping invalid UDP packet from: ", inbound.Source).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
log.Record(&log.AccessMessage{
|
||||
From: inbound.Source,
|
||||
@@ -117,21 +157,28 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection
|
||||
continue
|
||||
}
|
||||
|
||||
destination := request.Destination()
|
||||
|
||||
currentPacketCtx := ctx
|
||||
dest := request.Destination()
|
||||
if inbound.Source.IsValid() {
|
||||
currentPacketCtx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
||||
From: inbound.Source,
|
||||
To: dest,
|
||||
To: destination,
|
||||
Status: log.AccessAccepted,
|
||||
Reason: "",
|
||||
Email: request.User.Email,
|
||||
})
|
||||
}
|
||||
newError("tunnelling request to ", dest).WriteToLog(session.ExportIDToError(currentPacketCtx))
|
||||
newError("tunnelling request to ", destination).WriteToLog(session.ExportIDToError(currentPacketCtx))
|
||||
|
||||
data.UDP = &destination
|
||||
|
||||
if !s.cone || dest == nil {
|
||||
dest = &destination
|
||||
}
|
||||
|
||||
currentPacketCtx = protocol.ContextWithRequestHeader(currentPacketCtx, request)
|
||||
udpServer.Dispatch(currentPacketCtx, dest, data)
|
||||
udpServer.Dispatch(currentPacketCtx, *dest, data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,11 +186,13 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection
|
||||
}
|
||||
|
||||
func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error {
|
||||
sessionPolicy := s.policyManager.ForLevel(s.user.Level)
|
||||
conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake))
|
||||
sessionPolicy := s.policyManager.ForLevel(0)
|
||||
if err := conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil {
|
||||
return newError("unable to set read deadline").Base(err).AtWarning()
|
||||
}
|
||||
|
||||
bufferedReader := buf.BufferedReader{Reader: buf.NewReader(conn)}
|
||||
request, bodyReader, err := ReadTCPSession(s.user, &bufferedReader)
|
||||
request, bodyReader, err := ReadTCPSession(s.validator, &bufferedReader)
|
||||
if err != nil {
|
||||
log.Record(&log.AccessMessage{
|
||||
From: conn.RemoteAddr(),
|
||||
@@ -159,7 +208,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
|
||||
if inbound == nil {
|
||||
panic("no inbound metadata")
|
||||
}
|
||||
inbound.User = s.user
|
||||
inbound.User = request.User
|
||||
|
||||
dest := request.Destination()
|
||||
ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
|
||||
@@ -171,6 +220,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
|
||||
})
|
||||
newError("tunnelling request to ", dest).WriteToLog(session.ExportIDToError(ctx))
|
||||
|
||||
sessionPolicy = s.policyManager.ForLevel(request.User.Level)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
|
||||
|
||||
|
113
proxy/shadowsocks/validator.go
Normal file
113
proxy/shadowsocks/validator.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package shadowsocks
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
)
|
||||
|
||||
// Validator stores valid Shadowsocks users.
|
||||
type Validator struct {
|
||||
// Considering email's usage here, map + sync.Mutex/RWMutex may have better performance.
|
||||
email sync.Map
|
||||
users sync.Map
|
||||
}
|
||||
|
||||
// Add a Shadowsocks user, Email must be empty or unique.
|
||||
func (v *Validator) Add(u *protocol.MemoryUser) error {
|
||||
account := u.Account.(*MemoryAccount)
|
||||
|
||||
if !account.Cipher.IsAEAD() && v.Count() > 0 {
|
||||
return newError("The cipher do not support Single-port Multi-user")
|
||||
}
|
||||
|
||||
if u.Email != "" {
|
||||
_, loaded := v.email.LoadOrStore(strings.ToLower(u.Email), u)
|
||||
if loaded {
|
||||
return newError("User ", u.Email, " already exists.")
|
||||
}
|
||||
}
|
||||
|
||||
v.users.Store(string(account.Key)+"&"+account.GetCipherName(), u)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Del a Shadowsocks user with a non-empty Email.
|
||||
func (v *Validator) Del(e string) error {
|
||||
if e == "" {
|
||||
return newError("Email must not be empty.")
|
||||
}
|
||||
le := strings.ToLower(e)
|
||||
u, _ := v.email.Load(le)
|
||||
if u == nil {
|
||||
return newError("User ", e, " not found.")
|
||||
}
|
||||
account := u.(*protocol.MemoryUser).Account.(*MemoryAccount)
|
||||
v.email.Delete(le)
|
||||
v.users.Delete(string(account.Key) + "&" + account.GetCipherName())
|
||||
return nil
|
||||
}
|
||||
|
||||
// Count the number of Shadowsocks users
|
||||
func (v *Validator) Count() int {
|
||||
length := 0
|
||||
v.users.Range(func(_, _ interface{}) bool {
|
||||
length++
|
||||
|
||||
return true
|
||||
})
|
||||
return length
|
||||
}
|
||||
|
||||
// Get a Shadowsocks user and the user's cipher.
|
||||
func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol.MemoryUser, aead cipher.AEAD, ret []byte, ivLen int32, err error) {
|
||||
var dataSize int
|
||||
|
||||
switch command {
|
||||
case protocol.RequestCommandTCP:
|
||||
dataSize = 16
|
||||
case protocol.RequestCommandUDP:
|
||||
dataSize = 8192
|
||||
}
|
||||
|
||||
var aeadCipher *AEADCipher
|
||||
subkey := make([]byte, 32)
|
||||
data := make([]byte, dataSize)
|
||||
|
||||
v.users.Range(func(key, user interface{}) bool {
|
||||
account := user.(*protocol.MemoryUser).Account.(*MemoryAccount)
|
||||
aeadCipher = account.Cipher.(*AEADCipher)
|
||||
ivLen = aeadCipher.IVSize()
|
||||
subkey = subkey[:aeadCipher.KeyBytes]
|
||||
hkdfSHA1(account.Key, bs[:ivLen], subkey)
|
||||
aead = aeadCipher.AEADAuthCreator(subkey)
|
||||
|
||||
switch command {
|
||||
case protocol.RequestCommandTCP:
|
||||
ret, err = aead.Open(data[:0], data[4:16], bs[ivLen:ivLen+18], nil)
|
||||
case protocol.RequestCommandUDP:
|
||||
ret, err = aead.Open(data[:0], data[8180:8192], bs[ivLen:], nil)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
u = user.(*protocol.MemoryUser)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Get the only user without authentication
|
||||
func (v *Validator) GetOnlyUser() (u *protocol.MemoryUser, ivLen int32) {
|
||||
v.users.Range(func(_, user interface{}) bool {
|
||||
u = user.(*protocol.MemoryUser)
|
||||
return false
|
||||
})
|
||||
ivLen = u.Account.(*MemoryAccount).Cipher.IVSize()
|
||||
|
||||
return
|
||||
}
|
@@ -51,14 +51,19 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
||||
if outbound == nil || !outbound.Target.IsValid() {
|
||||
return newError("target not specified.")
|
||||
}
|
||||
// Destination of the inner request.
|
||||
destination := outbound.Target
|
||||
|
||||
// Outbound server.
|
||||
var server *protocol.ServerSpec
|
||||
// Outbound server's destination.
|
||||
var dest net.Destination
|
||||
// Connection to the outbound server.
|
||||
var conn internet.Connection
|
||||
|
||||
if err := retry.ExponentialBackoff(5, 100).On(func() error {
|
||||
server = c.serverPicker.PickServer()
|
||||
dest := server.Destination()
|
||||
dest = server.Destination()
|
||||
rawConn, err := dialer.Dial(ctx, dest)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -101,6 +106,11 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
||||
if err != nil {
|
||||
return newError("failed to establish connection to server").AtWarning().Base(err)
|
||||
}
|
||||
if udpRequest != nil {
|
||||
if udpRequest.Address == net.AnyIP || udpRequest.Address == net.AnyIPv6 {
|
||||
udpRequest.Address = dest.Address
|
||||
}
|
||||
}
|
||||
|
||||
if err := conn.SetDeadline(time.Time{}); err != nil {
|
||||
newError("failed to clear deadline after handshake").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||
@@ -128,11 +138,12 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
|
||||
defer udpConn.Close()
|
||||
requestFunc = func() error {
|
||||
defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
|
||||
return buf.Copy(link.Reader, &buf.SequentialWriter{Writer: NewUDPWriter(request, udpConn)}, buf.UpdateActivity(timer))
|
||||
writer := &UDPWriter{Writer: udpConn, Request: request}
|
||||
return buf.Copy(link.Reader, writer, buf.UpdateActivity(timer))
|
||||
}
|
||||
responseFunc = func() error {
|
||||
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
|
||||
reader := &UDPReader{reader: udpConn}
|
||||
reader := &UDPReader{Reader: udpConn}
|
||||
return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ const (
|
||||
|
||||
cmdTCPConnect = 0x01
|
||||
cmdTCPBind = 0x02
|
||||
cmdUDPPort = 0x03
|
||||
cmdUDPAssociate = 0x03
|
||||
cmdTorResolve = 0xF0
|
||||
cmdTorResolvePTR = 0xF1
|
||||
|
||||
@@ -39,8 +39,10 @@ var addrParser = protocol.NewAddressParser(
|
||||
)
|
||||
|
||||
type ServerSession struct {
|
||||
config *ServerConfig
|
||||
port net.Port
|
||||
config *ServerConfig
|
||||
address net.Address
|
||||
port net.Port
|
||||
localAddress net.Address
|
||||
}
|
||||
|
||||
func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
|
||||
@@ -162,7 +164,7 @@ func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Wri
|
||||
case cmdTCPConnect, cmdTorResolve, cmdTorResolvePTR:
|
||||
// We don't have a solution for Tor case now. Simply treat it as connect command.
|
||||
request.Command = protocol.RequestCommandTCP
|
||||
case cmdUDPPort:
|
||||
case cmdUDPAssociate:
|
||||
if !s.config.UdpEnabled {
|
||||
writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0))
|
||||
return nil, newError("UDP is not enabled.")
|
||||
@@ -185,15 +187,17 @@ func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Wri
|
||||
request.Address = addr
|
||||
request.Port = port
|
||||
|
||||
responseAddress := net.AnyIP
|
||||
responsePort := net.Port(1717)
|
||||
responseAddress := s.address
|
||||
responsePort := s.port
|
||||
//nolint:gocritic // Use if else chain for clarity
|
||||
if request.Command == protocol.RequestCommandUDP {
|
||||
addr := s.config.Address.AsAddress()
|
||||
if addr == nil {
|
||||
addr = net.LocalHostIP
|
||||
if s.config.Address != nil {
|
||||
// Use configured IP as remote address in the response to UDP Associate
|
||||
responseAddress = s.config.Address.AsAddress()
|
||||
} else {
|
||||
// Use conn.LocalAddr() IP as remote address in the response by default
|
||||
responseAddress = s.localAddress
|
||||
}
|
||||
responseAddress = addr
|
||||
responsePort = s.port
|
||||
}
|
||||
if err := writeSocks5Response(writer, statusSuccess, responseAddress, responsePort); err != nil {
|
||||
return nil, err
|
||||
@@ -353,47 +357,59 @@ func EncodeUDPPacket(request *protocol.RequestHeader, data []byte) (*buf.Buffer,
|
||||
}
|
||||
|
||||
type UDPReader struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func NewUDPReader(reader io.Reader) *UDPReader {
|
||||
return &UDPReader{reader: reader}
|
||||
Reader io.Reader
|
||||
}
|
||||
|
||||
func (r *UDPReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
b := buf.New()
|
||||
if _, err := b.ReadFrom(r.reader); err != nil {
|
||||
buffer := buf.New()
|
||||
_, err := buffer.ReadFrom(r.Reader)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return nil, err
|
||||
}
|
||||
if _, err := DecodeUDPPacket(b); err != nil {
|
||||
u, err := DecodeUDPPacket(buffer)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return nil, err
|
||||
}
|
||||
return buf.MultiBuffer{b}, nil
|
||||
dest := u.Destination()
|
||||
buffer.UDP = &dest
|
||||
return buf.MultiBuffer{buffer}, nil
|
||||
}
|
||||
|
||||
type UDPWriter struct {
|
||||
request *protocol.RequestHeader
|
||||
writer io.Writer
|
||||
Writer io.Writer
|
||||
Request *protocol.RequestHeader
|
||||
}
|
||||
|
||||
func NewUDPWriter(request *protocol.RequestHeader, writer io.Writer) *UDPWriter {
|
||||
return &UDPWriter{
|
||||
request: request,
|
||||
writer: writer,
|
||||
func (w *UDPWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
for {
|
||||
mb2, b := buf.SplitFirst(mb)
|
||||
mb = mb2
|
||||
if b == nil {
|
||||
break
|
||||
}
|
||||
request := w.Request
|
||||
if b.UDP != nil {
|
||||
request = &protocol.RequestHeader{
|
||||
Address: b.UDP.Address,
|
||||
Port: b.UDP.Port,
|
||||
}
|
||||
}
|
||||
packet, err := EncodeUDPPacket(request, b.Bytes())
|
||||
b.Release()
|
||||
if err != nil {
|
||||
buf.ReleaseMulti(mb)
|
||||
return err
|
||||
}
|
||||
_, err = w.Writer.Write(packet.Bytes())
|
||||
packet.Release()
|
||||
if err != nil {
|
||||
buf.ReleaseMulti(mb)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (w *UDPWriter) Write(b []byte) (int, error) {
|
||||
eb, err := EncodeUDPPacket(w.request, b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer eb.Release()
|
||||
if _, err := w.writer.Write(eb.Bytes()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(b), nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
|
||||
@@ -406,16 +422,6 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i
|
||||
defer b.Release()
|
||||
|
||||
common.Must2(b.Write([]byte{socks5Version, 0x01, authByte}))
|
||||
if authByte == authPassword {
|
||||
account := request.User.Account.(*Account)
|
||||
|
||||
common.Must(b.WriteByte(0x01))
|
||||
common.Must(b.WriteByte(byte(len(account.Username))))
|
||||
common.Must2(b.WriteString(account.Username))
|
||||
common.Must(b.WriteByte(byte(len(account.Password))))
|
||||
common.Must2(b.WriteString(account.Password))
|
||||
}
|
||||
|
||||
if err := buf.WriteAllBytes(writer, b.Bytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -433,6 +439,17 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i
|
||||
}
|
||||
|
||||
if authByte == authPassword {
|
||||
b.Clear()
|
||||
account := request.User.Account.(*Account)
|
||||
common.Must(b.WriteByte(0x01))
|
||||
common.Must(b.WriteByte(byte(len(account.Username))))
|
||||
common.Must2(b.WriteString(account.Username))
|
||||
common.Must(b.WriteByte(byte(len(account.Password))))
|
||||
common.Must2(b.WriteString(account.Password))
|
||||
if err := buf.WriteAllBytes(writer, b.Bytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.Clear()
|
||||
if _, err := b.ReadFullFrom(reader, 2); err != nil {
|
||||
return nil, err
|
||||
@@ -446,11 +463,15 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i
|
||||
|
||||
command := byte(cmdTCPConnect)
|
||||
if request.Command == protocol.RequestCommandUDP {
|
||||
command = byte(cmdUDPPort)
|
||||
command = byte(cmdUDPAssociate)
|
||||
}
|
||||
common.Must2(b.Write([]byte{socks5Version, command, 0x00 /* reserved */}))
|
||||
if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil {
|
||||
return nil, err
|
||||
if request.Command == protocol.RequestCommandUDP {
|
||||
common.Must2(b.Write([]byte{1, 0, 0, 0, 0, 0, 0 /* RFC 1928 */}))
|
||||
} else {
|
||||
if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := buf.WriteAllBytes(writer, b.Bytes()); err != nil {
|
||||
|
@@ -20,14 +20,14 @@ func TestUDPEncoding(t *testing.T) {
|
||||
Address: net.IPAddress([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}),
|
||||
Port: 1024,
|
||||
}
|
||||
writer := &buf.SequentialWriter{Writer: NewUDPWriter(request, b)}
|
||||
writer := &UDPWriter{Writer: b, Request: request}
|
||||
|
||||
content := []byte{'a'}
|
||||
payload := buf.New()
|
||||
payload.Write(content)
|
||||
common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{payload}))
|
||||
|
||||
reader := NewUDPReader(b)
|
||||
reader := &UDPReader{Reader: b}
|
||||
|
||||
decodedPayload, err := reader.ReadMultiBuffer()
|
||||
common.Must(err)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user