mirror of https://github.com/XTLS/Xray-core.git
Compare commits
418 Commits
Author | SHA1 | Date |
---|---|---|
|
e4f9d03bef | |
|
7acd5a623b | |
|
ee2000f6e1 | |
|
74d233dd64 | |
|
eb4f9429e6 | |
|
028a640b1b | |
|
9432a600e6 | |
|
8fe976d7ee | |
|
c1a7602412 | |
|
404af13b56 | |
|
c10bd28731 | |
|
501d5dec60 | |
|
d04a9d4fc9 | |
|
ae97821e40 | |
|
219a7dac20 | |
|
a1626cbf94 | |
|
f7dd84d6b9 | |
|
ea02ae74ed | |
|
c15974f7ee | |
|
13f0eccb8f | |
|
f0cb292dc6 | |
|
3654c0d710 | |
|
f8ec93dfdd | |
|
980236f2b6 | |
|
be29cc39d7 | |
|
9c6685d2ee | |
|
f5a70e4200 | |
|
75b3ef71a1 | |
|
df53afceae | |
|
459504300c | |
|
075051a693 | |
|
bbf25b14d9 | |
|
89074a14b6 | |
|
73c5650b17 | |
|
0a3c449cdf | |
|
ca07a705dc | |
|
3120ca4121 | |
|
9b6141b83f | |
|
416f2df11c | |
|
29c6318ffe | |
|
9ee9a0634e | |
|
544f7661ca | |
|
121eb7b4fc | |
|
3168d27b0b | |
|
b98d060ee0 | |
|
26d49df22e | |
|
1d450cfbd2 | |
|
017f53b5fc | |
|
0735053348 | |
|
e41a61c6f7 | |
|
a9715295b8 | |
|
f0b0a7cd4b | |
|
cefae55d7c | |
|
84eeb56ae4 | |
|
eba2906d3a | |
|
cc2849025d | |
|
c1ad35fba8 | |
|
447a49d16a | |
|
51504c624c | |
|
98a2e2c7a1 | |
|
a476310aec | |
|
b8924782a1 | |
|
45ab4cb5ba | |
|
8ce2a0e245 | |
|
61800fcc66 | |
|
ae0eec41d8 | |
|
080bd8241c | |
|
b356b35312 | |
|
1593677b09 | |
|
c85a91bc29 | |
|
dd16dcec03 | |
|
e9eec57b46 | |
|
32f0017449 | |
|
12f5b05aca | |
|
befa7b8138 | |
|
0c61752829 | |
|
cabc4c6013 | |
|
fbc56b88da | |
|
fc41874508 | |
|
03c20bf3b4 | |
|
2843167761 | |
|
021868afca | |
|
548646fb06 | |
|
e64fb3ca9b | |
|
09db7e1cca | |
|
457c1f65e0 | |
|
592157bb00 | |
|
8374d59ce6 | |
|
ec3b2b0907 | |
|
4b893fdd22 | |
|
ec2224974d | |
|
ba57ccdd45 | |
|
07ed0946b5 | |
|
685e66e96b | |
|
7e3a8d3a04 | |
|
e2302b421c | |
|
53e5814d19 | |
|
8a439bf3f2 | |
|
e2439c0483 | |
|
fac383672c | |
|
9a619f9e7c | |
|
bd38578978 | |
|
2cafb3ef89 | |
|
70a5fe9a25 | |
|
657c5c8570 | |
|
fe3a0cf954 | |
|
c05fd2f8c2 | |
|
f848a364e3 | |
|
bb48a2043d | |
|
18b823b4a6 | |
|
69e1734e3a | |
|
5c41292836 | |
|
d3a218f896 | |
|
950a64e9a4 | |
|
37f8654957 | |
|
96ca298b2a | |
|
93cfbd6a92 | |
|
9b5c3f417e | |
|
d7434e8e36 | |
|
e7c5059a6f | |
|
cee776c3b7 | |
|
baf5de5cd3 | |
|
601246468a | |
|
6991c119e7 | |
|
da9c0ea07f | |
|
eefdcb27f3 | |
|
b0fa7b6b85 | |
|
87ef07d5f4 | |
|
a994bf8b04 | |
|
173b03448f | |
|
a3f50d0f5d | |
|
8fe8aa5432 | |
|
7184a8165f | |
|
aa101d9dc2 | |
|
c83c0bb21d | |
|
1c8375997d | |
|
57f0a6712f | |
|
d5e94deea6 | |
|
84d9c63bdb | |
|
51bacf7722 | |
|
3778a367c8 | |
|
a0f1e1f377 | |
|
c100d35b88 | |
|
f9653d0d88 | |
|
ad3dd3df56 | |
|
cae94570df | |
|
5ea1315b85 | |
|
fa5d7a255b | |
|
bf02392969 | |
|
a15334b395 | |
|
09656bd5d1 | |
|
40e41d8b6b | |
|
b091076bae | |
|
7c9e3f97f1 | |
|
c5713eb952 | |
|
7c8bec8596 | |
|
d83ccaeea9 | |
|
303beff5dd | |
|
3a99520370 | |
|
b56917fde5 | |
|
ad3d347cfc | |
|
dd635c7c8d | |
|
813f9a33b7 | |
|
d21e9b0abd | |
|
3167a70ff8 | |
|
25c531c6c3 | |
|
d97a29f55b | |
|
53de58fad3 | |
|
4bffd5114d | |
|
360272a77d | |
|
d20a835016 | |
|
10255bca83 | |
|
da5a28a088 | |
|
77376ed94f | |
|
7f7f57d3b6 | |
|
0ea2a50264 | |
|
961cf9d3b1 | |
|
2b08d8638e | |
|
81f9f567ff | |
|
2fa5c299ac | |
|
8a6b9e7420 | |
|
3f0bc13429 | |
|
9a2ab9b6a3 | |
|
2fc4b31fcf | |
|
7b4db50c9d | |
|
60f7a03e1b | |
|
44bb83033f | |
|
006cf491e5 | |
|
1dba70004f | |
|
1dc9a72068 | |
|
eacdda3c93 | |
|
b0bf0d7fd5 | |
|
b7f21be8bc | |
|
01c14a5994 | |
|
9becf02316 | |
|
f51bf98714 | |
|
5e19c1a778 | |
|
e03b78dcec | |
|
d60281d0a5 | |
|
5a5e615b46 | |
|
c01a30e8f4 | |
|
38b175d53e | |
|
53ac4c031d | |
|
31a8fae764 | |
|
46d6b9f57a | |
|
921be3ac40 | |
|
2da476eef4 | |
|
2c97beae4e | |
|
4e7a57ef86 | |
|
06734d6f08 | |
|
1444552691 | |
|
0c3e1d4bd9 | |
|
c590163f9f | |
|
2fd765ea4c | |
|
be21b1194b | |
|
69cbb4c47a | |
|
6f092bd212 | |
|
2570855cd7 | |
|
84014d7464 | |
|
0ac7da2fc8 | |
|
f1c81557dc | |
|
ac52a226d1 | |
|
7205298474 | |
|
7523f7f440 | |
|
d9fd3f8eb1 | |
|
a109389efb | |
|
cc4b28b159 | |
|
5ae3791a8e | |
|
ea67c98eaf | |
|
7f8ddda1c2 | |
|
631301a6e1 | |
|
ee981524b0 | |
|
d25a2e0224 | |
|
1d89ae2847 | |
|
3500f5b577 | |
|
d24a636c75 | |
|
f86fe6f91a | |
|
11b61b02c8 | |
|
999bdc58d3 | |
|
4f05e0ac2b | |
|
e241e5bda6 | |
|
3d92f3f8b5 | |
|
3eba6a78fe | |
|
67c66faaed | |
|
71fee07175 | |
|
42d586df09 | |
|
4f8b73bb1f | |
|
291061e9da | |
|
f5e71b9db7 | |
|
e4c0fd7c00 | |
|
6177ec7faf | |
|
3ffdf93fc2 | |
|
aeec8dfe25 | |
|
394fac6c6c | |
|
c0af4f85c2 | |
|
707ef51fb1 | |
|
73df64a9f2 | |
|
1e1ee5e39b | |
|
e244db76fb | |
|
772936906a | |
|
57ed75eb67 | |
|
27450c95bd | |
|
db3ac2a977 | |
|
1a72e55ea5 | |
|
07ae08126c | |
|
4f6042c69f | |
|
cf575be678 | |
|
36321b8750 | |
|
6ec82a6792 | |
|
444db2acff | |
|
8b0b8793ed | |
|
585d5ba7c8 | |
|
92bec537f1 | |
|
e66797e79a | |
|
850f617a6f | |
|
852a7d4162 | |
|
490591efcc | |
|
d6d225c698 | |
|
efd32b0fb2 | |
|
ae2fa30e01 | |
|
c00e56c0da | |
|
853a866622 | |
|
7264750e28 | |
|
f7c20b85dc | |
|
b8bd243df5 | |
|
e013dce1df | |
|
d92002ad12 | |
|
b24a4028f1 | |
|
10d6b06578 | |
|
2d5475f428 | |
|
e02474ae15 | |
|
1a69baed17 | |
|
d616f6160d | |
|
229851f621 | |
|
fce86aad33 | |
|
cd1d000860 | |
|
c1db1f4dce | |
|
acadf5c0e9 | |
|
783ac10842 | |
|
efe8f3f4d6 | |
|
599cfd09b0 | |
|
75c99e283a | |
|
a343d68944 | |
|
f67167bb3b | |
|
e584b71b60 | |
|
d11826ee54 | |
|
d4806c8e54 | |
|
cd547a3f43 | |
|
017b56adf5 | |
|
ce89b5d7de | |
|
a45c343b89 | |
|
81b27aa4cc | |
|
1e9d288b99 | |
|
51769fdde1 | |
|
e603b97ab4 | |
|
316034226c | |
|
4a496f94e8 | |
|
b68a43f4fc | |
|
7aeca33729 | |
|
2df418abf1 | |
|
8eb3cfe144 | |
|
929f286c2c | |
|
dca57aab26 | |
|
f0f3b417f7 | |
|
6d4194415d | |
|
846d3ebd6c | |
|
a1ff507ef2 | |
|
b870cc097b | |
|
46d8bb58fc | |
|
34b68518fd | |
|
fb0cd0db4d | |
|
a6c5c57930 | |
|
07389eca96 | |
|
6152868dfe | |
|
6d8fe7315f | |
|
828a632076 | |
|
449affc731 | |
|
4f8f49024b | |
|
a9ed1a03aa | |
|
6f9df63c70 | |
|
253a422467 | |
|
72bbc5ae0e | |
|
ee21763928 | |
|
667279af57 | |
|
62e881b01a | |
|
9122d0f056 | |
|
def5807c64 | |
|
084f4f2e4c | |
|
65b467e448 | |
|
37e1e401a8 | |
|
e8616c6087 | |
|
ca6af4c19d | |
|
8852d02099 | |
|
9112cfd39c | |
|
bf4b1fab3c | |
|
d11d72be6c | |
|
c9f517108c | |
|
038f849dd3 | |
|
a4e80f01e4 | |
|
86b4b81f1d | |
|
6b8e36f6ee | |
|
5f5ae37571 | |
|
c80646a045 | |
|
51b2922427 | |
|
19d3a4faba | |
|
f58fededc5 | |
|
bb26f8576b | |
|
e7324700ed | |
|
cb7e081000 | |
|
70b8b2aaca | |
|
ecedc51173 | |
|
d9af02812f | |
|
6cc5d1de44 | |
|
1f2ffb5222 | |
|
a514d48bae | |
|
37c8957495 | |
|
f3231fb94e | |
|
bfd5da2f00 | |
|
ae518cce52 | |
|
dd81ad5342 | |
|
18e5b0963f | |
|
90d915ea05 | |
|
d9994538bc | |
|
69aa3f48cc | |
|
ca32496a38 | |
|
d3060c28f8 | |
|
ac8109eef8 | |
|
197bc78ea1 | |
|
039e5f2078 | |
|
242f3b0e0b | |
|
b4c1a56026 | |
|
9f8e9e8e64 | |
|
06c9e50c52 | |
|
4f601530fa | |
|
b33b0bc89d | |
|
01b7e5e9be | |
|
24a2be43ef | |
|
29d7865d78 | |
|
05d24d6827 | |
|
76b27a37cb | |
|
15cf31f30a | |
|
54ad0e96a0 | |
|
be23d5d3b7 | |
|
67affe3753 | |
|
2c0a89f7dc | |
|
a4d1509c23 | |
|
f4ab8d7e8b | |
|
beb603af06 | |
|
a0d06f3a97 | |
|
526c6789ed | |
|
6872be5cc3 | |
|
c6b78318cb | |
|
f89998fc77 | |
|
0573760346 | |
|
172f353bd7 | |
|
55efac7236 | |
|
f57ec13880 | |
|
f1e35ad9d4 |
|
@ -0,0 +1,89 @@
|
||||||
|
name: Bug report
|
||||||
|
description: "Submit Xray-core bug"
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Integrity requirements
|
||||||
|
description: |-
|
||||||
|
Please check all of the following options to prove that you have read and understood the requirements, otherwise this issue will be closed.
|
||||||
|
options:
|
||||||
|
- label: I confirm that I have read the documentation, understand the meaning of all the configuration items I wrote, and did not pile up seemingly useful options or default values.
|
||||||
|
required: true
|
||||||
|
- label: I provided the complete config and logs, rather than just providing the truncated parts based on my own judgment.
|
||||||
|
required: true
|
||||||
|
- label: I searched issues and did not find any similar issues.
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Version
|
||||||
|
description: Version of Xray-core
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: Please provide a detailed description of the error. And the information you think valuable.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Reproduction Method
|
||||||
|
description: |-
|
||||||
|
Based on the configuration you provided below, provide the method to reproduce the bug.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |-
|
||||||
|
## Configuration and Log Section
|
||||||
|
|
||||||
|
### For config
|
||||||
|
Please provide the configuration files that can reproduce the problem, including the server and client.
|
||||||
|
Don't just paste a big exported config file here. Eliminate useless inbound/outbound, rules, options, this can help determine the problem, if you really want to get help.
|
||||||
|
|
||||||
|
### For logs
|
||||||
|
Please set the log level to debug and dnsLog to true first.
|
||||||
|
Restart Xray-core, then operate according to the reproduction method, try to reduce the irrelevant part in the log.
|
||||||
|
Remember to delete parts with personal information (such as UUID and IP).
|
||||||
|
Provide the log of Xray-core, not the log output by the panel or other things.
|
||||||
|
|
||||||
|
### Finally
|
||||||
|
After removing parts that do not affect reproduction, provide the actual running **complete** file, do not only provide inbound or outbound or a few lines of logs based on your own judgment.
|
||||||
|
Put the content between the preset ```<details><pre><code>``` ```</code></pre></details>``` in the text box.
|
||||||
|
If the problem is very clear that only related to one end (such as core startup failure/crash after correctly writing the config according to the documents), N/A can be filled in for unnecessary areas below.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Client config
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Server config
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Client log
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Server log
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
|
validations:
|
||||||
|
required: true
|
|
@ -0,0 +1,89 @@
|
||||||
|
name: bug反馈
|
||||||
|
description: "提交 Xray-core bug"
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: 完整性要求
|
||||||
|
description: |-
|
||||||
|
请勾选以下所有选项以证明您已经阅读并理解了以下要求,否则该 issue 将被关闭。
|
||||||
|
options:
|
||||||
|
- label: 我保证阅读了文档,了解所有我编写的配置文件项的含义,而不是大量堆砌看似有用的选项或默认值。
|
||||||
|
required: true
|
||||||
|
- label: 我提供了完整的配置文件和日志,而不是出于自己的判断只给出截取的部分。
|
||||||
|
required: true
|
||||||
|
- label: 我搜索了issues,没有发现已提出的类似问题。
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: 版本
|
||||||
|
description: 使用的Xray-core版本
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 描述
|
||||||
|
description: 请提供错误的详细描述。以及你认为有价值的信息。
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 重现方式
|
||||||
|
description: |-
|
||||||
|
基于你下面提供的配置,提供重现BUG方法。
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |-
|
||||||
|
## 配置与日志部分
|
||||||
|
|
||||||
|
### 对于配置文件
|
||||||
|
请提供可以重现问题的配置文件,包括服务端和客户端。
|
||||||
|
不要直接在这里黏贴一大段导出的 config 文件。去掉无用的出入站、规则、选项,这可以帮助确定问题,如果你真的想得到帮助。
|
||||||
|
|
||||||
|
### 对于日志
|
||||||
|
请先将日志等级设置为 debug, dnsLog 设置为true.
|
||||||
|
重启 Xray-core ,再按复现方式操作,尽量减少日志中的无关部分。
|
||||||
|
记得删除有关个人信息(如UUID与IP)的部分。
|
||||||
|
提供 Xray-core 的日志,而不是面板或者别的东西输出的日志。
|
||||||
|
|
||||||
|
### 最后
|
||||||
|
在去掉不影响复现的部分后,提供实际运行的**完整**文件,不要出于自己的判断只提供入站出站或者几行日志。
|
||||||
|
把内容放在文本框预置的 ```<details><pre><code>``` 和 ```</code></pre></details>``` 中间。
|
||||||
|
如果问题十分明确只出现在某一端(如按文档正确编写配置后核心启动失败/崩溃),可以在下面不需要的项目填入N/A.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 客户端配置
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 服务端配置
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 客户端日志
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 服务端日志
|
||||||
|
value: |-
|
||||||
|
<details><pre><code>
|
||||||
|
|
||||||
|
</code></pre></details>
|
||||||
|
validations:
|
||||||
|
required: true
|
|
@ -2,7 +2,6 @@
|
||||||
"android-arm64": { "friendlyName": "android-arm64-v8a" },
|
"android-arm64": { "friendlyName": "android-arm64-v8a" },
|
||||||
"darwin-amd64": { "friendlyName": "macos-64" },
|
"darwin-amd64": { "friendlyName": "macos-64" },
|
||||||
"darwin-arm64": { "friendlyName": "macos-arm64-v8a" },
|
"darwin-arm64": { "friendlyName": "macos-arm64-v8a" },
|
||||||
"dragonfly-amd64": { "friendlyName": "dragonfly-64" },
|
|
||||||
"freebsd-386": { "friendlyName": "freebsd-32" },
|
"freebsd-386": { "friendlyName": "freebsd-32" },
|
||||||
"freebsd-amd64": { "friendlyName": "freebsd-64" },
|
"freebsd-amd64": { "friendlyName": "freebsd-64" },
|
||||||
"freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" },
|
"freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" },
|
||||||
|
@ -22,6 +21,7 @@
|
||||||
"linux-ppc64le": { "friendlyName": "linux-ppc64le" },
|
"linux-ppc64le": { "friendlyName": "linux-ppc64le" },
|
||||||
"linux-ppc64": { "friendlyName": "linux-ppc64" },
|
"linux-ppc64": { "friendlyName": "linux-ppc64" },
|
||||||
"linux-riscv64": { "friendlyName": "linux-riscv64" },
|
"linux-riscv64": { "friendlyName": "linux-riscv64" },
|
||||||
|
"linux-loong64": { "friendlyName": "linux-loong64" },
|
||||||
"linux-s390x": { "friendlyName": "linux-s390x" },
|
"linux-s390x": { "friendlyName": "linux-s390x" },
|
||||||
"openbsd-386": { "friendlyName": "openbsd-32" },
|
"openbsd-386": { "friendlyName": "openbsd-32" },
|
||||||
"openbsd-amd64": { "friendlyName": "openbsd-64" },
|
"openbsd-amd64": { "friendlyName": "openbsd-64" },
|
||||||
|
@ -31,4 +31,4 @@
|
||||||
"windows-amd64": { "friendlyName": "windows-64" },
|
"windows-amd64": { "friendlyName": "windows-64" },
|
||||||
"windows-arm64": { "friendlyName": "windows-arm64-v8a" },
|
"windows-arm64": { "friendlyName": "windows-arm64-v8a" },
|
||||||
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
|
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM --platform=$BUILDPLATFORM golang:alpine AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . .
|
||||||
|
ARG TARGETOS TARGETARCH
|
||||||
|
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
|
||||||
|
|
||||||
|
FROM --platform=${TARGETPLATFORM} alpine:latest
|
||||||
|
WORKDIR /root
|
||||||
|
COPY .github/docker/files/config.json /etc/xray/config.json
|
||||||
|
COPY --from=build /src/xray /usr/bin/xray
|
||||||
|
RUN set -ex \
|
||||||
|
&& apk add --no-cache tzdata ca-certificates \
|
||||||
|
&& mkdir -p /var/log/xray /usr/share/xray \
|
||||||
|
&& chmod +x /usr/bin/xray \
|
||||||
|
&& wget -O /usr/share/xray/geosite.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat \
|
||||||
|
&& wget -O /usr/share/xray/geoip.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||||
|
|
||||||
|
VOLUME /etc/xray
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
ENTRYPOINT [ "/usr/bin/xray" ]
|
||||||
|
CMD [ "-config", "/etc/xray/config.json" ]
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"inbounds": [{
|
||||||
|
"port": 9000,
|
||||||
|
"protocol": "vmess",
|
||||||
|
"settings": {
|
||||||
|
"clients": [
|
||||||
|
{
|
||||||
|
"id": "1eb6e917-774b-4a84-aff6-b058577c60a5",
|
||||||
|
"level": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"outbounds": [{
|
||||||
|
"protocol": "freedom",
|
||||||
|
"settings": {}
|
||||||
|
}]
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
name: Build docker image
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-image:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Docker metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ghcr.io/${{ github.repository_owner }}/xray-core
|
||||||
|
flavor: latest=true
|
||||||
|
tags: |
|
||||||
|
type=sha
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=pr
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- # Add support for more platforms with QEMU (optional)
|
||||||
|
# https://github.com/docker/setup-qemu-action
|
||||||
|
name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
file: .github/docker/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
|
@ -1,5 +1,11 @@
|
||||||
name: Build and Release
|
name: Build and Release
|
||||||
|
|
||||||
|
# NOTE: This Github Actions file depends on the Makefile.
|
||||||
|
# Building the correct package requires the correct binaries generated by the Makefile. To
|
||||||
|
# ensure the correct output, the Makefile must accept the appropriate input and compile the
|
||||||
|
# correct file with the correct name. If you need to modify this file, please ensure it won't
|
||||||
|
# disrupt the Makefile.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
|
@ -11,27 +17,27 @@ on:
|
||||||
- "**/*.go"
|
- "**/*.go"
|
||||||
- "go.mod"
|
- "go.mod"
|
||||||
- "go.sum"
|
- "go.sum"
|
||||||
- ".github/workflows/*.yml"
|
- ".github/workflows/release.yml"
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
paths:
|
paths:
|
||||||
- "**/*.go"
|
- "**/*.go"
|
||||||
- "go.mod"
|
- "go.mod"
|
||||||
- "go.sum"
|
- "go.sum"
|
||||||
- ".github/workflows/*.yml"
|
- ".github/workflows/release.yml"
|
||||||
jobs:
|
jobs:
|
||||||
prepare:
|
prepare:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Restore Cache
|
- name: Restore Cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
with:
|
with:
|
||||||
path: resources
|
path: resources
|
||||||
key: xray-geodat-
|
key: xray-geodat-
|
||||||
|
|
||||||
- name: Update Geodat
|
- name: Update Geodat
|
||||||
id: update
|
id: update
|
||||||
uses: nick-fields/retry@v2
|
uses: nick-fields/retry@v3
|
||||||
with:
|
with:
|
||||||
timeout_minutes: 60
|
timeout_minutes: 60
|
||||||
retry_wait_seconds: 60
|
retry_wait_seconds: 60
|
||||||
|
@ -57,7 +63,7 @@ jobs:
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Save Cache
|
- name: Save Cache
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4
|
||||||
if: ${{ steps.update.outputs.unhit }}
|
if: ${{ steps.update.outputs.unhit }}
|
||||||
with:
|
with:
|
||||||
path: resources
|
path: resources
|
||||||
|
@ -70,12 +76,10 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# Include amd64 on all platforms.
|
# Include amd64 on all platforms.
|
||||||
goos: [windows, freebsd, openbsd, linux, dragonfly, darwin]
|
goos: [windows, freebsd, openbsd, linux, darwin]
|
||||||
goarch: [amd64, 386]
|
goarch: [amd64, 386]
|
||||||
exclude:
|
exclude:
|
||||||
# Exclude i386 on darwin and dragonfly.
|
# Exclude i386 on darwin
|
||||||
- goarch: 386
|
|
||||||
goos: dragonfly
|
|
||||||
- goarch: 386
|
- goarch: 386
|
||||||
goos: darwin
|
goos: darwin
|
||||||
include:
|
include:
|
||||||
|
@ -105,12 +109,14 @@ jobs:
|
||||||
goarch: arm
|
goarch: arm
|
||||||
goarm: 7
|
goarm: 7
|
||||||
# BEGIN Other architectures
|
# BEGIN Other architectures
|
||||||
# BEGIN riscv64 & ARM64
|
# BEGIN riscv64 & ARM64 & LOONG64
|
||||||
- goos: linux
|
- goos: linux
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
- goos: linux
|
- goos: linux
|
||||||
goarch: riscv64
|
goarch: riscv64
|
||||||
# END riscv64 & ARM64
|
- goos: linux
|
||||||
|
goarch: loong64
|
||||||
|
# END riscv64 & ARM64 & LOONG64
|
||||||
# BEGIN MIPS
|
# BEGIN MIPS
|
||||||
- goos: linux
|
- goos: linux
|
||||||
goarch: mips64
|
goarch: mips64
|
||||||
|
@ -156,7 +162,7 @@ jobs:
|
||||||
CGO_ENABLED: 0
|
CGO_ENABLED: 0
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout codebase
|
- name: Checkout codebase
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Show workflow information
|
- name: Show workflow information
|
||||||
run: |
|
run: |
|
||||||
|
@ -165,47 +171,22 @@ jobs:
|
||||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.20'
|
go-version-file: go.mod
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Get project dependencies
|
- name: Get project dependencies
|
||||||
run: go mod download
|
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
|
- name: Build Xray
|
||||||
run: |
|
run: |
|
||||||
mkdir -p build_assets
|
mkdir -p build_assets
|
||||||
go build -v -o build_assets/xray -trimpath -ldflags "-s -w -buildid=" ./main
|
make
|
||||||
|
find . -maxdepth 1 -type f -regex './\(wxray\|xray\|xray_softfloat\)\(\|.exe\)' -exec mv {} ./build_assets/ \;
|
||||||
- name: Build background Xray on Windows
|
|
||||||
if: matrix.goos == 'windows'
|
|
||||||
run: |
|
|
||||||
go build -v -o build_assets/wxray.exe -trimpath -ldflags "-s -w -H windowsgui -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: Restore Cache
|
- name: Restore Cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
with:
|
with:
|
||||||
path: resources
|
path: resources
|
||||||
key: xray-geodat-
|
key: xray-geodat-
|
||||||
|
@ -217,6 +198,7 @@ jobs:
|
||||||
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
|
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
|
||||||
|
|
||||||
- name: Create ZIP archive
|
- name: Create ZIP archive
|
||||||
|
if: github.event_name == 'release'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
pushd build_assets || exit 1
|
pushd build_assets || exit 1
|
||||||
|
@ -235,7 +217,7 @@ jobs:
|
||||||
mv build_assets Xray-${{ env.ASSET_NAME }}
|
mv build_assets Xray-${{ env.ASSET_NAME }}
|
||||||
|
|
||||||
- name: Upload files to Artifacts
|
- name: Upload files to Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Xray-${{ env.ASSET_NAME }}
|
name: Xray-${{ env.ASSET_NAME }}
|
||||||
path: |
|
path: |
|
||||||
|
|
|
@ -27,15 +27,15 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
os: [windows-latest, ubuntu-latest, macos-latest]
|
os: [windows-latest, ubuntu-latest, macos-latest]
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.20'
|
|
||||||
check-latest: true
|
|
||||||
- name: Checkout codebase
|
- name: Checkout codebase
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
check-latest: true
|
||||||
- name: Restore Cache
|
- name: Restore Cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4
|
||||||
with:
|
with:
|
||||||
path: resources
|
path: resources
|
||||||
key: xray-geodat-
|
key: xray-geodat-
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
*.zip
|
*.zip
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
xray
|
xray
|
||||||
|
xray_softfloat
|
||||||
mockgen
|
mockgen
|
||||||
vprotogen
|
vprotogen
|
||||||
!infra/vprotogen/
|
!infra/vprotogen/
|
||||||
|
@ -26,3 +27,4 @@ errorgen
|
||||||
!common/errors/errorgen/
|
!common/errors/errorgen/
|
||||||
*.dat
|
*.dat
|
||||||
.vscode
|
.vscode
|
||||||
|
/build_assets
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
NAME = xray
|
||||||
|
|
||||||
|
VERSION=$(shell git describe --always --dirty)
|
||||||
|
|
||||||
|
# NOTE: This MAKEFILE can be used to build Xray-core locally and in Automatic workflows. It is \
|
||||||
|
provided for convinience in automatic building and functions as a part of it.
|
||||||
|
# NOTE: If you need to modify this file, please be aware that:\
|
||||||
|
- This file is not the main Makefile; it only accepts environment variables and builds the \
|
||||||
|
binary.\
|
||||||
|
- Automatic building expects the correct binaries to be built by this Makefile. If you \
|
||||||
|
intend to propose a change to this Makefile, carefully review the file below and ensure \
|
||||||
|
that the change will not accidently break the automatic building:\
|
||||||
|
.github/workflows/release.yml \
|
||||||
|
Otherwise it is recommended to contact the project maintainers.
|
||||||
|
|
||||||
|
LDFLAGS = -X github.com/xtls/xray-core/core.build=$(VERSION) -s -w -buildid=
|
||||||
|
PARAMS = -trimpath -ldflags "$(LDFLAGS)" -v
|
||||||
|
MAIN = ./main
|
||||||
|
PREFIX ?= $(shell go env GOPATH)
|
||||||
|
ifeq ($(GOOS),windows)
|
||||||
|
OUTPUT = $(NAME).exe
|
||||||
|
ADDITION = go build -o w$(NAME).exe -trimpath -ldflags "-H windowsgui $(LDFLAGS)" -v $(MAIN)
|
||||||
|
else
|
||||||
|
OUTPUT = $(NAME)
|
||||||
|
endif
|
||||||
|
ifeq ($(shell echo "$(GOARCH)" | grep -Eq "(mips|mipsle)" && echo true),true) #
|
||||||
|
ADDITION = GOMIPS=softfloat go build -o $(NAME)_softfloat -trimpath -ldflags "$(LDFLAGS)" -v $(MAIN)
|
||||||
|
endif
|
||||||
|
.PHONY: clean build
|
||||||
|
|
||||||
|
build:
|
||||||
|
go build -o $(OUTPUT) $(PARAMS) $(MAIN)
|
||||||
|
$(ADDITION)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -v -i $(PWD)
|
||||||
|
rm -f xray xray.exe wxray.exe xray_softfloat
|
212
README.md
212
README.md
|
@ -1,92 +1,16 @@
|
||||||
# Project X
|
# Project X
|
||||||
|
|
||||||
[Project X](https://github.com/XTLS) originates from XTLS protocol, provides a set of network tools such as [Xray-core](https://github.com/XTLS/Xray-core).
|
[Project X](https://github.com/XTLS) originates from XTLS protocol, providing a set of network tools such as [Xray-core](https://github.com/XTLS/Xray-core) and [REALITY](https://github.com/XTLS/REALITY).
|
||||||
|
|
||||||
|
[README](https://github.com/XTLS/Xray-core#readme) is open, so feel free to submit your project [here](https://github.com/XTLS/Xray-core/pulls).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[Mozilla Public License Version 2.0](https://github.com/XTLS/Xray-core/blob/main/LICENSE)
|
[Mozilla Public License Version 2.0](https://github.com/XTLS/Xray-core/blob/main/LICENSE)
|
||||||
|
|
||||||
## Installation
|
## Documentation
|
||||||
|
|
||||||
- Linux Script
|
[Project X Official Website](https://xtls.github.io)
|
||||||
- [Xray-install](https://github.com/XTLS/Xray-install)
|
|
||||||
- [Xray-script](https://github.com/kirin10000/Xray-script)
|
|
||||||
- Docker
|
|
||||||
- [teddysun/xray](https://hub.docker.com/r/teddysun/xray)
|
|
||||||
- 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
|
|
||||||
- `brew install xray`
|
|
||||||
- [(Tap) Repository 0](https://github.com/N4FA/homebrew-xray)
|
|
||||||
- [(Tap) Repository 1](https://github.com/xiruizhao/homebrew-xray)
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
[Code Of Conduct](https://github.com/XTLS/Xray-core/blob/main/CODE_OF_CONDUCT.md)
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
[Xray-examples](https://github.com/XTLS/Xray-examples) / [VLESS-TCP-XTLS-WHATEVER](https://github.com/XTLS/Xray-examples/tree/main/VLESS-TCP-XTLS-WHATEVER)
|
|
||||||
|
|
||||||
## GUI Clients
|
|
||||||
|
|
||||||
- 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) (This project had been archived and currently inactive)
|
|
||||||
- [Netch (NetFilter & TUN/TAP)](https://github.com/NetchX/Netch) (This project had been archived and currently inactive)
|
|
||||||
- Android
|
|
||||||
- [v2rayNG](https://github.com/2dust/v2rayNG)
|
|
||||||
- [Kitsunebi](https://github.com/rurirei/Kitsunebi/tree/release_xtls)
|
|
||||||
- iOS & macOS (with M1 chip)
|
|
||||||
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
|
|
||||||
- [Stash](https://apps.apple.com/app/stash/id1596063349)
|
|
||||||
- macOS (Intel chip & M1 chip)
|
|
||||||
- [Qv2ray](https://github.com/Qv2ray/Qv2ray) (This project had been archived and currently inactive)
|
|
||||||
- [V2RayXS](https://github.com/tzmax/V2RayXS)
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
This repo relies on the following third-party projects:
|
|
||||||
|
|
||||||
- Special thanks:
|
|
||||||
- [v2fly/v2ray-core](https://github.com/v2fly/v2ray-core)
|
|
||||||
- In production:
|
|
||||||
- [ghodss/yaml](https://github.com/ghodss/yaml)
|
|
||||||
- [gorilla/websocket](https://github.com/gorilla/websocket)
|
|
||||||
- [quic-go/quic-go](https://github.com/quic-go/quic-go)
|
|
||||||
- [pelletier/go-toml](https://github.com/pelletier/go-toml)
|
|
||||||
- [pires/go-proxyproto](https://github.com/pires/go-proxyproto)
|
|
||||||
- [refraction-networking/utls](https://github.com/refraction-networking/utls)
|
|
||||||
- [seiflotfy/cuckoofilter](https://github.com/seiflotfy/cuckoofilter)
|
|
||||||
- [google/starlark-go](https://github.com/google/starlark-go)
|
|
||||||
- For testing only:
|
|
||||||
- [miekg/dns](https://github.com/miekg/dns)
|
|
||||||
- [stretchr/testify](https://github.com/stretchr/testify)
|
|
||||||
- [h12w/socks](https://github.com/h12w/socks)
|
|
||||||
|
|
||||||
## Compilation
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go build -o xray.exe -trimpath -ldflags "-s -w -buildid=" ./main
|
|
||||||
```
|
|
||||||
|
|
||||||
### Linux / macOS
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
|
|
||||||
```
|
|
||||||
|
|
||||||
## Telegram
|
## Telegram
|
||||||
|
|
||||||
|
@ -94,6 +18,132 @@ go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
|
||||||
|
|
||||||
[Project X Channel](https://t.me/projectXtls)
|
[Project X Channel](https://t.me/projectXtls)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
- Linux Script
|
||||||
|
- [XTLS/Xray-install](https://github.com/XTLS/Xray-install) (**Official**)
|
||||||
|
- [tempest](https://github.com/team-cloudchaser/tempest) (supports [`systemd`](https://systemd.io) and [OpenRC](https://github.com/OpenRC/openrc); Linux-only)
|
||||||
|
- Docker
|
||||||
|
- [ghcr.io/xtls/xray-core](https://ghcr.io/xtls/xray-core) (**Official**)
|
||||||
|
- [teddysun/xray](https://hub.docker.com/r/teddysun/xray)
|
||||||
|
- Web Panel
|
||||||
|
- [X-UI-English](https://github.com/NidukaAkalanka/x-ui-english), [3X-UI](https://github.com/MHSanaei/3x-ui), [X-UI](https://github.com/alireza0/x-ui), [X-UI](https://github.com/diditra/x-ui)
|
||||||
|
- [Xray-UI](https://github.com/qist/xray-ui), [X-UI](https://github.com/sing-web/x-ui)
|
||||||
|
- [Hiddify](https://github.com/hiddify/hiddify-config)
|
||||||
|
- [Marzban](https://github.com/Gozargah/Marzban)
|
||||||
|
- [Libertea](https://github.com/VZiChoushaDui/Libertea)
|
||||||
|
- One Click
|
||||||
|
- [Xray-REALITY](https://github.com/zxcvos/Xray-script), [xray-reality](https://github.com/sajjaddg/xray-reality), [reality-ezpz](https://github.com/aleskxyz/reality-ezpz)
|
||||||
|
- [Xray_bash_onekey](https://github.com/hello-yunshu/Xray_bash_onekey), [XTool](https://github.com/LordPenguin666/XTool)
|
||||||
|
- [v2ray-agent](https://github.com/mack-a/v2ray-agent), [Xray_onekey](https://github.com/wulabing/Xray_onekey), [ProxySU](https://github.com/proxysu/ProxySU)
|
||||||
|
- Magisk
|
||||||
|
- [Xray4Magisk](https://github.com/Asterisk4Magisk/Xray4Magisk)
|
||||||
|
- [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk)
|
||||||
|
- Homebrew
|
||||||
|
- `brew install xray`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
- Example
|
||||||
|
- [VLESS-XTLS-uTLS-REALITY](https://github.com/XTLS/REALITY#readme)
|
||||||
|
- [VLESS-TCP-XTLS-Vision](https://github.com/XTLS/Xray-examples/tree/main/VLESS-TCP-XTLS-Vision)
|
||||||
|
- [All-in-One-fallbacks-Nginx](https://github.com/XTLS/Xray-examples/tree/main/All-in-One-fallbacks-Nginx)
|
||||||
|
- Xray-examples
|
||||||
|
- [XTLS/Xray-examples](https://github.com/XTLS/Xray-examples)
|
||||||
|
- [chika0801/Xray-examples](https://github.com/chika0801/Xray-examples)
|
||||||
|
- [lxhao61/integrated-examples](https://github.com/lxhao61/integrated-examples)
|
||||||
|
- Tutorial
|
||||||
|
- [XTLS Vision](https://github.com/chika0801/Xray-install)
|
||||||
|
- [REALITY (English)](https://cscot.pages.dev/2023/03/02/Xray-REALITY-tutorial/)
|
||||||
|
- [XTLS-Iran-Reality (English)](https://github.com/SasukeFreestyle/XTLS-Iran-Reality)
|
||||||
|
- [Xray REALITY with 'steal oneself' (English)](https://computerscot.github.io/vless-xtls-utls-reality-steal-oneself.html)
|
||||||
|
- [Xray with WireGuard inbound (English)](https://g800.pages.dev/wireguard)
|
||||||
|
|
||||||
|
## GUI Clients
|
||||||
|
|
||||||
|
- OpenWrt
|
||||||
|
- [PassWall](https://github.com/xiaorouji/openwrt-passwall), [PassWall 2](https://github.com/xiaorouji/openwrt-passwall2)
|
||||||
|
- [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)
|
||||||
|
- [NekoRay](https://github.com/Matsuridayo/nekoray)
|
||||||
|
- [Furious](https://github.com/LorenEteval/Furious)
|
||||||
|
- [HiddifyN](https://github.com/hiddify/HiddifyN)
|
||||||
|
- [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient)
|
||||||
|
- Android
|
||||||
|
- [v2rayNG](https://github.com/2dust/v2rayNG)
|
||||||
|
- [HiddifyNG](https://github.com/hiddify/HiddifyNG)
|
||||||
|
- [X-flutter](https://github.com/XTLS/X-flutter)
|
||||||
|
- iOS & macOS arm64
|
||||||
|
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
|
||||||
|
- [Streisand](https://apps.apple.com/app/streisand/id6450534064)
|
||||||
|
- macOS arm64 & x64
|
||||||
|
- [V2rayU](https://github.com/yanue/V2rayU)
|
||||||
|
- [V2RayXS](https://github.com/tzmax/V2RayXS)
|
||||||
|
- [Furious](https://github.com/LorenEteval/Furious)
|
||||||
|
- [FoXray](https://apps.apple.com/app/foxray/id6448898396)
|
||||||
|
- Linux
|
||||||
|
- [v2rayA](https://github.com/v2rayA/v2rayA)
|
||||||
|
- [NekoRay](https://github.com/Matsuridayo/nekoray)
|
||||||
|
- [Furious](https://github.com/LorenEteval/Furious)
|
||||||
|
|
||||||
|
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
|
||||||
|
|
||||||
|
- iOS & macOS arm64
|
||||||
|
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
|
||||||
|
- Xray Tools
|
||||||
|
- [xray-knife](https://github.com/lilendian0x00/xray-knife)
|
||||||
|
- Xray Wrapper
|
||||||
|
- [XTLS/libXray](https://github.com/XTLS/libXray)
|
||||||
|
- [xtlsapi](https://github.com/hiddify/xtlsapi)
|
||||||
|
- [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite)
|
||||||
|
- [XrayKit](https://github.com/arror/XrayKit)
|
||||||
|
- [Xray-core-python](https://github.com/LorenEteval/Xray-core-python)
|
||||||
|
- [xray-api](https://github.com/XVGuardian/xray-api)
|
||||||
|
- [XrayR](https://github.com/XrayR-project/XrayR)
|
||||||
|
- [XrayR-release](https://github.com/XrayR-project/XrayR-release)
|
||||||
|
- [XrayR-V2Board](https://github.com/missuo/XrayR-V2Board)
|
||||||
|
- [Clash.Meta](https://github.com/MetaCubeX/Clash.Meta)
|
||||||
|
- [Clash Verge](https://github.com/zzzgydi/clash-verge)
|
||||||
|
- [clashN](https://github.com/2dust/clashN)
|
||||||
|
- [Clash Meta for Android](https://github.com/MetaCubeX/ClashMetaForAndroid)
|
||||||
|
- [meta_for_ios](https://t.me/meta_for_ios)
|
||||||
|
- [sing-box](https://github.com/SagerNet/sing-box)
|
||||||
|
- [installReality](https://github.com/BoxXt/installReality)
|
||||||
|
- [sbox-reality](https://github.com/Misaka-blog/sbox-reality)
|
||||||
|
- [sing-box-for-ios](https://github.com/SagerNet/sing-box-for-ios)
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
[Code of Conduct](https://github.com/XTLS/Xray-core/blob/main/CODE_OF_CONDUCT.md)
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- [Xray-core v1.0.0](https://github.com/XTLS/Xray-core/releases/tag/v1.0.0) was forked from [v2fly-core 9a03cc5](https://github.com/v2fly/v2ray-core/commit/9a03cc5c98d04cc28320fcee26dbc236b3291256), and we have made & accumulated a huge number of enhancements over time, check [the release notes for each version](https://github.com/XTLS/Xray-core/releases).
|
||||||
|
- For third-party projects used in [Xray-core](https://github.com/XTLS/Xray-core), check your local or [the latest go.mod](https://github.com/XTLS/Xray-core/blob/main/go.mod).
|
||||||
|
|
||||||
|
## Compilation
|
||||||
|
|
||||||
|
### Windows (PowerShell)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:CGO_ENABLED=0
|
||||||
|
go build -o xray.exe -trimpath -ldflags "-s -w -buildid=" ./main
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux / macOS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CGO_ENABLED=0 go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reproducible Releases
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
## Stargazers over time
|
## Stargazers over time
|
||||||
|
|
||||||
[![Stargazers over time](https://starchart.cc/XTLS/Xray-core.svg)](https://starchart.cc/XTLS/Xray-core)
|
[![Stargazers over time](https://starchart.cc/XTLS/Xray-core.svg)](https://starchart.cc/XTLS/Xray-core)
|
||||||
|
|
|
@ -21,12 +21,14 @@ type Commander struct {
|
||||||
services []Service
|
services []Service
|
||||||
ohm outbound.Manager
|
ohm outbound.Manager
|
||||||
tag string
|
tag string
|
||||||
|
listen string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCommander creates a new Commander based on the given config.
|
// NewCommander creates a new Commander based on the given config.
|
||||||
func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
|
func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
|
||||||
c := &Commander{
|
c := &Commander{
|
||||||
tag: config.Tag,
|
tag: config.Tag,
|
||||||
|
listen: config.Listen,
|
||||||
}
|
}
|
||||||
|
|
||||||
common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) {
|
common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) {
|
||||||
|
@ -66,16 +68,29 @@ func (c *Commander) Start() error {
|
||||||
}
|
}
|
||||||
c.Unlock()
|
c.Unlock()
|
||||||
|
|
||||||
|
var listen = func(listener net.Listener) {
|
||||||
|
if err := c.server.Serve(listener); err != nil {
|
||||||
|
newError("failed to start grpc server").Base(err).AtError().WriteToLog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.listen) > 0 {
|
||||||
|
if l, err := net.Listen("tcp", c.listen); err != nil {
|
||||||
|
newError("API server failed to listen on ", c.listen).Base(err).AtError().WriteToLog()
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
newError("API server listening on ", l.Addr()).AtInfo().WriteToLog()
|
||||||
|
go listen(l)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
listener := &OutboundListener{
|
listener := &OutboundListener{
|
||||||
buffer: make(chan net.Conn, 4),
|
buffer: make(chan net.Conn, 4),
|
||||||
done: done.New(),
|
done: done.New(),
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go listen(listener)
|
||||||
if err := c.server.Serve(listener); err != nil {
|
|
||||||
newError("failed to start grpc server").Base(err).AtError().WriteToLog()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := c.ohm.RemoveHandler(context.Background(), c.tag); err != nil {
|
if err := c.ohm.RemoveHandler(context.Background(), c.tag); err != nil {
|
||||||
newError("failed to remove existing handler").WriteToLog()
|
newError("failed to remove existing handler").WriteToLog()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/commander/config.proto
|
// source: app/commander/config.proto
|
||||||
|
|
||||||
package commander
|
package commander
|
||||||
|
@ -29,6 +29,8 @@ type Config struct {
|
||||||
|
|
||||||
// Tag of the outbound handler that handles grpc connections.
|
// Tag of the outbound handler that handles grpc connections.
|
||||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||||
|
// Network address of commander grpc service.
|
||||||
|
Listen string `protobuf:"bytes,3,opt,name=listen,proto3" json:"listen,omitempty"`
|
||||||
// Services that supported by this server. All services must implement Service
|
// Services that supported by this server. All services must implement Service
|
||||||
// interface.
|
// interface.
|
||||||
Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"`
|
Service []*serial.TypedMessage `protobuf:"bytes,2,rep,name=service,proto3" json:"service,omitempty"`
|
||||||
|
@ -73,6 +75,13 @@ func (x *Config) GetTag() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetListen() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Listen
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (x *Config) GetService() []*serial.TypedMessage {
|
func (x *Config) GetService() []*serial.TypedMessage {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Service
|
return x.Service
|
||||||
|
@ -127,20 +136,21 @@ var file_app_commander_config_proto_rawDesc = []byte{
|
||||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72,
|
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72,
|
||||||
0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f,
|
0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f,
|
||||||
0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72,
|
0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72,
|
||||||
0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a,
|
0x6f, 0x74, 0x6f, 0x22, 0x6e, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a,
|
||||||
0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12,
|
0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12,
|
||||||
0x3a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
|
0x16, 0x0a, 0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73,
|
0x06, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x12, 0x3a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69,
|
||||||
0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
0x67, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52,
|
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79,
|
||||||
0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42,
|
0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76,
|
||||||
0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
0x69, 0x63, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
||||||
0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74,
|
0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
|
||||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
|
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65,
|
||||||
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
|
0x72, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
||||||
0x6e, 0x64, 0x65, 0x72, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e,
|
0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61,
|
||||||
0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0xaa, 0x02, 0x12, 0x58,
|
||||||
0x33,
|
0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65,
|
||||||
|
0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -12,6 +12,10 @@ import "common/serial/typed_message.proto";
|
||||||
message Config {
|
message Config {
|
||||||
// Tag of the outbound handler that handles grpc connections.
|
// Tag of the outbound handler that handles grpc connections.
|
||||||
string tag = 1;
|
string tag = 1;
|
||||||
|
|
||||||
|
// Network address of commander grpc service.
|
||||||
|
string listen = 3;
|
||||||
|
|
||||||
// Services that supported by this server. All services must implement Service
|
// Services that supported by this server. All services must implement Service
|
||||||
// interface.
|
// interface.
|
||||||
repeated xray.common.serial.TypedMessage service = 2;
|
repeated xray.common.serial.TypedMessage service = 2;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/dispatcher/config.proto
|
// source: app/dispatcher/config.proto
|
||||||
|
|
||||||
package dispatcher
|
package dispatcher
|
||||||
|
|
|
@ -4,7 +4,7 @@ package dispatcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -135,77 +135,10 @@ func (*DefaultDispatcher) Start() error {
|
||||||
// Close implements common.Closable.
|
// Close implements common.Closable.
|
||||||
func (*DefaultDispatcher) Close() error { return nil }
|
func (*DefaultDispatcher) Close() error { return nil }
|
||||||
|
|
||||||
func (d *DefaultDispatcher) getLink(ctx context.Context, network net.Network, sniffing session.SniffingRequest) (*transport.Link, *transport.Link) {
|
func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *transport.Link) {
|
||||||
downOpt := pipe.OptionsFromContext(ctx)
|
opt := pipe.OptionsFromContext(ctx)
|
||||||
upOpt := downOpt
|
uplinkReader, uplinkWriter := pipe.New(opt...)
|
||||||
|
downlinkReader, downlinkWriter := pipe.New(opt...)
|
||||||
if network == net.Network_UDP {
|
|
||||||
var ip2domain *sync.Map // net.IP.String() => domain, this map is used by server side when client turn on fakedns
|
|
||||||
// Client will send domain address in the buffer.UDP.Address, server record all possible target IP addrs.
|
|
||||||
// When target replies, server will restore the domain and send back to client.
|
|
||||||
// Note: this map is not global but per connection context
|
|
||||||
upOpt = append(upOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer {
|
|
||||||
for i, buffer := range mb {
|
|
||||||
if buffer.UDP == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
addr := buffer.UDP.Address
|
|
||||||
if addr.Family().IsIP() {
|
|
||||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(addr) && sniffing.Enabled {
|
|
||||||
domain := fkr0.GetDomainFromFakeDNS(addr)
|
|
||||||
if len(domain) > 0 {
|
|
||||||
buffer.UDP.Address = net.DomainAddress(domain)
|
|
||||||
newError("[fakedns client] override with domain: ", domain, " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
} else {
|
|
||||||
newError("[fakedns client] failed to find domain! :", addr.String(), " for xUDP buffer at ", i).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ip2domain == nil {
|
|
||||||
ip2domain = new(sync.Map)
|
|
||||||
newError("[fakedns client] create a new map").WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
}
|
|
||||||
domain := addr.Domain()
|
|
||||||
ips, err := d.dns.LookupIP(domain, dns.IPOption{true, true, false})
|
|
||||||
if err == nil {
|
|
||||||
for _, ip := range ips {
|
|
||||||
ip2domain.Store(ip.String(), domain)
|
|
||||||
}
|
|
||||||
newError("[fakedns client] candidate ip: "+fmt.Sprintf("%v", ips), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
} else {
|
|
||||||
newError("[fakedns client] failed to look up IP for ", domain, " for xUDP buffer at ", i).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mb
|
|
||||||
}))
|
|
||||||
downOpt = append(downOpt, pipe.OnTransmission(func(mb buf.MultiBuffer) buf.MultiBuffer {
|
|
||||||
for i, buffer := range mb {
|
|
||||||
if buffer.UDP == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
addr := buffer.UDP.Address
|
|
||||||
if addr.Family().IsIP() {
|
|
||||||
if ip2domain == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if domain, found := ip2domain.Load(addr.IP().String()); found {
|
|
||||||
buffer.UDP.Address = net.DomainAddress(domain.(string))
|
|
||||||
newError("[fakedns client] restore domain: ", domain.(string), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok {
|
|
||||||
fakeIp := fkr0.GetFakeIPForDomain(addr.Domain())
|
|
||||||
buffer.UDP.Address = fakeIp[0]
|
|
||||||
newError("[fakedns client] restore FakeIP: ", buffer.UDP, fmt.Sprintf("%v", fakeIp), " for xUDP buffer at ", i).WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mb
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
uplinkReader, uplinkWriter := pipe.New(upOpt...)
|
|
||||||
downlinkReader, downlinkWriter := pipe.New(downOpt...)
|
|
||||||
|
|
||||||
inboundLink := &transport.Link{
|
inboundLink := &transport.Link{
|
||||||
Reader: downlinkReader,
|
Reader: downlinkReader,
|
||||||
|
@ -254,8 +187,20 @@ func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResu
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, d := range request.ExcludeForDomain {
|
for _, d := range request.ExcludeForDomain {
|
||||||
if strings.ToLower(domain) == d {
|
if strings.HasPrefix(d, "regexp:") {
|
||||||
return false
|
pattern := d[7:]
|
||||||
|
re, err := regexp.Compile(pattern)
|
||||||
|
if err != nil {
|
||||||
|
newError("Unable to compile regex").WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if re.MatchString(domain) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if strings.ToLower(domain) == d {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
protocolString := result.Protocol()
|
protocolString := result.Protocol()
|
||||||
|
@ -263,11 +208,11 @@ func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResu
|
||||||
protocolString = resComp.ProtocolForDomainResult()
|
protocolString = resComp.ProtocolForDomainResult()
|
||||||
}
|
}
|
||||||
for _, p := range request.OverrideDestinationForProtocol {
|
for _, p := range request.OverrideDestinationForProtocol {
|
||||||
if strings.HasPrefix(protocolString, p) {
|
if strings.HasPrefix(protocolString, p) || strings.HasPrefix(p, protocolString) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
|
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" &&
|
||||||
destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) {
|
fkr0.IsIPInIPPool(destination.Address) {
|
||||||
newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx))
|
newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -286,10 +231,14 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
||||||
if !destination.IsValid() {
|
if !destination.IsValid() {
|
||||||
panic("Dispatcher: Invalid destination.")
|
panic("Dispatcher: Invalid destination.")
|
||||||
}
|
}
|
||||||
ob := &session.Outbound{
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
Target: destination,
|
if len(outbounds) == 0 {
|
||||||
|
outbounds = []*session.Outbound{{}}
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
}
|
}
|
||||||
ctx = session.ContextWithOutbound(ctx, ob)
|
ob := outbounds[len(outbounds)-1]
|
||||||
|
ob.OriginalTarget = destination
|
||||||
|
ob.Target = destination
|
||||||
content := session.ContentFromContext(ctx)
|
content := session.ContentFromContext(ctx)
|
||||||
if content == nil {
|
if content == nil {
|
||||||
content = new(session.Content)
|
content = new(session.Content)
|
||||||
|
@ -297,7 +246,7 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
||||||
}
|
}
|
||||||
|
|
||||||
sniffingRequest := content.SniffingRequest
|
sniffingRequest := content.SniffingRequest
|
||||||
inbound, outbound := d.getLink(ctx, destination.Network, sniffingRequest)
|
inbound, outbound := d.getLink(ctx)
|
||||||
if !sniffingRequest.Enabled {
|
if !sniffingRequest.Enabled {
|
||||||
go d.routedDispatch(ctx, outbound, destination)
|
go d.routedDispatch(ctx, outbound, destination)
|
||||||
} else {
|
} else {
|
||||||
|
@ -314,7 +263,15 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
||||||
domain := result.Domain()
|
domain := result.Domain()
|
||||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
||||||
destination.Address = net.ParseAddress(domain)
|
destination.Address = net.ParseAddress(domain)
|
||||||
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
|
protocol := result.Protocol()
|
||||||
|
if resComp, ok := result.(SnifferResultComposite); ok {
|
||||||
|
protocol = resComp.ProtocolForDomainResult()
|
||||||
|
}
|
||||||
|
isFakeIP := false
|
||||||
|
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(ob.Target.Address) {
|
||||||
|
isFakeIP = true
|
||||||
|
}
|
||||||
|
if sniffingRequest.RouteOnly && protocol != "fakedns" && protocol != "fakedns+others" && !isFakeIP {
|
||||||
ob.RouteTarget = destination
|
ob.RouteTarget = destination
|
||||||
} else {
|
} else {
|
||||||
ob.Target = destination
|
ob.Target = destination
|
||||||
|
@ -331,10 +288,14 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
||||||
if !destination.IsValid() {
|
if !destination.IsValid() {
|
||||||
return newError("Dispatcher: Invalid destination.")
|
return newError("Dispatcher: Invalid destination.")
|
||||||
}
|
}
|
||||||
ob := &session.Outbound{
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
Target: destination,
|
if len(outbounds) == 0 {
|
||||||
|
outbounds = []*session.Outbound{{}}
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
}
|
}
|
||||||
ctx = session.ContextWithOutbound(ctx, ob)
|
ob := outbounds[len(outbounds)-1]
|
||||||
|
ob.OriginalTarget = destination
|
||||||
|
ob.Target = destination
|
||||||
content := session.ContentFromContext(ctx)
|
content := session.ContentFromContext(ctx)
|
||||||
if content == nil {
|
if content == nil {
|
||||||
content = new(session.Content)
|
content = new(session.Content)
|
||||||
|
@ -342,29 +303,35 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
||||||
}
|
}
|
||||||
sniffingRequest := content.SniffingRequest
|
sniffingRequest := content.SniffingRequest
|
||||||
if !sniffingRequest.Enabled {
|
if !sniffingRequest.Enabled {
|
||||||
go d.routedDispatch(ctx, outbound, destination)
|
d.routedDispatch(ctx, outbound, destination)
|
||||||
} else {
|
} else {
|
||||||
go func() {
|
cReader := &cachedReader{
|
||||||
cReader := &cachedReader{
|
reader: outbound.Reader.(*pipe.Reader),
|
||||||
reader: outbound.Reader.(*pipe.Reader),
|
}
|
||||||
|
outbound.Reader = cReader
|
||||||
|
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network)
|
||||||
|
if err == nil {
|
||||||
|
content.Protocol = result.Protocol()
|
||||||
|
}
|
||||||
|
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
|
||||||
|
domain := result.Domain()
|
||||||
|
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
destination.Address = net.ParseAddress(domain)
|
||||||
|
protocol := result.Protocol()
|
||||||
|
if resComp, ok := result.(SnifferResultComposite); ok {
|
||||||
|
protocol = resComp.ProtocolForDomainResult()
|
||||||
}
|
}
|
||||||
outbound.Reader = cReader
|
isFakeIP := false
|
||||||
result, err := sniffer(ctx, cReader, sniffingRequest.MetadataOnly, destination.Network)
|
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(ob.Target.Address) {
|
||||||
if err == nil {
|
isFakeIP = true
|
||||||
content.Protocol = result.Protocol()
|
|
||||||
}
|
}
|
||||||
if err == nil && d.shouldOverride(ctx, result, sniffingRequest, destination) {
|
if sniffingRequest.RouteOnly && protocol != "fakedns" && protocol != "fakedns+others" && !isFakeIP {
|
||||||
domain := result.Domain()
|
ob.RouteTarget = destination
|
||||||
newError("sniffed domain: ", domain).WriteToLog(session.ExportIDToError(ctx))
|
} else {
|
||||||
destination.Address = net.ParseAddress(domain)
|
ob.Target = destination
|
||||||
if sniffingRequest.RouteOnly && result.Protocol() != "fakedns" {
|
|
||||||
ob.RouteTarget = destination
|
|
||||||
} else {
|
|
||||||
ob.Target = destination
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
d.routedDispatch(ctx, outbound, destination)
|
}
|
||||||
}()
|
d.routedDispatch(ctx, outbound, destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -415,9 +382,9 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
|
||||||
}
|
}
|
||||||
return contentResult, contentErr
|
return contentResult, contentErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
|
||||||
ob := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ob := outbounds[len(outbounds)-1]
|
||||||
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
|
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
|
||||||
proxied := hosts.LookupHosts(ob.Target.String())
|
proxied := hosts.LookupHosts(ob.Target.String())
|
||||||
if proxied != nil {
|
if proxied != nil {
|
||||||
|
@ -474,6 +441,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ob.Tag = handler.Tag()
|
||||||
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
|
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
|
||||||
if tag := handler.Tag(); tag != "" {
|
if tag := handler.Tag(); tag != "" {
|
||||||
if inTag == "" {
|
if inTag == "" {
|
||||||
|
|
|
@ -26,11 +26,12 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error)
|
||||||
return protocolSnifferWithMetadata{}, errNotInit
|
return protocolSnifferWithMetadata{}, errNotInit
|
||||||
}
|
}
|
||||||
return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
|
return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
|
||||||
Target := session.OutboundFromContext(ctx).Target
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if Target.Network == net.Network_TCP || Target.Network == net.Network_UDP {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(Target.Address)
|
if ob.Target.Network == net.Network_TCP || ob.Target.Network == net.Network_UDP {
|
||||||
|
domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(ob.Target.Address)
|
||||||
if domainFromFakeDNS != "" {
|
if domainFromFakeDNS != "" {
|
||||||
newError("fake dns got domain: ", domainFromFakeDNS, " for ip: ", Target.Address.String()).WriteToLog(session.ExportIDToError(ctx))
|
newError("fake dns got domain: ", domainFromFakeDNS, " for ip: ", ob.Target.Address.String()).WriteToLog(session.ExportIDToError(ctx))
|
||||||
return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
|
return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +39,7 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error)
|
||||||
if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil {
|
if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil {
|
||||||
ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt)
|
ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt)
|
||||||
if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {
|
if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {
|
||||||
inPool := fkr0.IsIPInIPPool(Target.Address)
|
inPool := fkr0.IsIPInIPPool(ob.Target.Address)
|
||||||
ipAddressInRangeValue.addressInRange = &inPool
|
ipAddressInRangeValue.addressInRange = &inPool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/dns/config.proto
|
// source: app/dns/config.proto
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
@ -134,6 +134,7 @@ type NameServer struct {
|
||||||
PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"`
|
PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"`
|
||||||
Geoip []*router.GeoIP `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"`
|
Geoip []*router.GeoIP `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"`
|
||||||
OriginalRules []*NameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"`
|
OriginalRules []*NameServer_OriginalRule `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"`
|
||||||
|
QueryStrategy QueryStrategy `protobuf:"varint,7,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *NameServer) Reset() {
|
func (x *NameServer) Reset() {
|
||||||
|
@ -210,6 +211,13 @@ func (x *NameServer) GetOriginalRules() []*NameServer_OriginalRule {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *NameServer) GetQueryStrategy() QueryStrategy {
|
||||||
|
if x != nil {
|
||||||
|
return x.QueryStrategy
|
||||||
|
}
|
||||||
|
return QueryStrategy_USE_IP
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@ -219,14 +227,14 @@ type Config struct {
|
||||||
// the moment. A special value 'localhost' as a domain address can be set to
|
// the moment. A special value 'localhost' as a domain address can be set to
|
||||||
// use DNS on local system.
|
// use DNS on local system.
|
||||||
//
|
//
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/dns/config.proto.
|
||||||
NameServers []*net.Endpoint `protobuf:"bytes,1,rep,name=NameServers,proto3" json:"NameServers,omitempty"`
|
NameServers []*net.Endpoint `protobuf:"bytes,1,rep,name=NameServers,proto3" json:"NameServers,omitempty"`
|
||||||
// NameServer list used by this DNS client.
|
// NameServer list used by this DNS client.
|
||||||
NameServer []*NameServer `protobuf:"bytes,5,rep,name=name_server,json=nameServer,proto3" json:"name_server,omitempty"`
|
NameServer []*NameServer `protobuf:"bytes,5,rep,name=name_server,json=nameServer,proto3" json:"name_server,omitempty"`
|
||||||
// Static hosts. Domain to IP.
|
// Static hosts. Domain to IP.
|
||||||
// Deprecated. Use static_hosts.
|
// Deprecated. Use static_hosts.
|
||||||
//
|
//
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/dns/config.proto.
|
||||||
Hosts map[string]*net.IPOrDomain `protobuf:"bytes,2,rep,name=Hosts,proto3" json:"Hosts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
Hosts map[string]*net.IPOrDomain `protobuf:"bytes,2,rep,name=Hosts,proto3" json:"Hosts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
// Client IP for EDNS client subnet. Must be 4 bytes (IPv4) or 16 bytes
|
// Client IP for EDNS client subnet. Must be 4 bytes (IPv4) or 16 bytes
|
||||||
// (IPv6).
|
// (IPv6).
|
||||||
|
@ -273,7 +281,7 @@ func (*Config) Descriptor() ([]byte, []int) {
|
||||||
return file_app_dns_config_proto_rawDescGZIP(), []int{1}
|
return file_app_dns_config_proto_rawDescGZIP(), []int{1}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/dns/config.proto.
|
||||||
func (x *Config) GetNameServers() []*net.Endpoint {
|
func (x *Config) GetNameServers() []*net.Endpoint {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.NameServers
|
return x.NameServers
|
||||||
|
@ -288,7 +296,7 @@ func (x *Config) GetNameServer() []*NameServer {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/dns/config.proto.
|
||||||
func (x *Config) GetHosts() map[string]*net.IPOrDomain {
|
func (x *Config) GetHosts() map[string]*net.IPOrDomain {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Hosts
|
return x.Hosts
|
||||||
|
@ -538,7 +546,7 @@ var file_app_dns_config_proto_rawDesc = []byte{
|
||||||
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69,
|
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x65, 0x73, 0x74, 0x69,
|
||||||
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70,
|
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70,
|
||||||
0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
|
0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xee, 0x03, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb2, 0x04, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65,
|
||||||
0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
|
0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
||||||
|
@ -559,77 +567,81 @@ var file_app_dns_config_proto_rawDesc = []byte{
|
||||||
0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76,
|
0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76,
|
||||||
0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52,
|
0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52,
|
||||||
0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x1a, 0x5e,
|
0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x42,
|
||||||
0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
||||||
0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20,
|
0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
||||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f,
|
0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74,
|
||||||
0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65,
|
0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65,
|
||||||
0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
0x67, 0x79, 0x1a, 0x5e, 0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36,
|
0x6d, 0x61, 0x69, 0x6e, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x0a, 0x0c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12,
|
0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e,
|
||||||
0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75,
|
0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67,
|
||||||
0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d,
|
0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f,
|
||||||
0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xef, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61,
|
||||||
0x67, 0x12, 0x3f, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73,
|
0x69, 0x6e, 0x1a, 0x36, 0x0a, 0x0c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75,
|
||||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
|
0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e,
|
0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02,
|
||||||
0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
|
0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xef, 0x05, 0x0a, 0x06, 0x43,
|
||||||
0x72, 0x73, 0x12, 0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65,
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3f, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72,
|
||||||
0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
|
0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61,
|
||||||
0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
|
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64,
|
||||||
0x72, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x39, 0x0a,
|
0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53,
|
||||||
0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78,
|
0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73,
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66,
|
0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72,
|
||||||
0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x02, 0x18,
|
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53,
|
||||||
0x01, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65,
|
0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
|
||||||
0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69,
|
0x72, 0x12, 0x39, 0x0a, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
|
||||||
0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f,
|
0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e,
|
||||||
0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72,
|
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72,
|
||||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
0x79, 0x42, 0x02, 0x18, 0x01, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09,
|
||||||
0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x73,
|
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||||
0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61,
|
0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61,
|
||||||
0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x22, 0x0a, 0x0c,
|
0x74, 0x69, 0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||||
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08, 0x20, 0x01,
|
0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43,
|
||||||
0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65,
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
|
||||||
0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65,
|
0x67, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10,
|
||||||
0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67,
|
||||||
0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72,
|
0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65,
|
||||||
0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61,
|
0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43,
|
||||||
0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46,
|
0x61, 0x63, 0x68, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74,
|
||||||
0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64,
|
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78,
|
||||||
0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x36,
|
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72,
|
||||||
0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63,
|
0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79,
|
||||||
0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16,
|
0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61,
|
||||||
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49,
|
0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28,
|
||||||
0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x55, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45,
|
0x08, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61,
|
||||||
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x63, 0x6b, 0x12, 0x36, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c,
|
||||||
0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
|
0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01,
|
||||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d,
|
0x28, 0x08, 0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62,
|
||||||
0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61,
|
0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x55, 0x0a, 0x0a, 0x48, 0x6f,
|
||||||
0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x92, 0x01,
|
0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
|
||||||
0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a,
|
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61,
|
||||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72,
|
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72,
|
||||||
0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74,
|
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
|
||||||
0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20,
|
0x01, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69,
|
0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
|
||||||
0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70,
|
0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44,
|
||||||
0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20,
|
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61,
|
0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69,
|
||||||
0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61,
|
0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12,
|
||||||
0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08,
|
0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12,
|
||||||
0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64,
|
0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69,
|
||||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f,
|
0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64,
|
||||||
0x72, 0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a,
|
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x2a, 0x45, 0x0a, 0x12,
|
||||||
0x35, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
|
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79,
|
||||||
0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07,
|
0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09,
|
||||||
0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45,
|
0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b,
|
||||||
0x5f, 0x49, 0x50, 0x36, 0x10, 0x02, 0x42, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
|
0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65,
|
||||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69,
|
0x78, 0x10, 0x03, 0x2a, 0x35, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61,
|
||||||
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72,
|
0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x00,
|
||||||
0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa,
|
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a,
|
||||||
0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06,
|
0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x02, 0x42, 0x46, 0x0a, 0x10, 0x63, 0x6f,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x50, 0x01,
|
||||||
|
0x5a, 0x21, 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,
|
||||||
|
0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44,
|
||||||
|
0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -664,19 +676,20 @@ var file_app_dns_config_proto_depIdxs = []int32{
|
||||||
4, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain
|
4, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain
|
||||||
9, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP
|
9, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP
|
||||||
5, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule
|
5, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule
|
||||||
8, // 4: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint
|
1, // 4: xray.app.dns.NameServer.query_strategy:type_name -> xray.app.dns.QueryStrategy
|
||||||
2, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
|
8, // 5: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint
|
||||||
6, // 6: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry
|
2, // 6: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
|
||||||
7, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
|
6, // 7: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry
|
||||||
1, // 8: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy
|
7, // 8: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
|
||||||
0, // 9: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType
|
1, // 9: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy
|
||||||
10, // 10: xray.app.dns.Config.HostsEntry.value:type_name -> xray.common.net.IPOrDomain
|
0, // 10: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType
|
||||||
0, // 11: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
|
10, // 11: xray.app.dns.Config.HostsEntry.value:type_name -> xray.common.net.IPOrDomain
|
||||||
12, // [12:12] is the sub-list for method output_type
|
0, // 12: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
|
||||||
12, // [12:12] is the sub-list for method input_type
|
13, // [13:13] is the sub-list for method output_type
|
||||||
12, // [12:12] is the sub-list for extension type_name
|
13, // [13:13] is the sub-list for method input_type
|
||||||
12, // [12:12] is the sub-list for extension extendee
|
13, // [13:13] is the sub-list for extension type_name
|
||||||
0, // [0:12] is the sub-list for field type_name
|
13, // [13:13] is the sub-list for extension extendee
|
||||||
|
0, // [0:13] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_app_dns_config_proto_init() }
|
func init() { file_app_dns_config_proto_init() }
|
||||||
|
|
|
@ -28,6 +28,7 @@ message NameServer {
|
||||||
repeated PriorityDomain prioritized_domain = 2;
|
repeated PriorityDomain prioritized_domain = 2;
|
||||||
repeated xray.app.router.GeoIP geoip = 3;
|
repeated xray.app.router.GeoIP geoip = 3;
|
||||||
repeated OriginalRule original_rules = 4;
|
repeated OriginalRule original_rules = 4;
|
||||||
|
QueryStrategy query_strategy = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DomainMatchingType {
|
enum DomainMatchingType {
|
||||||
|
|
|
@ -181,9 +181,7 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize the FQDN form query
|
// Normalize the FQDN form query
|
||||||
if strings.HasSuffix(domain, ".") {
|
domain = strings.TrimSuffix(domain, ".")
|
||||||
domain = domain[:len(domain)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Static host lookup
|
// Static host lookup
|
||||||
switch addrs := s.hosts.Lookup(domain, option); {
|
switch addrs := s.hosts.Lookup(domain, option); {
|
||||||
|
@ -215,7 +213,8 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) {
|
||||||
newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog()
|
newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog()
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch {
|
// 5 for RcodeRefused in miekg/dns, hardcode to reduce binary size
|
||||||
|
if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch && err != dns.ErrEmptyResponse && dns.RCodeFromError(err) != 5 {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
_ "github.com/xtls/xray-core/app/proxyman/outbound"
|
_ "github.com/xtls/xray-core/app/proxyman/outbound"
|
||||||
"github.com/xtls/xray-core/app/router"
|
"github.com/xtls/xray-core/app/router"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
|
@ -260,7 +261,7 @@ func TestUDPServer(t *testing.T) {
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
FakeEnable: false,
|
FakeEnable: false,
|
||||||
})
|
})
|
||||||
if err != feature_dns.ErrEmptyResponse {
|
if !errors.AllEqual(feature_dns.ErrEmptyResponse, errors.Cause(err)) {
|
||||||
t.Fatal("error: ", err)
|
t.Fatal("error: ", err)
|
||||||
}
|
}
|
||||||
if len(ips) != 0 {
|
if len(ips) != 0 {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/dns/fakedns/fakedns.proto
|
// source: app/dns/fakedns/fakedns.proto
|
||||||
|
|
||||||
package fakedns
|
package fakedns
|
||||||
|
|
|
@ -35,7 +35,7 @@ type Client struct {
|
||||||
var errExpectedIPNonMatch = errors.New("expectIPs not match")
|
var errExpectedIPNonMatch = errors.New("expectIPs not match")
|
||||||
|
|
||||||
// NewServer creates a name server object according to the network destination url.
|
// NewServer creates a name server object according to the network destination url.
|
||||||
func NewServer(dest net.Destination, dispatcher routing.Dispatcher) (Server, error) {
|
func NewServer(dest net.Destination, dispatcher routing.Dispatcher, queryStrategy QueryStrategy) (Server, error) {
|
||||||
if address := dest.Address; address.Family().IsDomain() {
|
if address := dest.Address; address.Family().IsDomain() {
|
||||||
u, err := url.Parse(address.Domain())
|
u, err := url.Parse(address.Domain())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -45,15 +45,15 @@ func NewServer(dest net.Destination, dispatcher routing.Dispatcher) (Server, err
|
||||||
case strings.EqualFold(u.String(), "localhost"):
|
case strings.EqualFold(u.String(), "localhost"):
|
||||||
return NewLocalNameServer(), nil
|
return NewLocalNameServer(), nil
|
||||||
case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode
|
case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode
|
||||||
return NewDoHNameServer(u, dispatcher)
|
return NewDoHNameServer(u, dispatcher, queryStrategy)
|
||||||
case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode
|
case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode
|
||||||
return NewDoHLocalNameServer(u), nil
|
return NewDoHLocalNameServer(u, queryStrategy), nil
|
||||||
case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
|
case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
|
||||||
return NewQUICNameServer(u)
|
return NewQUICNameServer(u, queryStrategy)
|
||||||
case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
|
case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
|
||||||
return NewTCPNameServer(u, dispatcher)
|
return NewTCPNameServer(u, dispatcher, queryStrategy)
|
||||||
case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode
|
case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode
|
||||||
return NewTCPLocalNameServer(u)
|
return NewTCPLocalNameServer(u, queryStrategy)
|
||||||
case strings.EqualFold(u.String(), "fakedns"):
|
case strings.EqualFold(u.String(), "fakedns"):
|
||||||
return NewFakeDNSServer(), nil
|
return NewFakeDNSServer(), nil
|
||||||
}
|
}
|
||||||
|
@ -68,12 +68,19 @@ func NewServer(dest net.Destination, dispatcher routing.Dispatcher) (Server, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs.
|
// NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs.
|
||||||
func NewClient(ctx context.Context, ns *NameServer, clientIP net.IP, container router.GeoIPMatcherContainer, matcherInfos *[]*DomainMatcherInfo, updateDomainRule func(strmatcher.Matcher, int, []*DomainMatcherInfo) error) (*Client, error) {
|
func NewClient(
|
||||||
|
ctx context.Context,
|
||||||
|
ns *NameServer,
|
||||||
|
clientIP net.IP,
|
||||||
|
container router.GeoIPMatcherContainer,
|
||||||
|
matcherInfos *[]*DomainMatcherInfo,
|
||||||
|
updateDomainRule func(strmatcher.Matcher, int, []*DomainMatcherInfo) error,
|
||||||
|
) (*Client, error) {
|
||||||
client := &Client{}
|
client := &Client{}
|
||||||
|
|
||||||
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
|
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
|
||||||
// Create a new server for each client for now
|
// Create a new server for each client for now
|
||||||
server, err := NewServer(ns.Address.AsDestination(), dispatcher)
|
server, err := NewServer(ns.Address.AsDestination(), dispatcher, ns.GetQueryStrategy())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newError("failed to create nameserver").Base(err).AtWarning()
|
return newError("failed to create nameserver").Base(err).AtWarning()
|
||||||
}
|
}
|
||||||
|
@ -160,7 +167,7 @@ func NewClient(ctx context.Context, ns *NameServer, clientIP net.IP, container r
|
||||||
func NewSimpleClient(ctx context.Context, endpoint *net.Endpoint, clientIP net.IP) (*Client, error) {
|
func NewSimpleClient(ctx context.Context, endpoint *net.Endpoint, clientIP net.IP) (*Client, error) {
|
||||||
client := &Client{}
|
client := &Client{}
|
||||||
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
|
err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
|
||||||
server, err := NewServer(endpoint.AsDestination(), dispatcher)
|
server, err := NewServer(endpoint.AsDestination(), dispatcher, QueryStrategy_USE_IP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newError("failed to create nameserver").Base(err).AtWarning()
|
return newError("failed to create nameserver").Base(err).AtWarning()
|
||||||
}
|
}
|
||||||
|
@ -218,3 +225,24 @@ func (c *Client) MatchExpectedIPs(domain string, ips []net.IP) ([]net.IP, error)
|
||||||
newError("domain ", domain, " expectIPs ", newIps, " matched at server ", c.Name()).AtDebug().WriteToLog()
|
newError("domain ", domain, " expectIPs ", newIps, " matched at server ", c.Name()).AtDebug().WriteToLog()
|
||||||
return newIps, nil
|
return newIps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ResolveIpOptionOverride(queryStrategy QueryStrategy, ipOption dns.IPOption) dns.IPOption {
|
||||||
|
switch queryStrategy {
|
||||||
|
case QueryStrategy_USE_IP:
|
||||||
|
return ipOption
|
||||||
|
case QueryStrategy_USE_IP4:
|
||||||
|
return dns.IPOption{
|
||||||
|
IPv4Enable: ipOption.IPv4Enable,
|
||||||
|
IPv6Enable: false,
|
||||||
|
FakeEnable: false,
|
||||||
|
}
|
||||||
|
case QueryStrategy_USE_IP6:
|
||||||
|
return dns.IPOption{
|
||||||
|
IPv4Enable: false,
|
||||||
|
IPv6Enable: ipOption.IPv6Enable,
|
||||||
|
FakeEnable: false,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ipOption
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -31,19 +31,20 @@ import (
|
||||||
type DoHNameServer struct {
|
type DoHNameServer struct {
|
||||||
dispatcher routing.Dispatcher
|
dispatcher routing.Dispatcher
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
ips map[string]*record
|
ips map[string]*record
|
||||||
pub *pubsub.Service
|
pub *pubsub.Service
|
||||||
cleanup *task.Periodic
|
cleanup *task.Periodic
|
||||||
reqID uint32
|
reqID uint32
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
dohURL string
|
dohURL string
|
||||||
name string
|
name string
|
||||||
|
queryStrategy QueryStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDoHNameServer creates DOH server object for remote resolving.
|
// NewDoHNameServer creates DOH server object for remote resolving.
|
||||||
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServer, error) {
|
func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, queryStrategy QueryStrategy) (*DoHNameServer, error) {
|
||||||
newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog()
|
newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog()
|
||||||
s := baseDOHNameServer(url, "DOH")
|
s := baseDOHNameServer(url, "DOH", queryStrategy)
|
||||||
|
|
||||||
s.dispatcher = dispatcher
|
s.dispatcher = dispatcher
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
|
@ -90,9 +91,9 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServ
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDoHLocalNameServer creates DOH client object for local resolving
|
// NewDoHLocalNameServer creates DOH client object for local resolving
|
||||||
func NewDoHLocalNameServer(url *url.URL) *DoHNameServer {
|
func NewDoHLocalNameServer(url *url.URL, queryStrategy QueryStrategy) *DoHNameServer {
|
||||||
url.Scheme = "https"
|
url.Scheme = "https"
|
||||||
s := baseDOHNameServer(url, "DOHL")
|
s := baseDOHNameServer(url, "DOHL", queryStrategy)
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
IdleConnTimeout: 90 * time.Second,
|
IdleConnTimeout: 90 * time.Second,
|
||||||
ForceAttemptHTTP2: true,
|
ForceAttemptHTTP2: true,
|
||||||
|
@ -122,12 +123,13 @@ func NewDoHLocalNameServer(url *url.URL) *DoHNameServer {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func baseDOHNameServer(url *url.URL, prefix string) *DoHNameServer {
|
func baseDOHNameServer(url *url.URL, prefix string, queryStrategy QueryStrategy) *DoHNameServer {
|
||||||
s := &DoHNameServer{
|
s := &DoHNameServer{
|
||||||
ips: make(map[string]*record),
|
ips: make(map[string]*record),
|
||||||
pub: pubsub.NewService(),
|
pub: pubsub.NewService(),
|
||||||
name: prefix + "//" + url.Host,
|
name: prefix + "//" + url.Host,
|
||||||
dohURL: url.String(),
|
dohURL: url.String(),
|
||||||
|
queryStrategy: queryStrategy,
|
||||||
}
|
}
|
||||||
s.cleanup = &task.Periodic{
|
s.cleanup = &task.Periodic{
|
||||||
Interval: time.Minute,
|
Interval: time.Minute,
|
||||||
|
@ -353,6 +355,10 @@ func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOpt
|
||||||
// QueryIP implements Server.
|
// QueryIP implements Server.
|
||||||
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { // nolint: dupl
|
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { // nolint: dupl
|
||||||
fqdn := Fqdn(domain)
|
fqdn := Fqdn(domain)
|
||||||
|
option = ResolveIpOptionOverride(s.queryStrategy, option)
|
||||||
|
if !option.IPv4Enable && !option.IPv6Enable {
|
||||||
|
return nil, dns_feature.ErrEmptyResponse
|
||||||
|
}
|
||||||
|
|
||||||
if disableCache {
|
if disableCache {
|
||||||
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
||||||
|
|
|
@ -17,7 +17,7 @@ func TestDOHNameServer(t *testing.T) {
|
||||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
s := NewDoHLocalNameServer(url)
|
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
|
@ -34,7 +34,7 @@ func TestDOHNameServerWithCache(t *testing.T) {
|
||||||
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
s := NewDoHLocalNameServer(url)
|
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
|
@ -57,3 +57,49 @@ func TestDOHNameServerWithCache(t *testing.T) {
|
||||||
t.Fatal(r)
|
t.Fatal(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDOHNameServerWithIPv4Override(t *testing.T) {
|
||||||
|
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP4)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
}, false)
|
||||||
|
cancel()
|
||||||
|
common.Must(err)
|
||||||
|
if len(ips) == 0 {
|
||||||
|
t.Error("expect some ips, but got 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range ips {
|
||||||
|
if len(ip) != net.IPv4len {
|
||||||
|
t.Error("expect only IPv4 response from DNS query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDOHNameServerWithIPv6Override(t *testing.T) {
|
||||||
|
url, err := url.Parse("https+local://1.1.1.1/dns-query")
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP6)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
}, false)
|
||||||
|
cancel()
|
||||||
|
common.Must(err)
|
||||||
|
if len(ips) == 0 {
|
||||||
|
t.Error("expect some ips, but got 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range ips {
|
||||||
|
if len(ip) != net.IPv6len {
|
||||||
|
t.Error("expect only IPv6 response from DNS query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
@ -24,28 +26,29 @@ import (
|
||||||
|
|
||||||
// NextProtoDQ - During connection establishment, DNS/QUIC support is indicated
|
// NextProtoDQ - During connection establishment, DNS/QUIC support is indicated
|
||||||
// by selecting the ALPN token "dq" in the crypto handshake.
|
// by selecting the ALPN token "dq" in the crypto handshake.
|
||||||
const NextProtoDQ = "doq-i00"
|
const NextProtoDQ = "doq"
|
||||||
|
|
||||||
const handshakeTimeout = time.Second * 8
|
const handshakeTimeout = time.Second * 8
|
||||||
|
|
||||||
// QUICNameServer implemented DNS over QUIC
|
// QUICNameServer implemented DNS over QUIC
|
||||||
type QUICNameServer struct {
|
type QUICNameServer struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
ips map[string]*record
|
ips map[string]*record
|
||||||
pub *pubsub.Service
|
pub *pubsub.Service
|
||||||
cleanup *task.Periodic
|
cleanup *task.Periodic
|
||||||
reqID uint32
|
reqID uint32
|
||||||
name string
|
name string
|
||||||
destination *net.Destination
|
destination *net.Destination
|
||||||
connection quic.Connection
|
connection quic.Connection
|
||||||
|
queryStrategy QueryStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewQUICNameServer creates DNS-over-QUIC client object for local resolving
|
// NewQUICNameServer creates DNS-over-QUIC client object for local resolving
|
||||||
func NewQUICNameServer(url *url.URL) (*QUICNameServer, error) {
|
func NewQUICNameServer(url *url.URL, queryStrategy QueryStrategy) (*QUICNameServer, error) {
|
||||||
newError("DNS: created Local DNS-over-QUIC client for ", url.String()).AtInfo().WriteToLog()
|
newError("DNS: created Local DNS-over-QUIC client for ", url.String()).AtInfo().WriteToLog()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
port := net.Port(784)
|
port := net.Port(853)
|
||||||
if url.Port() != "" {
|
if url.Port() != "" {
|
||||||
port, err = net.PortFromString(url.Port())
|
port, err = net.PortFromString(url.Port())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -55,10 +58,11 @@ func NewQUICNameServer(url *url.URL) (*QUICNameServer, error) {
|
||||||
dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port)
|
dest := net.UDPDestination(net.ParseAddress(url.Hostname()), port)
|
||||||
|
|
||||||
s := &QUICNameServer{
|
s := &QUICNameServer{
|
||||||
ips: make(map[string]*record),
|
ips: make(map[string]*record),
|
||||||
pub: pubsub.NewService(),
|
pub: pubsub.NewService(),
|
||||||
name: url.String(),
|
name: url.String(),
|
||||||
destination: &dest,
|
destination: &dest,
|
||||||
|
queryStrategy: queryStrategy,
|
||||||
}
|
}
|
||||||
s.cleanup = &task.Periodic{
|
s.cleanup = &task.Periodic{
|
||||||
Interval: time.Minute,
|
Interval: time.Minute,
|
||||||
|
@ -192,13 +196,18 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dnsReqBuf := buf.New()
|
||||||
|
binary.Write(dnsReqBuf, binary.BigEndian, uint16(b.Len()))
|
||||||
|
dnsReqBuf.Write(b.Bytes())
|
||||||
|
b.Release()
|
||||||
|
|
||||||
conn, err := s.openStream(dnsCtx)
|
conn, err := s.openStream(dnsCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError("failed to open quic connection").Base(err).AtError().WriteToLog()
|
newError("failed to open quic connection").Base(err).AtError().WriteToLog()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = conn.Write(b.Bytes())
|
_, err = conn.Write(dnsReqBuf.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newError("failed to send query").Base(err).AtError().WriteToLog()
|
newError("failed to send query").Base(err).AtError().WriteToLog()
|
||||||
return
|
return
|
||||||
|
@ -208,9 +217,21 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP
|
||||||
|
|
||||||
respBuf := buf.New()
|
respBuf := buf.New()
|
||||||
defer respBuf.Release()
|
defer respBuf.Release()
|
||||||
n, err := respBuf.ReadFrom(conn)
|
n, err := respBuf.ReadFullFrom(conn, 2)
|
||||||
if err != nil && n == 0 {
|
if err != nil && n == 0 {
|
||||||
newError("failed to read response").Base(err).AtError().WriteToLog()
|
newError("failed to read response length").Base(err).AtError().WriteToLog()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var length int16
|
||||||
|
err = binary.Read(bytes.NewReader(respBuf.Bytes()), binary.BigEndian, &length)
|
||||||
|
if err != nil {
|
||||||
|
newError("failed to parse response length").Base(err).AtError().WriteToLog()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
respBuf.Clear()
|
||||||
|
n, err = respBuf.ReadFullFrom(conn, int32(length))
|
||||||
|
if err != nil && n == 0 {
|
||||||
|
newError("failed to read response length").Base(err).AtError().WriteToLog()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +290,10 @@ func (s *QUICNameServer) findIPsForDomain(domain string, option dns_feature.IPOp
|
||||||
// QueryIP is called from dns.Server->queryIPTimeout
|
// QueryIP is called from dns.Server->queryIPTimeout
|
||||||
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) {
|
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) {
|
||||||
fqdn := Fqdn(domain)
|
fqdn := Fqdn(domain)
|
||||||
|
option = ResolveIpOptionOverride(s.queryStrategy, option)
|
||||||
|
if !option.IPv4Enable && !option.IPv6Enable {
|
||||||
|
return nil, dns_feature.ErrEmptyResponse
|
||||||
|
}
|
||||||
|
|
||||||
if disableCache {
|
if disableCache {
|
||||||
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
||||||
|
@ -373,8 +398,8 @@ func (s *QUICNameServer) openConnection() (quic.Connection, error) {
|
||||||
quicConfig := &quic.Config{
|
quicConfig := &quic.Config{
|
||||||
HandshakeIdleTimeout: handshakeTimeout,
|
HandshakeIdleTimeout: handshakeTimeout,
|
||||||
}
|
}
|
||||||
|
tlsConfig.ServerName = s.destination.Address.String()
|
||||||
conn, err := quic.DialAddrContext(context.Background(), s.destination.NetAddr(), tlsConfig.GetTLSConfig(tls.WithNextProto("http/1.1", http2.NextProtoTLS, NextProtoDQ)), quicConfig)
|
conn, err := quic.DialAddr(context.Background(), s.destination.NetAddr(), tlsConfig.GetTLSConfig(tls.WithNextProto("http/1.1", http2.NextProtoTLS, NextProtoDQ)), quicConfig)
|
||||||
log.Record(&log.AccessMessage{
|
log.Record(&log.AccessMessage{
|
||||||
From: "DNS",
|
From: "DNS",
|
||||||
To: s.destination,
|
To: s.destination,
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
func TestQUICNameServer(t *testing.T) {
|
func TestQUICNameServer(t *testing.T) {
|
||||||
url, err := url.Parse("quic://dns.adguard.com")
|
url, err := url.Parse("quic://dns.adguard.com")
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
s, err := NewQUICNameServer(url)
|
s, err := NewQUICNameServer(url, QueryStrategy_USE_IP)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{
|
||||||
|
@ -40,3 +40,49 @@ func TestQUICNameServer(t *testing.T) {
|
||||||
t.Fatal(r)
|
t.Fatal(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestQUICNameServerWithIPv4Override(t *testing.T) {
|
||||||
|
url, err := url.Parse("quic://dns.adguard.com")
|
||||||
|
common.Must(err)
|
||||||
|
s, err := NewQUICNameServer(url, QueryStrategy_USE_IP4)
|
||||||
|
common.Must(err)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||||
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
}, false)
|
||||||
|
cancel()
|
||||||
|
common.Must(err)
|
||||||
|
if len(ips) == 0 {
|
||||||
|
t.Error("expect some ips, but got 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range ips {
|
||||||
|
if len(ip) != net.IPv4len {
|
||||||
|
t.Error("expect only IPv4 response from DNS query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQUICNameServerWithIPv6Override(t *testing.T) {
|
||||||
|
url, err := url.Parse("quic://dns.adguard.com")
|
||||||
|
common.Must(err)
|
||||||
|
s, err := NewQUICNameServer(url, QueryStrategy_USE_IP6)
|
||||||
|
common.Must(err)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||||
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
}, false)
|
||||||
|
cancel()
|
||||||
|
common.Must(err)
|
||||||
|
if len(ips) == 0 {
|
||||||
|
t.Error("expect some ips, but got 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range ips {
|
||||||
|
if len(ip) != net.IPv6len {
|
||||||
|
t.Error("expect only IPv6 response from DNS query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -27,18 +27,23 @@ import (
|
||||||
// TCPNameServer implemented DNS over TCP (RFC7766).
|
// TCPNameServer implemented DNS over TCP (RFC7766).
|
||||||
type TCPNameServer struct {
|
type TCPNameServer struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
name string
|
name string
|
||||||
destination *net.Destination
|
destination *net.Destination
|
||||||
ips map[string]*record
|
ips map[string]*record
|
||||||
pub *pubsub.Service
|
pub *pubsub.Service
|
||||||
cleanup *task.Periodic
|
cleanup *task.Periodic
|
||||||
reqID uint32
|
reqID uint32
|
||||||
dial func(context.Context) (net.Conn, error)
|
dial func(context.Context) (net.Conn, error)
|
||||||
|
queryStrategy QueryStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTCPNameServer creates DNS over TCP server object for remote resolving.
|
// NewTCPNameServer creates DNS over TCP server object for remote resolving.
|
||||||
func NewTCPNameServer(url *url.URL, dispatcher routing.Dispatcher) (*TCPNameServer, error) {
|
func NewTCPNameServer(
|
||||||
s, err := baseTCPNameServer(url, "TCP")
|
url *url.URL,
|
||||||
|
dispatcher routing.Dispatcher,
|
||||||
|
queryStrategy QueryStrategy,
|
||||||
|
) (*TCPNameServer, error) {
|
||||||
|
s, err := baseTCPNameServer(url, "TCP", queryStrategy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -59,8 +64,8 @@ func NewTCPNameServer(url *url.URL, dispatcher routing.Dispatcher) (*TCPNameServ
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTCPLocalNameServer creates DNS over TCP client object for local resolving
|
// NewTCPLocalNameServer creates DNS over TCP client object for local resolving
|
||||||
func NewTCPLocalNameServer(url *url.URL) (*TCPNameServer, error) {
|
func NewTCPLocalNameServer(url *url.URL, queryStrategy QueryStrategy) (*TCPNameServer, error) {
|
||||||
s, err := baseTCPNameServer(url, "TCPL")
|
s, err := baseTCPNameServer(url, "TCPL", queryStrategy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -72,22 +77,22 @@ func NewTCPLocalNameServer(url *url.URL) (*TCPNameServer, error) {
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func baseTCPNameServer(url *url.URL, prefix string) (*TCPNameServer, error) {
|
func baseTCPNameServer(url *url.URL, prefix string, queryStrategy QueryStrategy) (*TCPNameServer, error) {
|
||||||
var err error
|
|
||||||
port := net.Port(53)
|
port := net.Port(53)
|
||||||
if url.Port() != "" {
|
if url.Port() != "" {
|
||||||
port, err = net.PortFromString(url.Port())
|
var err error
|
||||||
if err != nil {
|
if port, err = net.PortFromString(url.Port()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dest := net.TCPDestination(net.ParseAddress(url.Hostname()), port)
|
dest := net.TCPDestination(net.ParseAddress(url.Hostname()), port)
|
||||||
|
|
||||||
s := &TCPNameServer{
|
s := &TCPNameServer{
|
||||||
destination: &dest,
|
destination: &dest,
|
||||||
ips: make(map[string]*record),
|
ips: make(map[string]*record),
|
||||||
pub: pubsub.NewService(),
|
pub: pubsub.NewService(),
|
||||||
name: prefix + "//" + dest.NetAddr(),
|
name: prefix + "//" + dest.NetAddr(),
|
||||||
|
queryStrategy: queryStrategy,
|
||||||
}
|
}
|
||||||
s.cleanup = &task.Periodic{
|
s.cleanup = &task.Periodic{
|
||||||
Interval: time.Minute,
|
Interval: time.Minute,
|
||||||
|
@ -308,6 +313,10 @@ func (s *TCPNameServer) findIPsForDomain(domain string, option dns_feature.IPOpt
|
||||||
// QueryIP implements Server.
|
// QueryIP implements Server.
|
||||||
func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) {
|
func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) {
|
||||||
fqdn := Fqdn(domain)
|
fqdn := Fqdn(domain)
|
||||||
|
option = ResolveIpOptionOverride(s.queryStrategy, option)
|
||||||
|
if !option.IPv4Enable && !option.IPv6Enable {
|
||||||
|
return nil, dns_feature.ErrEmptyResponse
|
||||||
|
}
|
||||||
|
|
||||||
if disableCache {
|
if disableCache {
|
||||||
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog()
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
func TestTCPLocalNameServer(t *testing.T) {
|
func TestTCPLocalNameServer(t *testing.T) {
|
||||||
url, err := url.Parse("tcp+local://8.8.8.8")
|
url, err := url.Parse("tcp+local://8.8.8.8")
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
s, err := NewTCPLocalNameServer(url)
|
s, err := NewTCPLocalNameServer(url, QueryStrategy_USE_IP)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
|
@ -33,7 +33,7 @@ func TestTCPLocalNameServer(t *testing.T) {
|
||||||
func TestTCPLocalNameServerWithCache(t *testing.T) {
|
func TestTCPLocalNameServerWithCache(t *testing.T) {
|
||||||
url, err := url.Parse("tcp+local://8.8.8.8")
|
url, err := url.Parse("tcp+local://8.8.8.8")
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
s, err := NewTCPLocalNameServer(url)
|
s, err := NewTCPLocalNameServer(url, QueryStrategy_USE_IP)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
|
@ -57,3 +57,51 @@ func TestTCPLocalNameServerWithCache(t *testing.T) {
|
||||||
t.Fatal(r)
|
t.Fatal(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTCPLocalNameServerWithIPv4Override(t *testing.T) {
|
||||||
|
url, err := url.Parse("tcp+local://8.8.8.8")
|
||||||
|
common.Must(err)
|
||||||
|
s, err := NewTCPLocalNameServer(url, QueryStrategy_USE_IP4)
|
||||||
|
common.Must(err)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
}, false)
|
||||||
|
cancel()
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
if len(ips) == 0 {
|
||||||
|
t.Error("expect some ips, but got 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range ips {
|
||||||
|
if len(ip) != net.IPv4len {
|
||||||
|
t.Error("expect only IPv4 response from DNS query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTCPLocalNameServerWithIPv6Override(t *testing.T) {
|
||||||
|
url, err := url.Parse("tcp+local://8.8.8.8")
|
||||||
|
common.Must(err)
|
||||||
|
s, err := NewTCPLocalNameServer(url, QueryStrategy_USE_IP6)
|
||||||
|
common.Must(err)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
|
||||||
|
IPv4Enable: true,
|
||||||
|
IPv6Enable: true,
|
||||||
|
}, false)
|
||||||
|
cancel()
|
||||||
|
common.Must(err)
|
||||||
|
|
||||||
|
if len(ips) == 0 {
|
||||||
|
t.Error("expect some ips, but got 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range ips {
|
||||||
|
if len(ip) != net.IPv6len {
|
||||||
|
t.Error("expect only IPv6 response from DNS query")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/log/command/config.proto
|
// source: app/log/command/config.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// - protoc-gen-go-grpc v1.2.0
|
// - protoc-gen-go-grpc v1.3.0
|
||||||
// - protoc v3.21.12
|
// - protoc v5.27.0
|
||||||
// source: app/log/command/config.proto
|
// source: app/log/command/config.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
@ -18,6 +18,10 @@ import (
|
||||||
// Requires gRPC-Go v1.32.0 or later.
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
const _ = grpc.SupportPackageIsVersion7
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
const (
|
||||||
|
LoggerService_RestartLogger_FullMethodName = "/xray.app.log.command.LoggerService/RestartLogger"
|
||||||
|
)
|
||||||
|
|
||||||
// LoggerServiceClient is the client API for LoggerService service.
|
// LoggerServiceClient is the client API for LoggerService service.
|
||||||
//
|
//
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
@ -35,7 +39,7 @@ func NewLoggerServiceClient(cc grpc.ClientConnInterface) LoggerServiceClient {
|
||||||
|
|
||||||
func (c *loggerServiceClient) RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) {
|
func (c *loggerServiceClient) RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) {
|
||||||
out := new(RestartLoggerResponse)
|
out := new(RestartLoggerResponse)
|
||||||
err := c.cc.Invoke(ctx, "/xray.app.log.command.LoggerService/RestartLogger", in, out, opts...)
|
err := c.cc.Invoke(ctx, LoggerService_RestartLogger_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -80,7 +84,7 @@ func _LoggerService_RestartLogger_Handler(srv interface{}, ctx context.Context,
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/xray.app.log.command.LoggerService/RestartLogger",
|
FullMethod: LoggerService_RestartLogger_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(LoggerServiceServer).RestartLogger(ctx, req.(*RestartLoggerRequest))
|
return srv.(LoggerServiceServer).RestartLogger(ctx, req.(*RestartLoggerRequest))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/log/config.proto
|
// source: app/log/config.proto
|
||||||
|
|
||||||
package log
|
package log
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/metrics/config.proto
|
// source: app/metrics/config.proto
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package burst
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/v2fly/v2ray-core/v4/common/errors/errorgen
|
||||||
|
|
||||||
|
const (
|
||||||
|
rttFailed = time.Duration(math.MaxInt64 - iota)
|
||||||
|
rttUntested
|
||||||
|
rttUnqualified
|
||||||
|
)
|
|
@ -0,0 +1,108 @@
|
||||||
|
package burst
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/core"
|
||||||
|
"github.com/xtls/xray-core/app/observatory"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/signal/done"
|
||||||
|
"github.com/xtls/xray-core/features/extension"
|
||||||
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Observer struct {
|
||||||
|
config *Config
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
statusLock sync.Mutex
|
||||||
|
hp *HealthPing
|
||||||
|
|
||||||
|
finished *done.Instance
|
||||||
|
|
||||||
|
ohm outbound.Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) {
|
||||||
|
return &observatory.ObservationResult{Status: o.createResult()}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Observer) createResult() []*observatory.OutboundStatus {
|
||||||
|
var result []*observatory.OutboundStatus
|
||||||
|
o.hp.access.Lock()
|
||||||
|
defer o.hp.access.Unlock()
|
||||||
|
for name, value := range o.hp.Results {
|
||||||
|
status := observatory.OutboundStatus{
|
||||||
|
Alive: value.getStatistics().All != value.getStatistics().Fail,
|
||||||
|
Delay: value.getStatistics().Average.Milliseconds(),
|
||||||
|
LastErrorReason: "",
|
||||||
|
OutboundTag: name,
|
||||||
|
LastSeenTime: 0,
|
||||||
|
LastTryTime: 0,
|
||||||
|
HealthPing: &observatory.HealthPingMeasurementResult{
|
||||||
|
All: int64(value.getStatistics().All),
|
||||||
|
Fail: int64(value.getStatistics().Fail),
|
||||||
|
Deviation: int64(value.getStatistics().Deviation),
|
||||||
|
Average: int64(value.getStatistics().Average),
|
||||||
|
Max: int64(value.getStatistics().Max),
|
||||||
|
Min: int64(value.getStatistics().Min),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
result = append(result, &status)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Observer) Type() interface{} {
|
||||||
|
return extension.ObservatoryType()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Observer) Start() error {
|
||||||
|
if o.config != nil && len(o.config.SubjectSelector) != 0 {
|
||||||
|
o.finished = done.New()
|
||||||
|
o.hp.StartScheduler(func() ([]string, error) {
|
||||||
|
hs, ok := o.ohm.(outbound.HandlerSelector)
|
||||||
|
if !ok {
|
||||||
|
|
||||||
|
return nil, newError("outbound.Manager is not a HandlerSelector")
|
||||||
|
}
|
||||||
|
|
||||||
|
outbounds := hs.Select(o.config.SubjectSelector)
|
||||||
|
return outbounds, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Observer) Close() error {
|
||||||
|
if o.finished != nil {
|
||||||
|
o.hp.StopScheduler()
|
||||||
|
return o.finished.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, config *Config) (*Observer, error) {
|
||||||
|
var outboundManager outbound.Manager
|
||||||
|
err := core.RequireFeatures(ctx, func(om outbound.Manager) {
|
||||||
|
outboundManager = om
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Cannot get depended features").Base(err)
|
||||||
|
}
|
||||||
|
hp := NewHealthPing(ctx, config.PingConfig)
|
||||||
|
return &Observer{
|
||||||
|
config: config,
|
||||||
|
ctx: ctx,
|
||||||
|
ohm: outboundManager,
|
||||||
|
hp: hp,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
|
return New(ctx, config.(*Config))
|
||||||
|
}))
|
||||||
|
}
|
|
@ -0,0 +1,276 @@
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.34.1
|
||||||
|
// protoc v5.27.0
|
||||||
|
// source: app/observatory/burst/config.proto
|
||||||
|
|
||||||
|
package burst
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// @Document The selectors for outbound under observation
|
||||||
|
SubjectSelector []string `protobuf:"bytes,2,rep,name=subject_selector,json=subjectSelector,proto3" json:"subject_selector,omitempty"`
|
||||||
|
PingConfig *HealthPingConfig `protobuf:"bytes,3,opt,name=ping_config,json=pingConfig,proto3" json:"ping_config,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_app_observatory_burst_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_app_observatory_burst_config_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
|
return file_app_observatory_burst_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetSubjectSelector() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.SubjectSelector
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetPingConfig() *HealthPingConfig {
|
||||||
|
if x != nil {
|
||||||
|
return x.PingConfig
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type HealthPingConfig struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// destination url, need 204 for success return
|
||||||
|
// default https://connectivitycheck.gstatic.com/generate_204
|
||||||
|
Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"`
|
||||||
|
// connectivity check url
|
||||||
|
Connectivity string `protobuf:"bytes,2,opt,name=connectivity,proto3" json:"connectivity,omitempty"`
|
||||||
|
// health check interval, int64 values of time.Duration
|
||||||
|
Interval int64 `protobuf:"varint,3,opt,name=interval,proto3" json:"interval,omitempty"`
|
||||||
|
// sampling count is the amount of recent ping results which are kept for calculation
|
||||||
|
SamplingCount int32 `protobuf:"varint,4,opt,name=samplingCount,proto3" json:"samplingCount,omitempty"`
|
||||||
|
// ping timeout, int64 values of time.Duration
|
||||||
|
Timeout int64 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingConfig) Reset() {
|
||||||
|
*x = HealthPingConfig{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_app_observatory_burst_config_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingConfig) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HealthPingConfig) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HealthPingConfig) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_app_observatory_burst_config_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HealthPingConfig.ProtoReflect.Descriptor instead.
|
||||||
|
func (*HealthPingConfig) Descriptor() ([]byte, []int) {
|
||||||
|
return file_app_observatory_burst_config_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingConfig) GetDestination() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Destination
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingConfig) GetConnectivity() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Connectivity
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingConfig) GetInterval() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Interval
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingConfig) GetSamplingCount() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.SamplingCount
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingConfig) GetTimeout() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Timeout
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_app_observatory_burst_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_app_observatory_burst_config_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x22, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
|
||||||
|
0x79, 0x2f, 0x62, 0x75, 0x72, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
|
||||||
|
0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e,
|
||||||
|
0x62, 0x75, 0x72, 0x73, 0x74, 0x22, 0x87, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
|
0x12, 0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65,
|
||||||
|
0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a,
|
||||||
|
0x65, 0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x52, 0x0a, 0x0b, 0x70,
|
||||||
|
0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
|
||||||
|
0x32, 0x31, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70,
|
||||||
|
0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x62, 0x75, 0x72,
|
||||||
|
0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e,
|
||||||
|
0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22,
|
||||||
|
0xb4, 0x01, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f,
|
||||||
|
0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
|
||||||
|
0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69,
|
||||||
|
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
|
||||||
|
0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f,
|
||||||
|
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e,
|
||||||
|
0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e,
|
||||||
|
0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x69,
|
||||||
|
0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x73,
|
||||||
|
0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07,
|
||||||
|
0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74,
|
||||||
|
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x42, 0x70, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
|
||||||
|
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
|
||||||
|
0x72, 0x79, 0x2e, 0x62, 0x75, 0x72, 0x73, 0x74, 0x50, 0x01, 0x5a, 0x2f, 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, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76,
|
||||||
|
0x61, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x62, 0x75, 0x72, 0x73, 0x74, 0xaa, 0x02, 0x1a, 0x58, 0x72,
|
||||||
|
0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
|
||||||
|
0x72, 0x79, 0x2e, 0x42, 0x75, 0x72, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_app_observatory_burst_config_proto_rawDescOnce sync.Once
|
||||||
|
file_app_observatory_burst_config_proto_rawDescData = file_app_observatory_burst_config_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_app_observatory_burst_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_app_observatory_burst_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_app_observatory_burst_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_observatory_burst_config_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_app_observatory_burst_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_app_observatory_burst_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
|
var file_app_observatory_burst_config_proto_goTypes = []interface{}{
|
||||||
|
(*Config)(nil), // 0: xray.core.app.observatory.burst.Config
|
||||||
|
(*HealthPingConfig)(nil), // 1: xray.core.app.observatory.burst.HealthPingConfig
|
||||||
|
}
|
||||||
|
var file_app_observatory_burst_config_proto_depIdxs = []int32{
|
||||||
|
1, // 0: xray.core.app.observatory.burst.Config.ping_config:type_name -> xray.core.app.observatory.burst.HealthPingConfig
|
||||||
|
1, // [1:1] is the sub-list for method output_type
|
||||||
|
1, // [1:1] is the sub-list for method input_type
|
||||||
|
1, // [1:1] is the sub-list for extension type_name
|
||||||
|
1, // [1:1] is the sub-list for extension extendee
|
||||||
|
0, // [0:1] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_app_observatory_burst_config_proto_init() }
|
||||||
|
func file_app_observatory_burst_config_proto_init() {
|
||||||
|
if File_app_observatory_burst_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_app_observatory_burst_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Config); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_app_observatory_burst_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*HealthPingConfig); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_app_observatory_burst_config_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 2,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_app_observatory_burst_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_app_observatory_burst_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_app_observatory_burst_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_app_observatory_burst_config_proto = out.File
|
||||||
|
file_app_observatory_burst_config_proto_rawDesc = nil
|
||||||
|
file_app_observatory_burst_config_proto_goTypes = nil
|
||||||
|
file_app_observatory_burst_config_proto_depIdxs = nil
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.core.app.observatory.burst;
|
||||||
|
option csharp_namespace = "Xray.App.Observatory.Burst";
|
||||||
|
option go_package = "github.com/xtls/xray-core/app/observatory/burst";
|
||||||
|
option java_package = "com.xray.app.observatory.burst";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {
|
||||||
|
/* @Document The selectors for outbound under observation
|
||||||
|
*/
|
||||||
|
repeated string subject_selector = 2;
|
||||||
|
|
||||||
|
HealthPingConfig ping_config = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HealthPingConfig {
|
||||||
|
// destination url, need 204 for success return
|
||||||
|
// default https://connectivitycheck.gstatic.com/generate_204
|
||||||
|
string destination = 1;
|
||||||
|
// connectivity check url
|
||||||
|
string connectivity = 2;
|
||||||
|
// health check interval, int64 values of time.Duration
|
||||||
|
int64 interval = 3;
|
||||||
|
// sampling count is the amount of recent ping results which are kept for calculation
|
||||||
|
int32 samplingCount = 4;
|
||||||
|
// ping timeout, int64 values of time.Duration
|
||||||
|
int64 timeout = 5;
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package mtproto
|
package burst
|
||||||
|
|
||||||
import "github.com/xtls/xray-core/common/errors"
|
import "github.com/xtls/xray-core/common/errors"
|
||||||
|
|
|
@ -0,0 +1,253 @@
|
||||||
|
package burst
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/dice"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HealthPingSettings holds settings for health Checker
|
||||||
|
type HealthPingSettings struct {
|
||||||
|
Destination string `json:"destination"`
|
||||||
|
Connectivity string `json:"connectivity"`
|
||||||
|
Interval time.Duration `json:"interval"`
|
||||||
|
SamplingCount int `json:"sampling"`
|
||||||
|
Timeout time.Duration `json:"timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthPing is the health checker for balancers
|
||||||
|
type HealthPing struct {
|
||||||
|
ctx context.Context
|
||||||
|
access sync.Mutex
|
||||||
|
ticker *time.Ticker
|
||||||
|
tickerClose chan struct{}
|
||||||
|
|
||||||
|
Settings *HealthPingSettings
|
||||||
|
Results map[string]*HealthPingRTTS
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHealthPing creates a new HealthPing with settings
|
||||||
|
func NewHealthPing(ctx context.Context, config *HealthPingConfig) *HealthPing {
|
||||||
|
settings := &HealthPingSettings{}
|
||||||
|
if config != nil {
|
||||||
|
settings = &HealthPingSettings{
|
||||||
|
Connectivity: strings.TrimSpace(config.Connectivity),
|
||||||
|
Destination: strings.TrimSpace(config.Destination),
|
||||||
|
Interval: time.Duration(config.Interval),
|
||||||
|
SamplingCount: int(config.SamplingCount),
|
||||||
|
Timeout: time.Duration(config.Timeout),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if settings.Destination == "" {
|
||||||
|
// Destination URL, need 204 for success return default to chromium
|
||||||
|
// https://github.com/chromium/chromium/blob/main/components/safety_check/url_constants.cc#L10
|
||||||
|
// https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/safety_check/url_constants.cc#10
|
||||||
|
settings.Destination = "https://connectivitycheck.gstatic.com/generate_204"
|
||||||
|
}
|
||||||
|
if settings.Interval == 0 {
|
||||||
|
settings.Interval = time.Duration(1) * time.Minute
|
||||||
|
} else if settings.Interval < 10 {
|
||||||
|
newError("health check interval is too small, 10s is applied").AtWarning().WriteToLog()
|
||||||
|
settings.Interval = time.Duration(10) * time.Second
|
||||||
|
}
|
||||||
|
if settings.SamplingCount <= 0 {
|
||||||
|
settings.SamplingCount = 10
|
||||||
|
}
|
||||||
|
if settings.Timeout <= 0 {
|
||||||
|
// results are saved after all health pings finish,
|
||||||
|
// a larger timeout could possibly makes checks run longer
|
||||||
|
settings.Timeout = time.Duration(5) * time.Second
|
||||||
|
}
|
||||||
|
return &HealthPing{
|
||||||
|
ctx: ctx,
|
||||||
|
Settings: settings,
|
||||||
|
Results: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartScheduler implements the HealthChecker
|
||||||
|
func (h *HealthPing) StartScheduler(selector func() ([]string, error)) {
|
||||||
|
if h.ticker != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
interval := h.Settings.Interval * time.Duration(h.Settings.SamplingCount)
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
|
tickerClose := make(chan struct{})
|
||||||
|
h.ticker = ticker
|
||||||
|
h.tickerClose = tickerClose
|
||||||
|
go func() {
|
||||||
|
tags, err := selector()
|
||||||
|
if err != nil {
|
||||||
|
newError("error select outbounds for initial health check: ", err).AtWarning().WriteToLog()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.Check(tags)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
go func() {
|
||||||
|
tags, err := selector()
|
||||||
|
if err != nil {
|
||||||
|
newError("error select outbounds for scheduled health check: ", err).AtWarning().WriteToLog()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.doCheck(tags, interval, h.Settings.SamplingCount)
|
||||||
|
h.Cleanup(tags)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
continue
|
||||||
|
case <-tickerClose:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopScheduler implements the HealthChecker
|
||||||
|
func (h *HealthPing) StopScheduler() {
|
||||||
|
if h.ticker == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.ticker.Stop()
|
||||||
|
h.ticker = nil
|
||||||
|
close(h.tickerClose)
|
||||||
|
h.tickerClose = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check implements the HealthChecker
|
||||||
|
func (h *HealthPing) Check(tags []string) error {
|
||||||
|
if len(tags) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
newError("perform one-time health check for tags ", tags).AtInfo().WriteToLog()
|
||||||
|
h.doCheck(tags, 0, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type rtt struct {
|
||||||
|
handler string
|
||||||
|
value time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// doCheck performs the 'rounds' amount checks in given 'duration'. You should make
|
||||||
|
// sure all tags are valid for current balancer
|
||||||
|
func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int) {
|
||||||
|
count := len(tags) * rounds
|
||||||
|
if count == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ch := make(chan *rtt, count)
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
handler := tag
|
||||||
|
client := newPingClient(
|
||||||
|
h.ctx,
|
||||||
|
h.Settings.Destination,
|
||||||
|
h.Settings.Timeout,
|
||||||
|
handler,
|
||||||
|
)
|
||||||
|
for i := 0; i < rounds; i++ {
|
||||||
|
delay := time.Duration(0)
|
||||||
|
if duration > 0 {
|
||||||
|
delay = time.Duration(dice.Roll(int(duration)))
|
||||||
|
}
|
||||||
|
time.AfterFunc(delay, func() {
|
||||||
|
newError("checking ", handler).AtDebug().WriteToLog()
|
||||||
|
delay, err := client.MeasureDelay()
|
||||||
|
if err == nil {
|
||||||
|
ch <- &rtt{
|
||||||
|
handler: handler,
|
||||||
|
value: delay,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !h.checkConnectivity() {
|
||||||
|
newError("network is down").AtWarning().WriteToLog()
|
||||||
|
ch <- &rtt{
|
||||||
|
handler: handler,
|
||||||
|
value: 0,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newError(fmt.Sprintf(
|
||||||
|
"error ping %s with %s: %s",
|
||||||
|
h.Settings.Destination,
|
||||||
|
handler,
|
||||||
|
err,
|
||||||
|
)).AtWarning().WriteToLog()
|
||||||
|
ch <- &rtt{
|
||||||
|
handler: handler,
|
||||||
|
value: rttFailed,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
rtt := <-ch
|
||||||
|
if rtt.value > 0 {
|
||||||
|
// should not put results when network is down
|
||||||
|
h.PutResult(rtt.handler, rtt.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutResult put a ping rtt to results
|
||||||
|
func (h *HealthPing) PutResult(tag string, rtt time.Duration) {
|
||||||
|
h.access.Lock()
|
||||||
|
defer h.access.Unlock()
|
||||||
|
if h.Results == nil {
|
||||||
|
h.Results = make(map[string]*HealthPingRTTS)
|
||||||
|
}
|
||||||
|
r, ok := h.Results[tag]
|
||||||
|
if !ok {
|
||||||
|
// validity is 2 times to sampling period, since the check are
|
||||||
|
// distributed in the time line randomly, in extreme cases,
|
||||||
|
// previous checks are distributed on the left, and latters
|
||||||
|
// on the right
|
||||||
|
validity := h.Settings.Interval * time.Duration(h.Settings.SamplingCount) * 2
|
||||||
|
r = NewHealthPingResult(h.Settings.SamplingCount, validity)
|
||||||
|
h.Results[tag] = r
|
||||||
|
}
|
||||||
|
r.Put(rtt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup removes results of removed handlers,
|
||||||
|
// tags should be all valid tags of the Balancer now
|
||||||
|
func (h *HealthPing) Cleanup(tags []string) {
|
||||||
|
h.access.Lock()
|
||||||
|
defer h.access.Unlock()
|
||||||
|
for tag := range h.Results {
|
||||||
|
found := false
|
||||||
|
for _, v := range tags {
|
||||||
|
if tag == v {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
delete(h.Results, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkConnectivity checks the network connectivity, it returns
|
||||||
|
// true if network is good or "connectivity check url" not set
|
||||||
|
func (h *HealthPing) checkConnectivity() bool {
|
||||||
|
if h.Settings.Connectivity == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
tester := newDirectPingClient(
|
||||||
|
h.Settings.Connectivity,
|
||||||
|
h.Settings.Timeout,
|
||||||
|
)
|
||||||
|
if _, err := tester.MeasureDelay(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
package burst
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HealthPingStats is the statistics of HealthPingRTTS
|
||||||
|
type HealthPingStats struct {
|
||||||
|
All int
|
||||||
|
Fail int
|
||||||
|
Deviation time.Duration
|
||||||
|
Average time.Duration
|
||||||
|
Max time.Duration
|
||||||
|
Min time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthPingRTTS holds ping rtts for health Checker
|
||||||
|
type HealthPingRTTS struct {
|
||||||
|
idx int
|
||||||
|
cap int
|
||||||
|
validity time.Duration
|
||||||
|
rtts []*pingRTT
|
||||||
|
|
||||||
|
lastUpdateAt time.Time
|
||||||
|
stats *HealthPingStats
|
||||||
|
}
|
||||||
|
|
||||||
|
type pingRTT struct {
|
||||||
|
time time.Time
|
||||||
|
value time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHealthPingResult returns a *HealthPingResult with specified capacity
|
||||||
|
func NewHealthPingResult(cap int, validity time.Duration) *HealthPingRTTS {
|
||||||
|
return &HealthPingRTTS{cap: cap, validity: validity}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets statistics of the HealthPingRTTS
|
||||||
|
func (h *HealthPingRTTS) Get() *HealthPingStats {
|
||||||
|
return h.getStatistics()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWithCache get statistics and write cache for next call
|
||||||
|
// Make sure use Mutex.Lock() before calling it, RWMutex.RLock()
|
||||||
|
// is not an option since it writes cache
|
||||||
|
func (h *HealthPingRTTS) GetWithCache() *HealthPingStats {
|
||||||
|
lastPutAt := h.rtts[h.idx].time
|
||||||
|
now := time.Now()
|
||||||
|
if h.stats == nil || h.lastUpdateAt.Before(lastPutAt) || h.findOutdated(now) >= 0 {
|
||||||
|
h.stats = h.getStatistics()
|
||||||
|
h.lastUpdateAt = now
|
||||||
|
}
|
||||||
|
return h.stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put puts a new rtt to the HealthPingResult
|
||||||
|
func (h *HealthPingRTTS) Put(d time.Duration) {
|
||||||
|
if h.rtts == nil {
|
||||||
|
h.rtts = make([]*pingRTT, h.cap)
|
||||||
|
for i := 0; i < h.cap; i++ {
|
||||||
|
h.rtts[i] = &pingRTT{}
|
||||||
|
}
|
||||||
|
h.idx = -1
|
||||||
|
}
|
||||||
|
h.idx = h.calcIndex(1)
|
||||||
|
now := time.Now()
|
||||||
|
h.rtts[h.idx].time = now
|
||||||
|
h.rtts[h.idx].value = d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HealthPingRTTS) calcIndex(step int) int {
|
||||||
|
idx := h.idx
|
||||||
|
idx += step
|
||||||
|
if idx >= h.cap {
|
||||||
|
idx %= h.cap
|
||||||
|
}
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HealthPingRTTS) getStatistics() *HealthPingStats {
|
||||||
|
stats := &HealthPingStats{}
|
||||||
|
stats.Fail = 0
|
||||||
|
stats.Max = 0
|
||||||
|
stats.Min = rttFailed
|
||||||
|
sum := time.Duration(0)
|
||||||
|
cnt := 0
|
||||||
|
validRTTs := make([]time.Duration, 0)
|
||||||
|
for _, rtt := range h.rtts {
|
||||||
|
switch {
|
||||||
|
case rtt.value == 0 || time.Since(rtt.time) > h.validity:
|
||||||
|
continue
|
||||||
|
case rtt.value == rttFailed:
|
||||||
|
stats.Fail++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cnt++
|
||||||
|
sum += rtt.value
|
||||||
|
validRTTs = append(validRTTs, rtt.value)
|
||||||
|
if stats.Max < rtt.value {
|
||||||
|
stats.Max = rtt.value
|
||||||
|
}
|
||||||
|
if stats.Min > rtt.value {
|
||||||
|
stats.Min = rtt.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stats.All = cnt + stats.Fail
|
||||||
|
if cnt == 0 {
|
||||||
|
stats.Min = 0
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
stats.Average = time.Duration(int(sum) / cnt)
|
||||||
|
var std float64
|
||||||
|
if cnt < 2 {
|
||||||
|
// no enough data for standard deviation, we assume it's half of the average rtt
|
||||||
|
// if we don't do this, standard deviation of 1 round tested nodes is 0, will always
|
||||||
|
// selected before 2 or more rounds tested nodes
|
||||||
|
std = float64(stats.Average / 2)
|
||||||
|
} else {
|
||||||
|
variance := float64(0)
|
||||||
|
for _, rtt := range validRTTs {
|
||||||
|
variance += math.Pow(float64(rtt-stats.Average), 2)
|
||||||
|
}
|
||||||
|
std = math.Sqrt(variance / float64(cnt))
|
||||||
|
}
|
||||||
|
stats.Deviation = time.Duration(std)
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HealthPingRTTS) findOutdated(now time.Time) int {
|
||||||
|
for i := h.cap - 1; i < 2*h.cap; i++ {
|
||||||
|
// from oldest to latest
|
||||||
|
idx := h.calcIndex(i)
|
||||||
|
validity := h.rtts[idx].time.Add(h.validity)
|
||||||
|
if h.lastUpdateAt.After(validity) {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
if validity.Before(now) {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
package burst_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
reflect "reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/app/observatory/burst"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHealthPingResults(t *testing.T) {
|
||||||
|
rtts := []int64{60, 140, 60, 140, 60, 60, 140, 60, 140}
|
||||||
|
hr := burst.NewHealthPingResult(4, time.Hour)
|
||||||
|
for _, rtt := range rtts {
|
||||||
|
hr.Put(time.Duration(rtt))
|
||||||
|
}
|
||||||
|
rttFailed := time.Duration(math.MaxInt64)
|
||||||
|
expected := &burst.HealthPingStats{
|
||||||
|
All: 4,
|
||||||
|
Fail: 0,
|
||||||
|
Deviation: 40,
|
||||||
|
Average: 100,
|
||||||
|
Max: 140,
|
||||||
|
Min: 60,
|
||||||
|
}
|
||||||
|
actual := hr.Get()
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", expected, actual)
|
||||||
|
}
|
||||||
|
hr.Put(rttFailed)
|
||||||
|
hr.Put(rttFailed)
|
||||||
|
expected.Fail = 2
|
||||||
|
actual = hr.Get()
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("failed half-failures test, expected: %v, actual: %v", expected, actual)
|
||||||
|
}
|
||||||
|
hr.Put(rttFailed)
|
||||||
|
hr.Put(rttFailed)
|
||||||
|
expected = &burst.HealthPingStats{
|
||||||
|
All: 4,
|
||||||
|
Fail: 4,
|
||||||
|
Deviation: 0,
|
||||||
|
Average: 0,
|
||||||
|
Max: 0,
|
||||||
|
Min: 0,
|
||||||
|
}
|
||||||
|
actual = hr.Get()
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("failed all-failures test, expected: %v, actual: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHealthPingResultsIgnoreOutdated(t *testing.T) {
|
||||||
|
rtts := []int64{60, 140, 60, 140}
|
||||||
|
hr := burst.NewHealthPingResult(4, time.Duration(10)*time.Millisecond)
|
||||||
|
for i, rtt := range rtts {
|
||||||
|
if i == 2 {
|
||||||
|
// wait for previous 2 outdated
|
||||||
|
time.Sleep(time.Duration(10) * time.Millisecond)
|
||||||
|
}
|
||||||
|
hr.Put(time.Duration(rtt))
|
||||||
|
}
|
||||||
|
hr.Get()
|
||||||
|
expected := &burst.HealthPingStats{
|
||||||
|
All: 2,
|
||||||
|
Fail: 0,
|
||||||
|
Deviation: 40,
|
||||||
|
Average: 100,
|
||||||
|
Max: 140,
|
||||||
|
Min: 60,
|
||||||
|
}
|
||||||
|
actual := hr.Get()
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("failed 'half-outdated' test, expected: %v, actual: %v", expected, actual)
|
||||||
|
}
|
||||||
|
// wait for all outdated
|
||||||
|
time.Sleep(time.Duration(10) * time.Millisecond)
|
||||||
|
expected = &burst.HealthPingStats{
|
||||||
|
All: 0,
|
||||||
|
Fail: 0,
|
||||||
|
Deviation: 0,
|
||||||
|
Average: 0,
|
||||||
|
Max: 0,
|
||||||
|
Min: 0,
|
||||||
|
}
|
||||||
|
actual = hr.Get()
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("failed 'outdated / not-tested' test, expected: %v, actual: %v", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
hr.Put(time.Duration(60))
|
||||||
|
expected = &burst.HealthPingStats{
|
||||||
|
All: 1,
|
||||||
|
Fail: 0,
|
||||||
|
// 1 sample, std=0.5rtt
|
||||||
|
Deviation: 30,
|
||||||
|
Average: 60,
|
||||||
|
Max: 60,
|
||||||
|
Min: 60,
|
||||||
|
}
|
||||||
|
actual = hr.Get()
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package burst
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/tagged"
|
||||||
|
)
|
||||||
|
|
||||||
|
type pingClient struct {
|
||||||
|
destination string
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPingClient(ctx context.Context, destination string, timeout time.Duration, handler string) *pingClient {
|
||||||
|
return &pingClient{
|
||||||
|
destination: destination,
|
||||||
|
httpClient: newHTTPClient(ctx, handler, timeout),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDirectPingClient(destination string, timeout time.Duration) *pingClient {
|
||||||
|
return &pingClient{
|
||||||
|
destination: destination,
|
||||||
|
httpClient: &http.Client{Timeout: timeout},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHTTPClient(ctxv context.Context, handler string, timeout time.Duration) *http.Client {
|
||||||
|
tr := &http.Transport{
|
||||||
|
DisableKeepAlives: true,
|
||||||
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
dest, err := net.ParseDestination(network + ":" + addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tagged.Dialer(ctxv, dest, handler)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return &http.Client{
|
||||||
|
Transport: tr,
|
||||||
|
Timeout: timeout,
|
||||||
|
// don't follow redirect
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeasureDelay returns the delay time of the request to dest
|
||||||
|
func (s *pingClient) MeasureDelay() (time.Duration, error) {
|
||||||
|
if s.httpClient == nil {
|
||||||
|
panic("pingClient no initialized")
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(http.MethodHead, s.destination, nil)
|
||||||
|
if err != nil {
|
||||||
|
return rttFailed, err
|
||||||
|
}
|
||||||
|
start := time.Now()
|
||||||
|
resp, err := s.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return rttFailed, err
|
||||||
|
}
|
||||||
|
// don't wait for body
|
||||||
|
resp.Body.Close()
|
||||||
|
return time.Since(start), nil
|
||||||
|
}
|
|
@ -1,6 +1,3 @@
|
||||||
//go:build !confonly
|
|
||||||
// +build !confonly
|
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/observatory/command/command.proto
|
// source: app/observatory/command/command.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// - protoc-gen-go-grpc v1.2.0
|
// - protoc-gen-go-grpc v1.3.0
|
||||||
// - protoc v3.21.12
|
// - protoc v5.27.0
|
||||||
// source: app/observatory/command/command.proto
|
// source: app/observatory/command/command.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
@ -18,6 +18,10 @@ import (
|
||||||
// Requires gRPC-Go v1.32.0 or later.
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
const _ = grpc.SupportPackageIsVersion7
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
const (
|
||||||
|
ObservatoryService_GetOutboundStatus_FullMethodName = "/xray.core.app.observatory.command.ObservatoryService/GetOutboundStatus"
|
||||||
|
)
|
||||||
|
|
||||||
// ObservatoryServiceClient is the client API for ObservatoryService service.
|
// ObservatoryServiceClient is the client API for ObservatoryService service.
|
||||||
//
|
//
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
@ -35,7 +39,7 @@ func NewObservatoryServiceClient(cc grpc.ClientConnInterface) ObservatoryService
|
||||||
|
|
||||||
func (c *observatoryServiceClient) GetOutboundStatus(ctx context.Context, in *GetOutboundStatusRequest, opts ...grpc.CallOption) (*GetOutboundStatusResponse, error) {
|
func (c *observatoryServiceClient) GetOutboundStatus(ctx context.Context, in *GetOutboundStatusRequest, opts ...grpc.CallOption) (*GetOutboundStatusResponse, error) {
|
||||||
out := new(GetOutboundStatusResponse)
|
out := new(GetOutboundStatusResponse)
|
||||||
err := c.cc.Invoke(ctx, "/xray.core.app.observatory.command.ObservatoryService/GetOutboundStatus", in, out, opts...)
|
err := c.cc.Invoke(ctx, ObservatoryService_GetOutboundStatus_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -80,7 +84,7 @@ func _ObservatoryService_GetOutboundStatus_Handler(srv interface{}, ctx context.
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/xray.core.app.observatory.command.ObservatoryService/GetOutboundStatus",
|
FullMethod: ObservatoryService_GetOutboundStatus_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(ObservatoryServiceServer).GetOutboundStatus(ctx, req.(*GetOutboundStatusRequest))
|
return srv.(ObservatoryServiceServer).GetOutboundStatus(ctx, req.(*GetOutboundStatusRequest))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/observatory/config.proto
|
// source: app/observatory/config.proto
|
||||||
|
|
||||||
package observatory
|
package observatory
|
||||||
|
@ -67,6 +67,93 @@ func (x *ObservationResult) GetStatus() []*OutboundStatus {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HealthPingMeasurementResult struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
All int64 `protobuf:"varint,1,opt,name=all,proto3" json:"all,omitempty"`
|
||||||
|
Fail int64 `protobuf:"varint,2,opt,name=fail,proto3" json:"fail,omitempty"`
|
||||||
|
Deviation int64 `protobuf:"varint,3,opt,name=deviation,proto3" json:"deviation,omitempty"`
|
||||||
|
Average int64 `protobuf:"varint,4,opt,name=average,proto3" json:"average,omitempty"`
|
||||||
|
Max int64 `protobuf:"varint,5,opt,name=max,proto3" json:"max,omitempty"`
|
||||||
|
Min int64 `protobuf:"varint,6,opt,name=min,proto3" json:"min,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingMeasurementResult) Reset() {
|
||||||
|
*x = HealthPingMeasurementResult{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_app_observatory_config_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingMeasurementResult) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HealthPingMeasurementResult) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HealthPingMeasurementResult) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_app_observatory_config_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HealthPingMeasurementResult.ProtoReflect.Descriptor instead.
|
||||||
|
func (*HealthPingMeasurementResult) Descriptor() ([]byte, []int) {
|
||||||
|
return file_app_observatory_config_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingMeasurementResult) GetAll() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.All
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingMeasurementResult) GetFail() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Fail
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingMeasurementResult) GetDeviation() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Deviation
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingMeasurementResult) GetAverage() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Average
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingMeasurementResult) GetMax() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Max
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingMeasurementResult) GetMin() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Min
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type OutboundStatus struct {
|
type OutboundStatus struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@ -90,13 +177,14 @@ type OutboundStatus struct {
|
||||||
LastSeenTime int64 `protobuf:"varint,5,opt,name=last_seen_time,json=lastSeenTime,proto3" json:"last_seen_time,omitempty"`
|
LastSeenTime int64 `protobuf:"varint,5,opt,name=last_seen_time,json=lastSeenTime,proto3" json:"last_seen_time,omitempty"`
|
||||||
// @Document The time this outbound is tried
|
// @Document The time this outbound is tried
|
||||||
// @Type id.outboundTag
|
// @Type id.outboundTag
|
||||||
LastTryTime int64 `protobuf:"varint,6,opt,name=last_try_time,json=lastTryTime,proto3" json:"last_try_time,omitempty"`
|
LastTryTime int64 `protobuf:"varint,6,opt,name=last_try_time,json=lastTryTime,proto3" json:"last_try_time,omitempty"`
|
||||||
|
HealthPing *HealthPingMeasurementResult `protobuf:"bytes,7,opt,name=health_ping,json=healthPing,proto3" json:"health_ping,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *OutboundStatus) Reset() {
|
func (x *OutboundStatus) Reset() {
|
||||||
*x = OutboundStatus{}
|
*x = OutboundStatus{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[1]
|
mi := &file_app_observatory_config_proto_msgTypes[2]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -109,7 +197,7 @@ func (x *OutboundStatus) String() string {
|
||||||
func (*OutboundStatus) ProtoMessage() {}
|
func (*OutboundStatus) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *OutboundStatus) ProtoReflect() protoreflect.Message {
|
func (x *OutboundStatus) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[1]
|
mi := &file_app_observatory_config_proto_msgTypes[2]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -122,7 +210,7 @@ func (x *OutboundStatus) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use OutboundStatus.ProtoReflect.Descriptor instead.
|
// Deprecated: Use OutboundStatus.ProtoReflect.Descriptor instead.
|
||||||
func (*OutboundStatus) Descriptor() ([]byte, []int) {
|
func (*OutboundStatus) Descriptor() ([]byte, []int) {
|
||||||
return file_app_observatory_config_proto_rawDescGZIP(), []int{1}
|
return file_app_observatory_config_proto_rawDescGZIP(), []int{2}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *OutboundStatus) GetAlive() bool {
|
func (x *OutboundStatus) GetAlive() bool {
|
||||||
|
@ -167,6 +255,13 @@ func (x *OutboundStatus) GetLastTryTime() int64 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *OutboundStatus) GetHealthPing() *HealthPingMeasurementResult {
|
||||||
|
if x != nil {
|
||||||
|
return x.HealthPing
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type ProbeResult struct {
|
type ProbeResult struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@ -187,7 +282,7 @@ type ProbeResult struct {
|
||||||
func (x *ProbeResult) Reset() {
|
func (x *ProbeResult) Reset() {
|
||||||
*x = ProbeResult{}
|
*x = ProbeResult{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[2]
|
mi := &file_app_observatory_config_proto_msgTypes[3]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -200,7 +295,7 @@ func (x *ProbeResult) String() string {
|
||||||
func (*ProbeResult) ProtoMessage() {}
|
func (*ProbeResult) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *ProbeResult) ProtoReflect() protoreflect.Message {
|
func (x *ProbeResult) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[2]
|
mi := &file_app_observatory_config_proto_msgTypes[3]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -213,7 +308,7 @@ func (x *ProbeResult) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use ProbeResult.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ProbeResult.ProtoReflect.Descriptor instead.
|
||||||
func (*ProbeResult) Descriptor() ([]byte, []int) {
|
func (*ProbeResult) Descriptor() ([]byte, []int) {
|
||||||
return file_app_observatory_config_proto_rawDescGZIP(), []int{2}
|
return file_app_observatory_config_proto_rawDescGZIP(), []int{3}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ProbeResult) GetAlive() bool {
|
func (x *ProbeResult) GetAlive() bool {
|
||||||
|
@ -250,7 +345,7 @@ type Intensity struct {
|
||||||
func (x *Intensity) Reset() {
|
func (x *Intensity) Reset() {
|
||||||
*x = Intensity{}
|
*x = Intensity{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[3]
|
mi := &file_app_observatory_config_proto_msgTypes[4]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -263,7 +358,7 @@ func (x *Intensity) String() string {
|
||||||
func (*Intensity) ProtoMessage() {}
|
func (*Intensity) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Intensity) ProtoReflect() protoreflect.Message {
|
func (x *Intensity) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[3]
|
mi := &file_app_observatory_config_proto_msgTypes[4]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -276,7 +371,7 @@ func (x *Intensity) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use Intensity.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Intensity.ProtoReflect.Descriptor instead.
|
||||||
func (*Intensity) Descriptor() ([]byte, []int) {
|
func (*Intensity) Descriptor() ([]byte, []int) {
|
||||||
return file_app_observatory_config_proto_rawDescGZIP(), []int{3}
|
return file_app_observatory_config_proto_rawDescGZIP(), []int{4}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Intensity) GetProbeInterval() uint32 {
|
func (x *Intensity) GetProbeInterval() uint32 {
|
||||||
|
@ -301,7 +396,7 @@ type Config struct {
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
*x = Config{}
|
*x = Config{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[4]
|
mi := &file_app_observatory_config_proto_msgTypes[5]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -314,7 +409,7 @@ func (x *Config) String() string {
|
||||||
func (*Config) ProtoMessage() {}
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[4]
|
mi := &file_app_observatory_config_proto_msgTypes[5]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -327,7 +422,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
func (*Config) Descriptor() ([]byte, []int) {
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
return file_app_observatory_config_proto_rawDescGZIP(), []int{4}
|
return file_app_observatory_config_proto_rawDescGZIP(), []int{5}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetSubjectSelector() []string {
|
func (x *Config) GetSubjectSelector() []string {
|
||||||
|
@ -370,47 +465,62 @@ var file_app_observatory_config_proto_rawDesc = []byte{
|
||||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f,
|
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f,
|
||||||
0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f,
|
0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f,
|
||||||
0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
|
0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
|
||||||
0x73, 0x22, 0xd5, 0x01, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74,
|
0x73, 0x22, 0x9f, 0x01, 0x0a, 0x1b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67,
|
||||||
0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20,
|
0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c,
|
||||||
0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65,
|
0x74, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03,
|
||||||
0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79,
|
0x61, 0x6c, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x72,
|
0x03, 0x52, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x61,
|
||||||
0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x61, 0x73,
|
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x64, 0x65, 0x76, 0x69,
|
||||||
0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c,
|
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65,
|
||||||
0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x04, 0x20, 0x01,
|
0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x12,
|
||||||
0x28, 0x09, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12,
|
0x10, 0x0a, 0x03, 0x6d, 0x61, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6d, 0x61,
|
||||||
0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74, 0x69, 0x6d,
|
0x78, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03,
|
||||||
0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65,
|
0x6d, 0x69, 0x6e, 0x22, 0xae, 0x02, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64,
|
||||||
0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x72,
|
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18,
|
||||||
0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6c, 0x61,
|
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05,
|
||||||
0x73, 0x74, 0x54, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x0b, 0x50, 0x72, 0x6f,
|
0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x64, 0x65, 0x6c,
|
||||||
0x62, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x76,
|
0x61, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
||||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x14,
|
0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c,
|
||||||
0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x64,
|
0x61, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21,
|
||||||
0x65, 0x6c, 0x61, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72,
|
0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x04,
|
||||||
0x6f, 0x72, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61,
|
||||||
0x0f, 0x6c, 0x61, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e,
|
0x67, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74,
|
||||||
0x22, 0x32, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x12, 0x25, 0x0a,
|
0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x53,
|
||||||
0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18,
|
0x65, 0x65, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65,
|
0x74, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b,
|
||||||
0x72, 0x76, 0x61, 0x6c, 0x22, 0xa6, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
|
0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x57, 0x0a, 0x0b, 0x68,
|
||||||
0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63,
|
0x65, 0x61, 0x6c, 0x74, 0x68, 0x5f, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,
|
||||||
0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65,
|
0x32, 0x36, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70,
|
||||||
0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72,
|
0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x48, 0x65, 0x61,
|
||||||
0x6f, 0x62, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
|
0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x6d, 0x65,
|
||||||
0x72, 0x6f, 0x62, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65,
|
0x6e, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0a, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68,
|
||||||
0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
|
0x50, 0x69, 0x6e, 0x67, 0x22, 0x65, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73,
|
||||||
0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2d,
|
0x75, 0x6c, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x0a, 0x12, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72,
|
0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c,
|
||||||
0x65, 0x6e, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x65, 0x6e, 0x61, 0x62,
|
0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12,
|
||||||
0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x5e, 0x0a,
|
0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x72, 0x65,
|
||||||
0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62,
|
0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74,
|
||||||
0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74,
|
0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x32, 0x0a, 0x09, 0x49,
|
||||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
|
0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x62,
|
||||||
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72,
|
0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
|
||||||
0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70,
|
0x52, 0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22,
|
||||||
0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x62, 0x06, 0x70,
|
0xa6, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x75,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02,
|
||||||
|
0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x6c,
|
||||||
|
0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x75,
|
||||||
|
0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x55,
|
||||||
|
0x72, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65,
|
||||||
|
0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x62,
|
||||||
|
0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x6e, 0x61,
|
||||||
|
0x62, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18,
|
||||||
|
0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6e,
|
||||||
|
0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e,
|
||||||
|
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61,
|
||||||
|
0x74, 0x6f, 0x72, 0x79, 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, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
|
||||||
|
0x79, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x62, 0x73,
|
||||||
|
0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -425,21 +535,23 @@ func file_app_observatory_config_proto_rawDescGZIP() []byte {
|
||||||
return file_app_observatory_config_proto_rawDescData
|
return file_app_observatory_config_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_app_observatory_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
var file_app_observatory_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||||
var file_app_observatory_config_proto_goTypes = []interface{}{
|
var file_app_observatory_config_proto_goTypes = []interface{}{
|
||||||
(*ObservationResult)(nil), // 0: xray.core.app.observatory.ObservationResult
|
(*ObservationResult)(nil), // 0: xray.core.app.observatory.ObservationResult
|
||||||
(*OutboundStatus)(nil), // 1: xray.core.app.observatory.OutboundStatus
|
(*HealthPingMeasurementResult)(nil), // 1: xray.core.app.observatory.HealthPingMeasurementResult
|
||||||
(*ProbeResult)(nil), // 2: xray.core.app.observatory.ProbeResult
|
(*OutboundStatus)(nil), // 2: xray.core.app.observatory.OutboundStatus
|
||||||
(*Intensity)(nil), // 3: xray.core.app.observatory.Intensity
|
(*ProbeResult)(nil), // 3: xray.core.app.observatory.ProbeResult
|
||||||
(*Config)(nil), // 4: xray.core.app.observatory.Config
|
(*Intensity)(nil), // 4: xray.core.app.observatory.Intensity
|
||||||
|
(*Config)(nil), // 5: xray.core.app.observatory.Config
|
||||||
}
|
}
|
||||||
var file_app_observatory_config_proto_depIdxs = []int32{
|
var file_app_observatory_config_proto_depIdxs = []int32{
|
||||||
1, // 0: xray.core.app.observatory.ObservationResult.status:type_name -> xray.core.app.observatory.OutboundStatus
|
2, // 0: xray.core.app.observatory.ObservationResult.status:type_name -> xray.core.app.observatory.OutboundStatus
|
||||||
1, // [1:1] is the sub-list for method output_type
|
1, // 1: xray.core.app.observatory.OutboundStatus.health_ping:type_name -> xray.core.app.observatory.HealthPingMeasurementResult
|
||||||
1, // [1:1] is the sub-list for method input_type
|
2, // [2:2] is the sub-list for method output_type
|
||||||
1, // [1:1] is the sub-list for extension type_name
|
2, // [2:2] is the sub-list for method input_type
|
||||||
1, // [1:1] is the sub-list for extension extendee
|
2, // [2:2] is the sub-list for extension type_name
|
||||||
0, // [0:1] is the sub-list for field type_name
|
2, // [2:2] is the sub-list for extension extendee
|
||||||
|
0, // [0:2] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_app_observatory_config_proto_init() }
|
func init() { file_app_observatory_config_proto_init() }
|
||||||
|
@ -461,7 +573,7 @@ func file_app_observatory_config_proto_init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_app_observatory_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
file_app_observatory_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*OutboundStatus); i {
|
switch v := v.(*HealthPingMeasurementResult); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -473,7 +585,7 @@ func file_app_observatory_config_proto_init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_app_observatory_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
file_app_observatory_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*ProbeResult); i {
|
switch v := v.(*OutboundStatus); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -485,7 +597,7 @@ func file_app_observatory_config_proto_init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_app_observatory_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
file_app_observatory_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Intensity); i {
|
switch v := v.(*ProbeResult); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -497,6 +609,18 @@ func file_app_observatory_config_proto_init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_app_observatory_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
file_app_observatory_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Intensity); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_app_observatory_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Config); i {
|
switch v := v.(*Config); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -515,7 +639,7 @@ func file_app_observatory_config_proto_init() {
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_app_observatory_config_proto_rawDesc,
|
RawDescriptor: file_app_observatory_config_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 5,
|
NumMessages: 6,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,6 +10,15 @@ message ObservationResult {
|
||||||
repeated OutboundStatus status = 1;
|
repeated OutboundStatus status = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message HealthPingMeasurementResult {
|
||||||
|
int64 all = 1;
|
||||||
|
int64 fail = 2;
|
||||||
|
int64 deviation = 3;
|
||||||
|
int64 average = 4;
|
||||||
|
int64 max = 5;
|
||||||
|
int64 min = 6;
|
||||||
|
}
|
||||||
|
|
||||||
message OutboundStatus{
|
message OutboundStatus{
|
||||||
/* @Document Whether this outbound is usable
|
/* @Document Whether this outbound is usable
|
||||||
@Restriction ReadOnlyForUser
|
@Restriction ReadOnlyForUser
|
||||||
|
@ -36,6 +45,8 @@ message OutboundStatus{
|
||||||
@Type id.outboundTag
|
@Type id.outboundTag
|
||||||
*/
|
*/
|
||||||
int64 last_try_time = 6;
|
int64 last_try_time = 6;
|
||||||
|
|
||||||
|
HealthPingMeasurementResult health_ping = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ProbeResult{
|
message ProbeResult{
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
v2net "github.com/xtls/xray-core/common/net"
|
v2net "github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
@ -19,6 +18,7 @@ import (
|
||||||
"github.com/xtls/xray-core/features/extension"
|
"github.com/xtls/xray-core/features/extension"
|
||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
"github.com/xtls/xray-core/transport/internet/tagged"
|
"github.com/xtls/xray-core/transport/internet/tagged"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Observer struct {
|
type Observer struct {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/policy/config.proto
|
// source: app/policy/config.proto
|
||||||
|
|
||||||
package policy
|
package policy
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/proxyman/command/command.proto
|
// source: app/proxyman/command/command.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// - protoc-gen-go-grpc v1.2.0
|
// - protoc-gen-go-grpc v1.3.0
|
||||||
// - protoc v3.21.12
|
// - protoc v5.27.0
|
||||||
// source: app/proxyman/command/command.proto
|
// source: app/proxyman/command/command.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
@ -18,6 +18,15 @@ import (
|
||||||
// Requires gRPC-Go v1.32.0 or later.
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
const _ = grpc.SupportPackageIsVersion7
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
const (
|
||||||
|
HandlerService_AddInbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/AddInbound"
|
||||||
|
HandlerService_RemoveInbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/RemoveInbound"
|
||||||
|
HandlerService_AlterInbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/AlterInbound"
|
||||||
|
HandlerService_AddOutbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/AddOutbound"
|
||||||
|
HandlerService_RemoveOutbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/RemoveOutbound"
|
||||||
|
HandlerService_AlterOutbound_FullMethodName = "/xray.app.proxyman.command.HandlerService/AlterOutbound"
|
||||||
|
)
|
||||||
|
|
||||||
// HandlerServiceClient is the client API for HandlerService service.
|
// HandlerServiceClient is the client API for HandlerService service.
|
||||||
//
|
//
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
@ -40,7 +49,7 @@ func NewHandlerServiceClient(cc grpc.ClientConnInterface) HandlerServiceClient {
|
||||||
|
|
||||||
func (c *handlerServiceClient) AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error) {
|
func (c *handlerServiceClient) AddInbound(ctx context.Context, in *AddInboundRequest, opts ...grpc.CallOption) (*AddInboundResponse, error) {
|
||||||
out := new(AddInboundResponse)
|
out := new(AddInboundResponse)
|
||||||
err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/AddInbound", in, out, opts...)
|
err := c.cc.Invoke(ctx, HandlerService_AddInbound_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -49,7 +58,7 @@ func (c *handlerServiceClient) AddInbound(ctx context.Context, in *AddInboundReq
|
||||||
|
|
||||||
func (c *handlerServiceClient) RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error) {
|
func (c *handlerServiceClient) RemoveInbound(ctx context.Context, in *RemoveInboundRequest, opts ...grpc.CallOption) (*RemoveInboundResponse, error) {
|
||||||
out := new(RemoveInboundResponse)
|
out := new(RemoveInboundResponse)
|
||||||
err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/RemoveInbound", in, out, opts...)
|
err := c.cc.Invoke(ctx, HandlerService_RemoveInbound_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -58,7 +67,7 @@ func (c *handlerServiceClient) RemoveInbound(ctx context.Context, in *RemoveInbo
|
||||||
|
|
||||||
func (c *handlerServiceClient) AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error) {
|
func (c *handlerServiceClient) AlterInbound(ctx context.Context, in *AlterInboundRequest, opts ...grpc.CallOption) (*AlterInboundResponse, error) {
|
||||||
out := new(AlterInboundResponse)
|
out := new(AlterInboundResponse)
|
||||||
err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/AlterInbound", in, out, opts...)
|
err := c.cc.Invoke(ctx, HandlerService_AlterInbound_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -67,7 +76,7 @@ func (c *handlerServiceClient) AlterInbound(ctx context.Context, in *AlterInboun
|
||||||
|
|
||||||
func (c *handlerServiceClient) AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error) {
|
func (c *handlerServiceClient) AddOutbound(ctx context.Context, in *AddOutboundRequest, opts ...grpc.CallOption) (*AddOutboundResponse, error) {
|
||||||
out := new(AddOutboundResponse)
|
out := new(AddOutboundResponse)
|
||||||
err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/AddOutbound", in, out, opts...)
|
err := c.cc.Invoke(ctx, HandlerService_AddOutbound_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -76,7 +85,7 @@ func (c *handlerServiceClient) AddOutbound(ctx context.Context, in *AddOutboundR
|
||||||
|
|
||||||
func (c *handlerServiceClient) RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error) {
|
func (c *handlerServiceClient) RemoveOutbound(ctx context.Context, in *RemoveOutboundRequest, opts ...grpc.CallOption) (*RemoveOutboundResponse, error) {
|
||||||
out := new(RemoveOutboundResponse)
|
out := new(RemoveOutboundResponse)
|
||||||
err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/RemoveOutbound", in, out, opts...)
|
err := c.cc.Invoke(ctx, HandlerService_RemoveOutbound_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -85,7 +94,7 @@ func (c *handlerServiceClient) RemoveOutbound(ctx context.Context, in *RemoveOut
|
||||||
|
|
||||||
func (c *handlerServiceClient) AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error) {
|
func (c *handlerServiceClient) AlterOutbound(ctx context.Context, in *AlterOutboundRequest, opts ...grpc.CallOption) (*AlterOutboundResponse, error) {
|
||||||
out := new(AlterOutboundResponse)
|
out := new(AlterOutboundResponse)
|
||||||
err := c.cc.Invoke(ctx, "/xray.app.proxyman.command.HandlerService/AlterOutbound", in, out, opts...)
|
err := c.cc.Invoke(ctx, HandlerService_AlterOutbound_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -150,7 +159,7 @@ func _HandlerService_AddInbound_Handler(srv interface{}, ctx context.Context, de
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/xray.app.proxyman.command.HandlerService/AddInbound",
|
FullMethod: HandlerService_AddInbound_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(HandlerServiceServer).AddInbound(ctx, req.(*AddInboundRequest))
|
return srv.(HandlerServiceServer).AddInbound(ctx, req.(*AddInboundRequest))
|
||||||
|
@ -168,7 +177,7 @@ func _HandlerService_RemoveInbound_Handler(srv interface{}, ctx context.Context,
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/xray.app.proxyman.command.HandlerService/RemoveInbound",
|
FullMethod: HandlerService_RemoveInbound_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(HandlerServiceServer).RemoveInbound(ctx, req.(*RemoveInboundRequest))
|
return srv.(HandlerServiceServer).RemoveInbound(ctx, req.(*RemoveInboundRequest))
|
||||||
|
@ -186,7 +195,7 @@ func _HandlerService_AlterInbound_Handler(srv interface{}, ctx context.Context,
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/xray.app.proxyman.command.HandlerService/AlterInbound",
|
FullMethod: HandlerService_AlterInbound_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(HandlerServiceServer).AlterInbound(ctx, req.(*AlterInboundRequest))
|
return srv.(HandlerServiceServer).AlterInbound(ctx, req.(*AlterInboundRequest))
|
||||||
|
@ -204,7 +213,7 @@ func _HandlerService_AddOutbound_Handler(srv interface{}, ctx context.Context, d
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/xray.app.proxyman.command.HandlerService/AddOutbound",
|
FullMethod: HandlerService_AddOutbound_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(HandlerServiceServer).AddOutbound(ctx, req.(*AddOutboundRequest))
|
return srv.(HandlerServiceServer).AddOutbound(ctx, req.(*AddOutboundRequest))
|
||||||
|
@ -222,7 +231,7 @@ func _HandlerService_RemoveOutbound_Handler(srv interface{}, ctx context.Context
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/xray.app.proxyman.command.HandlerService/RemoveOutbound",
|
FullMethod: HandlerService_RemoveOutbound_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(HandlerServiceServer).RemoveOutbound(ctx, req.(*RemoveOutboundRequest))
|
return srv.(HandlerServiceServer).RemoveOutbound(ctx, req.(*RemoveOutboundRequest))
|
||||||
|
@ -240,7 +249,7 @@ func _HandlerService_AlterOutbound_Handler(srv interface{}, ctx context.Context,
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/xray.app.proxyman.command.HandlerService/AlterOutbound",
|
FullMethod: HandlerService_AlterOutbound_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(HandlerServiceServer).AlterOutbound(ctx, req.(*AlterOutboundRequest))
|
return srv.(HandlerServiceServer).AlterOutbound(ctx, req.(*AlterOutboundRequest))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/proxyman/config.proto
|
// source: app/proxyman/config.proto
|
||||||
|
|
||||||
package proxyman
|
package proxyman
|
||||||
|
@ -326,7 +326,7 @@ type ReceiverConfig struct {
|
||||||
// Override domains for the given protocol.
|
// Override domains for the given protocol.
|
||||||
// Deprecated. Use sniffing_settings.
|
// Deprecated. Use sniffing_settings.
|
||||||
//
|
//
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/proxyman/config.proto.
|
||||||
DomainOverride []KnownProtocols `protobuf:"varint,7,rep,packed,name=domain_override,json=domainOverride,proto3,enum=xray.app.proxyman.KnownProtocols" json:"domain_override,omitempty"`
|
DomainOverride []KnownProtocols `protobuf:"varint,7,rep,packed,name=domain_override,json=domainOverride,proto3,enum=xray.app.proxyman.KnownProtocols" json:"domain_override,omitempty"`
|
||||||
SniffingSettings *SniffingConfig `protobuf:"bytes,8,opt,name=sniffing_settings,json=sniffingSettings,proto3" json:"sniffing_settings,omitempty"`
|
SniffingSettings *SniffingConfig `protobuf:"bytes,8,opt,name=sniffing_settings,json=sniffingSettings,proto3" json:"sniffing_settings,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -398,7 +398,7 @@ func (x *ReceiverConfig) GetReceiveOriginalDestination() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/proxyman/config.proto.
|
||||||
func (x *ReceiverConfig) GetDomainOverride() []KnownProtocols {
|
func (x *ReceiverConfig) GetDomainOverride() []KnownProtocols {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.DomainOverride
|
return x.DomainOverride
|
||||||
|
@ -524,6 +524,7 @@ type SenderConfig struct {
|
||||||
StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
|
StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
|
||||||
ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
|
ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
|
||||||
MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"`
|
MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"`
|
||||||
|
ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SenderConfig) Reset() {
|
func (x *SenderConfig) Reset() {
|
||||||
|
@ -586,6 +587,13 @@ func (x *SenderConfig) GetMultiplexSettings() *MultiplexingConfig {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *SenderConfig) GetViaCidr() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ViaCidr
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type MultiplexingConfig struct {
|
type MultiplexingConfig struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@ -594,7 +602,11 @@ type MultiplexingConfig struct {
|
||||||
// Whether or not Mux is enabled.
|
// Whether or not Mux is enabled.
|
||||||
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||||
// Max number of concurrent connections that one Mux connection can handle.
|
// Max number of concurrent connections that one Mux connection can handle.
|
||||||
Concurrency uint32 `protobuf:"varint,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"`
|
Concurrency int32 `protobuf:"varint,2,opt,name=concurrency,proto3" json:"concurrency,omitempty"`
|
||||||
|
// Transport XUDP in another Mux.
|
||||||
|
XudpConcurrency int32 `protobuf:"varint,3,opt,name=xudpConcurrency,proto3" json:"xudpConcurrency,omitempty"`
|
||||||
|
// "reject" (default), "allow" or "skip".
|
||||||
|
XudpProxyUDP443 string `protobuf:"bytes,4,opt,name=xudpProxyUDP443,proto3" json:"xudpProxyUDP443,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *MultiplexingConfig) Reset() {
|
func (x *MultiplexingConfig) Reset() {
|
||||||
|
@ -636,13 +648,27 @@ func (x *MultiplexingConfig) GetEnabled() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *MultiplexingConfig) GetConcurrency() uint32 {
|
func (x *MultiplexingConfig) GetConcurrency() int32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Concurrency
|
return x.Concurrency
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *MultiplexingConfig) GetXudpConcurrency() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.XudpConcurrency
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *MultiplexingConfig) GetXudpProxyUDP443() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.XudpProxyUDP443
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type AllocationStrategy_AllocationStrategyConcurrency struct {
|
type AllocationStrategy_AllocationStrategyConcurrency struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@ -837,7 +863,7 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
|
||||||
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65,
|
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,
|
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,
|
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,
|
0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xcb, 0x02, 0x0a, 0x0c, 0x53, 0x65,
|
||||||
0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x03, 0x76, 0x69,
|
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,
|
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,
|
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f,
|
||||||
|
@ -856,12 +882,19 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
|
||||||
0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72,
|
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,
|
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,
|
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,
|
0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x08,
|
||||||
0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66,
|
0x76, 0x69, 0x61, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
||||||
0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20,
|
0x76, 0x69, 0x61, 0x43, 0x69, 0x64, 0x72, 0x22, 0xa4, 0x01, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74,
|
||||||
0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b,
|
0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18,
|
||||||
0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
0x0d, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x2a, 0x23,
|
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, 0x05, 0x52, 0x0b, 0x63,
|
||||||
|
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75,
|
||||||
|
0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20,
|
||||||
|
0x01, 0x28, 0x05, 0x52, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72,
|
||||||
|
0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78,
|
||||||
|
0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x78,
|
||||||
|
0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x2a, 0x23,
|
||||||
0x0a, 0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73,
|
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,
|
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,
|
0x53, 0x10, 0x01, 0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
|
|
|
@ -91,11 +91,16 @@ message SenderConfig {
|
||||||
xray.transport.internet.StreamConfig stream_settings = 2;
|
xray.transport.internet.StreamConfig stream_settings = 2;
|
||||||
xray.transport.internet.ProxyConfig proxy_settings = 3;
|
xray.transport.internet.ProxyConfig proxy_settings = 3;
|
||||||
MultiplexingConfig multiplex_settings = 4;
|
MultiplexingConfig multiplex_settings = 4;
|
||||||
|
string via_cidr = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MultiplexingConfig {
|
message MultiplexingConfig {
|
||||||
// Whether or not Mux is enabled.
|
// Whether or not Mux is enabled.
|
||||||
bool enabled = 1;
|
bool enabled = 1;
|
||||||
// Max number of concurrent connections that one Mux connection can handle.
|
// Max number of concurrent connections that one Mux connection can handle.
|
||||||
uint32 concurrency = 2;
|
int32 concurrency = 2;
|
||||||
|
// Transport XUDP in another Mux.
|
||||||
|
int32 xudpConcurrency = 3;
|
||||||
|
// "reject" (default), "allow" or "skip".
|
||||||
|
string xudpProxyUDP443 = 4;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ func (w *tcpWorker) callback(conn stat.Connection) {
|
||||||
sid := session.NewID()
|
sid := session.NewID()
|
||||||
ctx = session.ContextWithID(ctx, sid)
|
ctx = session.ContextWithID(ctx, sid)
|
||||||
|
|
||||||
|
outbounds := []*session.Outbound{{}}
|
||||||
if w.recvOrigDest {
|
if w.recvOrigDest {
|
||||||
var dest net.Destination
|
var dest net.Destination
|
||||||
switch getTProxyType(w.stream) {
|
switch getTProxyType(w.stream) {
|
||||||
|
@ -74,11 +75,10 @@ func (w *tcpWorker) callback(conn stat.Connection) {
|
||||||
dest = net.DestinationFromAddr(conn.LocalAddr())
|
dest = net.DestinationFromAddr(conn.LocalAddr())
|
||||||
}
|
}
|
||||||
if dest.IsValid() {
|
if dest.IsValid() {
|
||||||
ctx = session.ContextWithOutbound(ctx, &session.Outbound{
|
outbounds[0].Target = dest
|
||||||
Target: dest,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
|
|
||||||
if w.uplinkCounter != nil || w.downlinkCounter != nil {
|
if w.uplinkCounter != nil || w.downlinkCounter != nil {
|
||||||
conn = &stat.CounterConnection{
|
conn = &stat.CounterConnection{
|
||||||
|
@ -308,11 +308,11 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
|
||||||
sid := session.NewID()
|
sid := session.NewID()
|
||||||
ctx = session.ContextWithID(ctx, sid)
|
ctx = session.ContextWithID(ctx, sid)
|
||||||
|
|
||||||
|
outbounds := []*session.Outbound{{}}
|
||||||
if originalDest.IsValid() {
|
if originalDest.IsValid() {
|
||||||
ctx = session.ContextWithOutbound(ctx, &session.Outbound{
|
outbounds[0].Target = originalDest
|
||||||
Target: originalDest,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||||
Source: source,
|
Source: source,
|
||||||
Gateway: net.UDPDestination(w.address, w.port),
|
Gateway: net.UDPDestination(w.address, w.port),
|
||||||
|
@ -362,7 +362,7 @@ func (w *udpWorker) clean() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for addr, conn := range w.activeConn {
|
for addr, conn := range w.activeConn {
|
||||||
if nowSec-atomic.LoadInt64(&conn.lastActivityTime) > 5*60 { // TODO Timeout too small
|
if nowSec-atomic.LoadInt64(&conn.lastActivityTime) > 2*60 {
|
||||||
if !conn.inactive {
|
if !conn.inactive {
|
||||||
conn.setInactive()
|
conn.setInactive()
|
||||||
delete(w.activeConn, addr)
|
delete(w.activeConn, addr)
|
||||||
|
|
|
@ -2,12 +2,11 @@ package outbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/proxyman"
|
"github.com/xtls/xray-core/app/proxyman"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/buf"
|
||||||
"github.com/xtls/xray-core/common/mux"
|
"github.com/xtls/xray-core/common/mux"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/net/cnc"
|
"github.com/xtls/xray-core/common/net/cnc"
|
||||||
|
@ -22,6 +21,10 @@ import (
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
"github.com/xtls/xray-core/transport/internet/tls"
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
"github.com/xtls/xray-core/transport/pipe"
|
"github.com/xtls/xray-core/transport/pipe"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
gonet "net"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) {
|
func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) {
|
||||||
|
@ -57,6 +60,8 @@ type Handler struct {
|
||||||
proxy proxy.Outbound
|
proxy proxy.Outbound
|
||||||
outboundManager outbound.Manager
|
outboundManager outbound.Manager
|
||||||
mux *mux.ClientManager
|
mux *mux.ClientManager
|
||||||
|
xudp *mux.ClientManager
|
||||||
|
udp443 string
|
||||||
uplinkCounter stats.Counter
|
uplinkCounter stats.Counter
|
||||||
downlinkCounter stats.Counter
|
downlinkCounter stats.Counter
|
||||||
}
|
}
|
||||||
|
@ -106,22 +111,50 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.senderSettings != nil && h.senderSettings.MultiplexSettings != nil {
|
if h.senderSettings != nil && h.senderSettings.MultiplexSettings != nil {
|
||||||
config := h.senderSettings.MultiplexSettings
|
if config := h.senderSettings.MultiplexSettings; config.Enabled {
|
||||||
if config.Concurrency < 1 || config.Concurrency > 1024 {
|
if config.Concurrency < 0 {
|
||||||
return nil, newError("invalid mux concurrency: ", config.Concurrency).AtWarning()
|
h.mux = &mux.ClientManager{Enabled: false}
|
||||||
}
|
}
|
||||||
h.mux = &mux.ClientManager{
|
if config.Concurrency == 0 {
|
||||||
Enabled: h.senderSettings.MultiplexSettings.Enabled,
|
config.Concurrency = 8 // same as before
|
||||||
Picker: &mux.IncrementalWorkerPicker{
|
}
|
||||||
Factory: &mux.DialingWorkerFactory{
|
if config.Concurrency > 0 {
|
||||||
Proxy: proxyHandler,
|
h.mux = &mux.ClientManager{
|
||||||
Dialer: h,
|
Enabled: true,
|
||||||
Strategy: mux.ClientStrategy{
|
Picker: &mux.IncrementalWorkerPicker{
|
||||||
MaxConcurrency: config.Concurrency,
|
Factory: &mux.DialingWorkerFactory{
|
||||||
MaxConnection: 128,
|
Proxy: proxyHandler,
|
||||||
|
Dialer: h,
|
||||||
|
Strategy: mux.ClientStrategy{
|
||||||
|
MaxConcurrency: uint32(config.Concurrency),
|
||||||
|
MaxConnection: 128,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
|
if config.XudpConcurrency < 0 {
|
||||||
|
h.xudp = &mux.ClientManager{Enabled: false}
|
||||||
|
}
|
||||||
|
if config.XudpConcurrency == 0 {
|
||||||
|
h.xudp = nil // same as before
|
||||||
|
}
|
||||||
|
if config.XudpConcurrency > 0 {
|
||||||
|
h.xudp = &mux.ClientManager{
|
||||||
|
Enabled: true,
|
||||||
|
Picker: &mux.IncrementalWorkerPicker{
|
||||||
|
Factory: &mux.DialingWorkerFactory{
|
||||||
|
Proxy: proxyHandler,
|
||||||
|
Dialer: h,
|
||||||
|
Strategy: mux.ClientStrategy{
|
||||||
|
MaxConcurrency: uint32(config.XudpConcurrency),
|
||||||
|
MaxConnection: 128,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.udp443 = config.XudpProxyUDP443
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,31 +169,59 @@ func (h *Handler) Tag() string {
|
||||||
|
|
||||||
// Dispatch implements proxy.Outbound.Dispatch.
|
// Dispatch implements proxy.Outbound.Dispatch.
|
||||||
func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
|
func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
|
||||||
if h.mux != nil && (h.mux.Enabled || session.MuxPreferedFromContext(ctx)) {
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if err := h.mux.Dispatch(ctx, link); err != nil {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
err := newError("failed to process mux outbound traffic").Base(err)
|
if ob.Target.Network == net.Network_UDP && ob.OriginalTarget.Address != nil && ob.OriginalTarget.Address != ob.Target.Address {
|
||||||
session.SubmitOutboundErrorToOriginator(ctx, err)
|
link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
|
||||||
err.WriteToLog(session.ExportIDToError(ctx))
|
link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
|
||||||
common.Interrupt(link.Writer)
|
}
|
||||||
}
|
if h.mux != nil {
|
||||||
} else {
|
test := func(err error) {
|
||||||
err := h.proxy.Process(ctx, link, h)
|
if err != nil {
|
||||||
if err != nil {
|
err := newError("failed to process mux outbound traffic").Base(err)
|
||||||
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, context.Canceled) {
|
session.SubmitOutboundErrorToOriginator(ctx, err)
|
||||||
err = nil
|
err.WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
common.Interrupt(link.Writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if ob.Target.Network == net.Network_UDP && ob.Target.Port == 443 {
|
||||||
// Ensure outbound ray is properly closed.
|
switch h.udp443 {
|
||||||
err := newError("failed to process outbound traffic").Base(err)
|
case "reject":
|
||||||
session.SubmitOutboundErrorToOriginator(ctx, err)
|
test(newError("XUDP rejected UDP/443 traffic").AtInfo())
|
||||||
err.WriteToLog(session.ExportIDToError(ctx))
|
return
|
||||||
common.Interrupt(link.Writer)
|
case "skip":
|
||||||
} else {
|
goto out
|
||||||
common.Must(common.Close(link.Writer))
|
}
|
||||||
|
}
|
||||||
|
if h.xudp != nil && ob.Target.Network == net.Network_UDP {
|
||||||
|
if !h.xudp.Enabled {
|
||||||
|
goto out
|
||||||
|
}
|
||||||
|
test(h.xudp.Dispatch(ctx, link))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if h.mux.Enabled {
|
||||||
|
test(h.mux.Dispatch(ctx, link))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
common.Interrupt(link.Reader)
|
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
|
err := h.proxy.Process(ctx, link, h)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, context.Canceled) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
// Ensure outbound ray is properly closed.
|
||||||
|
err := newError("failed to process outbound traffic").Base(err)
|
||||||
|
session.SubmitOutboundErrorToOriginator(ctx, err)
|
||||||
|
err.WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
common.Interrupt(link.Writer)
|
||||||
|
} else {
|
||||||
|
common.Close(link.Writer)
|
||||||
|
}
|
||||||
|
common.Interrupt(link.Reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address implements internet.Dialer.
|
// Address implements internet.Dialer.
|
||||||
|
@ -171,6 +232,10 @@ func (h *Handler) Address() net.Address {
|
||||||
return h.senderSettings.Via.AsAddress()
|
return h.senderSettings.Via.AsAddress()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) DestIpAddress() net.IP {
|
||||||
|
return internet.DestIpAddress()
|
||||||
|
}
|
||||||
|
|
||||||
// Dial implements internet.Dialer.
|
// Dial implements internet.Dialer.
|
||||||
func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connection, error) {
|
func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connection, error) {
|
||||||
if h.senderSettings != nil {
|
if h.senderSettings != nil {
|
||||||
|
@ -179,10 +244,11 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
||||||
handler := h.outboundManager.GetHandler(tag)
|
handler := h.outboundManager.GetHandler(tag)
|
||||||
if handler != nil {
|
if handler != nil {
|
||||||
newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
||||||
ctx = session.ContextWithOutbound(ctx, &session.Outbound{
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, append(outbounds, &session.Outbound{
|
||||||
Target: dest,
|
Target: dest,
|
||||||
})
|
Tag: tag,
|
||||||
|
})) // add another outbound in session ctx
|
||||||
opts := pipe.OptionsFromContext(ctx)
|
opts := pipe.OptionsFromContext(ctx)
|
||||||
uplinkReader, uplinkWriter := pipe.New(opts...)
|
uplinkReader, uplinkWriter := pipe.New(opts...)
|
||||||
downlinkReader, downlinkWriter := pipe.New(opts...)
|
downlinkReader, downlinkWriter := pipe.New(opts...)
|
||||||
|
@ -202,12 +268,13 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.senderSettings.Via != nil {
|
if h.senderSettings.Via != nil {
|
||||||
outbound := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outbound == nil {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
outbound = new(session.Outbound)
|
if h.senderSettings.ViaCidr == "" {
|
||||||
ctx = session.ContextWithOutbound(ctx, outbound)
|
ob.Gateway = h.senderSettings.Via.AsAddress()
|
||||||
|
} else { //Get a random address.
|
||||||
|
ob.Gateway = ParseRandomIPv6(h.senderSettings.Via.AsAddress(), h.senderSettings.ViaCidr)
|
||||||
}
|
}
|
||||||
outbound.Gateway = h.senderSettings.Via.AsAddress()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +283,11 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := internet.Dial(ctx, dest, h.streamSettings)
|
conn, err := internet.Dial(ctx, dest, h.streamSettings)
|
||||||
return h.getStatCouterConnection(conn), err
|
conn = h.getStatCouterConnection(conn)
|
||||||
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
ob.Conn = conn
|
||||||
|
return conn, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) getStatCouterConnection(conn stat.Connection) stat.Connection {
|
func (h *Handler) getStatCouterConnection(conn stat.Connection) stat.Connection {
|
||||||
|
@ -245,3 +316,22 @@ func (h *Handler) Close() error {
|
||||||
common.Close(h.mux)
|
common.Close(h.mux)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func ParseRandomIPv6(address net.Address, prefix string) net.Address {
|
||||||
|
_, network, _ := gonet.ParseCIDR(address.IP().String() + "/" + prefix)
|
||||||
|
|
||||||
|
maskSize, totalBits := network.Mask.Size()
|
||||||
|
subnetSize := big.NewInt(1).Lsh(big.NewInt(1), uint(totalBits-maskSize))
|
||||||
|
|
||||||
|
// random
|
||||||
|
randomBigInt, _ := rand.Int(rand.Reader, subnetSize)
|
||||||
|
|
||||||
|
startIPBigInt := big.NewInt(0).SetBytes(network.IP.To16())
|
||||||
|
randomIPBigInt := big.NewInt(0).Add(startIPBigInt, randomBigInt)
|
||||||
|
|
||||||
|
randomIPBytes := randomIPBigInt.Bytes()
|
||||||
|
randomIPBytes = append(make([]byte, 16-len(randomIPBytes)), randomIPBytes...)
|
||||||
|
|
||||||
|
return net.ParseAddress(gonet.IP(randomIPBytes).String())
|
||||||
|
}
|
||||||
|
|
|
@ -2,13 +2,19 @@ package outbound_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/policy"
|
"github.com/xtls/xray-core/app/policy"
|
||||||
|
"github.com/xtls/xray-core/app/proxyman"
|
||||||
. "github.com/xtls/xray-core/app/proxyman/outbound"
|
. "github.com/xtls/xray-core/app/proxyman/outbound"
|
||||||
"github.com/xtls/xray-core/app/stats"
|
"github.com/xtls/xray-core/app/stats"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
|
"github.com/xtls/xray-core/common/session"
|
||||||
core "github.com/xtls/xray-core/core"
|
core "github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
"github.com/xtls/xray-core/proxy/freedom"
|
"github.com/xtls/xray-core/proxy/freedom"
|
||||||
|
@ -39,6 +45,7 @@ func TestOutboundWithoutStatCounter(t *testing.T) {
|
||||||
v, _ := core.New(config)
|
v, _ := core.New(config)
|
||||||
v.AddFeature((outbound.Manager)(new(Manager)))
|
v.AddFeature((outbound.Manager)(new(Manager)))
|
||||||
ctx := context.WithValue(context.Background(), xrayKey, v)
|
ctx := context.WithValue(context.Background(), xrayKey, v)
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}})
|
||||||
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
||||||
Tag: "tag",
|
Tag: "tag",
|
||||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
|
@ -68,6 +75,7 @@ func TestOutboundWithStatCounter(t *testing.T) {
|
||||||
v, _ := core.New(config)
|
v, _ := core.New(config)
|
||||||
v.AddFeature((outbound.Manager)(new(Manager)))
|
v.AddFeature((outbound.Manager)(new(Manager)))
|
||||||
ctx := context.WithValue(context.Background(), xrayKey, v)
|
ctx := context.WithValue(context.Background(), xrayKey, v)
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, []*session.Outbound{{}})
|
||||||
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
h, _ := NewHandler(ctx, &core.OutboundHandlerConfig{
|
||||||
Tag: "tag",
|
Tag: "tag",
|
||||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
|
@ -78,3 +86,91 @@ func TestOutboundWithStatCounter(t *testing.T) {
|
||||||
t.Errorf("Expected conn to be CounterConnection")
|
t.Errorf("Expected conn to be CounterConnection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTagsCache(t *testing.T) {
|
||||||
|
|
||||||
|
test_duration := 10 * time.Second
|
||||||
|
threads_num := 50
|
||||||
|
delay := 10 * time.Millisecond
|
||||||
|
tags_prefix := "node"
|
||||||
|
|
||||||
|
tags := sync.Map{}
|
||||||
|
counter := atomic.Uint64{}
|
||||||
|
|
||||||
|
ohm, err := New(context.Background(), &proxyman.OutboundConfig{})
|
||||||
|
if err != nil {
|
||||||
|
t.Error("failed to create outbound handler manager")
|
||||||
|
}
|
||||||
|
config := &core.Config{
|
||||||
|
App: []*serial.TypedMessage{},
|
||||||
|
}
|
||||||
|
v, _ := core.New(config)
|
||||||
|
v.AddFeature(ohm)
|
||||||
|
ctx := context.WithValue(context.Background(), xrayKey, v)
|
||||||
|
|
||||||
|
stop_add_rm := false
|
||||||
|
wg_add_rm := sync.WaitGroup{}
|
||||||
|
addHandlers := func() {
|
||||||
|
defer wg_add_rm.Done()
|
||||||
|
for !stop_add_rm {
|
||||||
|
time.Sleep(delay)
|
||||||
|
idx := counter.Add(1)
|
||||||
|
tag := fmt.Sprintf("%s%d", tags_prefix, idx)
|
||||||
|
cfg := &core.OutboundHandlerConfig{
|
||||||
|
Tag: tag,
|
||||||
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
|
}
|
||||||
|
if h, err := NewHandler(ctx, cfg); err == nil {
|
||||||
|
if err := ohm.AddHandler(ctx, h); err == nil {
|
||||||
|
// t.Log("add handler:", tag)
|
||||||
|
tags.Store(tag, nil)
|
||||||
|
} else {
|
||||||
|
t.Error("failed to add handler:", tag)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Error("failed to create handler:", tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rmHandlers := func() {
|
||||||
|
defer wg_add_rm.Done()
|
||||||
|
for !stop_add_rm {
|
||||||
|
time.Sleep(delay)
|
||||||
|
tags.Range(func(key interface{}, value interface{}) bool {
|
||||||
|
if _, ok := tags.LoadAndDelete(key); ok {
|
||||||
|
// t.Log("remove handler:", key)
|
||||||
|
ohm.RemoveHandler(ctx, key.(string))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectors := []string{tags_prefix}
|
||||||
|
wg_get := sync.WaitGroup{}
|
||||||
|
stop_get := false
|
||||||
|
getTags := func() {
|
||||||
|
defer wg_get.Done()
|
||||||
|
for !stop_get {
|
||||||
|
time.Sleep(delay)
|
||||||
|
_ = ohm.Select(selectors)
|
||||||
|
// t.Logf("get tags: %v", tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < threads_num; i++ {
|
||||||
|
wg_add_rm.Add(2)
|
||||||
|
go rmHandlers()
|
||||||
|
go addHandlers()
|
||||||
|
wg_get.Add(1)
|
||||||
|
go getTags()
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(test_duration)
|
||||||
|
stop_add_rm = true
|
||||||
|
wg_add_rm.Wait()
|
||||||
|
stop_get = true
|
||||||
|
wg_get.Wait()
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ package outbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -21,12 +22,14 @@ type Manager struct {
|
||||||
taggedHandler map[string]outbound.Handler
|
taggedHandler map[string]outbound.Handler
|
||||||
untaggedHandlers []outbound.Handler
|
untaggedHandlers []outbound.Handler
|
||||||
running bool
|
running bool
|
||||||
|
tagsCache *sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Manager.
|
// New creates a new Manager.
|
||||||
func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) {
|
func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) {
|
||||||
m := &Manager{
|
m := &Manager{
|
||||||
taggedHandler: make(map[string]outbound.Handler),
|
taggedHandler: make(map[string]outbound.Handler),
|
||||||
|
tagsCache: &sync.Map{},
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
@ -103,6 +106,8 @@ func (m *Manager) AddHandler(ctx context.Context, handler outbound.Handler) erro
|
||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
defer m.access.Unlock()
|
defer m.access.Unlock()
|
||||||
|
|
||||||
|
m.tagsCache = &sync.Map{}
|
||||||
|
|
||||||
if m.defaultHandler == nil {
|
if m.defaultHandler == nil {
|
||||||
m.defaultHandler = handler
|
m.defaultHandler = handler
|
||||||
}
|
}
|
||||||
|
@ -132,6 +137,8 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
|
||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
defer m.access.Unlock()
|
defer m.access.Unlock()
|
||||||
|
|
||||||
|
m.tagsCache = &sync.Map{}
|
||||||
|
|
||||||
delete(m.taggedHandler, tag)
|
delete(m.taggedHandler, tag)
|
||||||
if m.defaultHandler != nil && m.defaultHandler.Tag() == tag {
|
if m.defaultHandler != nil && m.defaultHandler.Tag() == tag {
|
||||||
m.defaultHandler = nil
|
m.defaultHandler = nil
|
||||||
|
@ -142,24 +149,29 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
|
||||||
|
|
||||||
// Select implements outbound.HandlerSelector.
|
// Select implements outbound.HandlerSelector.
|
||||||
func (m *Manager) Select(selectors []string) []string {
|
func (m *Manager) Select(selectors []string) []string {
|
||||||
|
|
||||||
|
key := strings.Join(selectors, ",")
|
||||||
|
if cache, ok := m.tagsCache.Load(key); ok {
|
||||||
|
return cache.([]string)
|
||||||
|
}
|
||||||
|
|
||||||
m.access.RLock()
|
m.access.RLock()
|
||||||
defer m.access.RUnlock()
|
defer m.access.RUnlock()
|
||||||
|
|
||||||
tags := make([]string, 0, len(selectors))
|
tags := make([]string, 0, len(selectors))
|
||||||
|
|
||||||
for tag := range m.taggedHandler {
|
for tag := range m.taggedHandler {
|
||||||
match := false
|
|
||||||
for _, selector := range selectors {
|
for _, selector := range selectors {
|
||||||
if strings.HasPrefix(tag, selector) {
|
if strings.HasPrefix(tag, selector) {
|
||||||
match = true
|
tags = append(tags, tag)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if match {
|
|
||||||
tags = append(tags, tag)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort.Strings(tags)
|
||||||
|
m.tagsCache.Store(key, tags)
|
||||||
|
|
||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,24 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *Handler) getUoTConnection(ctx context.Context, dest net.Destination) (stat.Connection, error) {
|
func (h *Handler) getUoTConnection(ctx context.Context, dest net.Destination) (stat.Connection, error) {
|
||||||
if !dest.Address.Family().IsDomain() || dest.Address.Domain() != uot.UOTMagicAddress {
|
if dest.Address == nil {
|
||||||
|
return nil, newError("nil destination address")
|
||||||
|
}
|
||||||
|
if !dest.Address.Family().IsDomain() {
|
||||||
|
return nil, os.ErrInvalid
|
||||||
|
}
|
||||||
|
var uotVersion int
|
||||||
|
if dest.Address.Domain() == uot.MagicAddress {
|
||||||
|
uotVersion = uot.Version
|
||||||
|
} else if dest.Address.Domain() == uot.LegacyMagicAddress {
|
||||||
|
uotVersion = uot.LegacyVersion
|
||||||
|
} else {
|
||||||
return nil, os.ErrInvalid
|
return nil, os.ErrInvalid
|
||||||
}
|
}
|
||||||
packetConn, err := internet.ListenSystemPacket(ctx, &net.UDPAddr{IP: net.AnyIP.IP(), Port: 0}, h.streamSettings.SocketSettings)
|
packetConn, err := internet.ListenSystemPacket(ctx, &net.UDPAddr{IP: net.AnyIP.IP(), Port: 0}, h.streamSettings.SocketSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newError("unable to listen socket").Base(err)
|
return nil, newError("unable to listen socket").Base(err)
|
||||||
}
|
}
|
||||||
conn := uot.NewServerConn(packetConn)
|
conn := uot.NewServerConn(packetConn, uotVersion)
|
||||||
return h.getStatCouterConnection(conn), nil
|
return h.getStatCouterConnection(conn), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
"github.com/xtls/xray-core/common/mux"
|
"github.com/xtls/xray-core/common/mux"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
@ -12,6 +11,7 @@ import (
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
"github.com/xtls/xray-core/transport/pipe"
|
"github.com/xtls/xray-core/transport/pipe"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bridge is a component in reverse proxy, that relays connections from Portal to local address.
|
// Bridge is a component in reverse proxy, that relays connections from Portal to local address.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/reverse/config.proto
|
// source: app/reverse/config.proto
|
||||||
|
|
||||||
package reverse
|
package reverse
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
"github.com/xtls/xray-core/common/mux"
|
"github.com/xtls/xray-core/common/mux"
|
||||||
|
@ -15,6 +14,7 @@ import (
|
||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
"github.com/xtls/xray-core/transport/pipe"
|
"github.com/xtls/xray-core/transport/pipe"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Portal struct {
|
type Portal struct {
|
||||||
|
@ -62,12 +62,13 @@ func (p *Portal) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Portal) HandleConnection(ctx context.Context, link *transport.Link) error {
|
func (p *Portal) HandleConnection(ctx context.Context, link *transport.Link) error {
|
||||||
outboundMeta := session.OutboundFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if outboundMeta == nil {
|
ob := outbounds[len(outbounds) - 1]
|
||||||
|
if ob == nil {
|
||||||
return newError("outbound metadata not found").AtError()
|
return newError("outbound metadata not found").AtError()
|
||||||
}
|
}
|
||||||
|
|
||||||
if isDomain(outboundMeta.Target, p.domain) {
|
if isDomain(ob.Target, p.domain) {
|
||||||
muxClient, err := mux.NewClientWorker(*link, mux.ClientStrategy{})
|
muxClient, err := mux.NewClientWorker(*link, mux.ClientStrategy{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newError("failed to create mux client worker").Base(err).AtWarning()
|
return newError("failed to create mux client worker").Base(err).AtWarning()
|
||||||
|
@ -206,9 +207,10 @@ func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
|
||||||
downlinkReader, downlinkWriter := pipe.New(opt...)
|
downlinkReader, downlinkWriter := pipe.New(opt...)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = session.ContextWithOutbound(ctx, &session.Outbound{
|
outbounds := []*session.Outbound{{
|
||||||
Target: net.UDPDestination(net.DomainAddress(internalDomain), 0),
|
Target: net.UDPDestination(net.DomainAddress(internalDomain), 0),
|
||||||
})
|
}}
|
||||||
|
ctx = session.ContextWithOutbounds(ctx, outbounds)
|
||||||
f := client.Dispatch(ctx, &transport.Link{
|
f := client.Dispatch(ctx, &transport.Link{
|
||||||
Reader: uplinkReader,
|
Reader: uplinkReader,
|
||||||
Writer: downlinkWriter,
|
Writer: downlinkWriter,
|
||||||
|
|
|
@ -2,8 +2,11 @@ package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
sync "sync"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/dice"
|
"github.com/xtls/xray-core/app/observatory"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/features/extension"
|
"github.com/xtls/xray-core/features/extension"
|
||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
)
|
)
|
||||||
|
@ -12,34 +15,103 @@ type BalancingStrategy interface {
|
||||||
PickOutbound([]string) string
|
PickOutbound([]string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
type RandomStrategy struct{}
|
type BalancingPrincipleTarget interface {
|
||||||
|
GetPrincipleTarget([]string) []string
|
||||||
|
}
|
||||||
|
|
||||||
func (s *RandomStrategy) PickOutbound(tags []string) string {
|
type RoundRobinStrategy struct {
|
||||||
n := len(tags)
|
FallbackTag string
|
||||||
if n == 0 {
|
|
||||||
panic("0 tags")
|
ctx context.Context
|
||||||
|
observatory extension.Observatory
|
||||||
|
mu sync.Mutex
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RoundRobinStrategy) InjectContext(ctx context.Context) {
|
||||||
|
s.ctx = ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RoundRobinStrategy) GetPrincipleTarget(strings []string) []string {
|
||||||
|
return strings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RoundRobinStrategy) PickOutbound(tags []string) string {
|
||||||
|
if len(s.FallbackTag) > 0 && s.observatory == nil {
|
||||||
|
common.Must(core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error {
|
||||||
|
s.observatory = observatory
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
if s.observatory != nil {
|
||||||
|
observeReport, err := s.observatory.GetObservation(s.ctx)
|
||||||
|
if err == nil {
|
||||||
|
aliveTags := make([]string, 0)
|
||||||
|
if result, ok := observeReport.(*observatory.ObservationResult); ok {
|
||||||
|
status := result.Status
|
||||||
|
statusMap := make(map[string]*observatory.OutboundStatus)
|
||||||
|
for _, outboundStatus := range status {
|
||||||
|
statusMap[outboundStatus.OutboundTag] = outboundStatus
|
||||||
|
}
|
||||||
|
for _, candidate := range tags {
|
||||||
|
if outboundStatus, found := statusMap[candidate]; found {
|
||||||
|
if outboundStatus.Alive {
|
||||||
|
aliveTags = append(aliveTags, candidate)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// unfound candidate is considered alive
|
||||||
|
aliveTags = append(aliveTags, candidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tags = aliveTags
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tags[dice.Roll(n)]
|
n := len(tags)
|
||||||
|
if n == 0 {
|
||||||
|
// goes to fallbackTag
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
tag := tags[s.index%n]
|
||||||
|
s.index = (s.index + 1) % n
|
||||||
|
return tag
|
||||||
}
|
}
|
||||||
|
|
||||||
type Balancer struct {
|
type Balancer struct {
|
||||||
selectors []string
|
selectors []string
|
||||||
strategy BalancingStrategy
|
strategy BalancingStrategy
|
||||||
ohm outbound.Manager
|
ohm outbound.Manager
|
||||||
|
fallbackTag string
|
||||||
|
|
||||||
|
override override
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PickOutbound picks the tag of a outbound
|
||||||
func (b *Balancer) PickOutbound() (string, error) {
|
func (b *Balancer) PickOutbound() (string, error) {
|
||||||
hs, ok := b.ohm.(outbound.HandlerSelector)
|
candidates, err := b.SelectOutbounds()
|
||||||
if !ok {
|
if err != nil {
|
||||||
return "", newError("outbound.Manager is not a HandlerSelector")
|
if b.fallbackTag != "" {
|
||||||
|
newError("fallback to [", b.fallbackTag, "], due to error: ", err).AtInfo().WriteToLog()
|
||||||
|
return b.fallbackTag, nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
tags := hs.Select(b.selectors)
|
var tag string
|
||||||
if len(tags) == 0 {
|
if o := b.override.Get(); o != "" {
|
||||||
return "", newError("no available outbounds selected")
|
tag = o
|
||||||
|
} else {
|
||||||
|
tag = b.strategy.PickOutbound(candidates)
|
||||||
}
|
}
|
||||||
tag := b.strategy.PickOutbound(tags)
|
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
|
if b.fallbackTag != "" {
|
||||||
|
newError("fallback to [", b.fallbackTag, "], due to empty tag returned").AtInfo().WriteToLog()
|
||||||
|
return b.fallbackTag, nil
|
||||||
|
}
|
||||||
|
// will use default handler
|
||||||
return "", newError("balancing strategy returns empty tag")
|
return "", newError("balancing strategy returns empty tag")
|
||||||
}
|
}
|
||||||
return tag, nil
|
return tag, nil
|
||||||
|
@ -50,3 +122,45 @@ func (b *Balancer) InjectContext(ctx context.Context) {
|
||||||
contextReceiver.InjectContext(ctx)
|
contextReceiver.InjectContext(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectOutbounds select outbounds with selectors of the Balancer
|
||||||
|
func (b *Balancer) SelectOutbounds() ([]string, error) {
|
||||||
|
hs, ok := b.ohm.(outbound.HandlerSelector)
|
||||||
|
if !ok {
|
||||||
|
return nil, newError("outbound.Manager is not a HandlerSelector")
|
||||||
|
}
|
||||||
|
tags := hs.Select(b.selectors)
|
||||||
|
return tags, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrincipleTarget implements routing.BalancerPrincipleTarget
|
||||||
|
func (r *Router) GetPrincipleTarget(tag string) ([]string, error) {
|
||||||
|
if b, ok := r.balancers[tag]; ok {
|
||||||
|
if s, ok := b.strategy.(BalancingPrincipleTarget); ok {
|
||||||
|
candidates, err := b.SelectOutbounds()
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("unable to select outbounds").Base(err)
|
||||||
|
}
|
||||||
|
return s.GetPrincipleTarget(candidates), nil
|
||||||
|
}
|
||||||
|
return nil, newError("unsupported GetPrincipleTarget")
|
||||||
|
}
|
||||||
|
return nil, newError("cannot find tag")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOverrideTarget implements routing.BalancerOverrider
|
||||||
|
func (r *Router) SetOverrideTarget(tag, target string) error {
|
||||||
|
if b, ok := r.balancers[tag]; ok {
|
||||||
|
b.override.Put(target)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return newError("cannot find tag")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOverrideTarget implements routing.BalancerOverrider
|
||||||
|
func (r *Router) GetOverrideTarget(tag string) (string, error) {
|
||||||
|
if b, ok := r.balancers[tag]; ok {
|
||||||
|
return b.override.Get(), nil
|
||||||
|
}
|
||||||
|
return "", newError("cannot find tag")
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *Router) OverrideBalancer(balancer string, target string) error {
|
||||||
|
var b *Balancer
|
||||||
|
for tag, bl := range r.balancers {
|
||||||
|
if tag == balancer {
|
||||||
|
b = bl
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b == nil {
|
||||||
|
return newError("balancer '", balancer, "' not found")
|
||||||
|
}
|
||||||
|
b.override.Put(target)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type overrideSettings struct {
|
||||||
|
target string
|
||||||
|
}
|
||||||
|
|
||||||
|
type override struct {
|
||||||
|
access sync.RWMutex
|
||||||
|
settings overrideSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets the override settings
|
||||||
|
func (o *override) Get() string {
|
||||||
|
o.access.RLock()
|
||||||
|
defer o.access.RUnlock()
|
||||||
|
return o.settings.target
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put updates the override settings
|
||||||
|
func (o *override) Put(target string) {
|
||||||
|
o.access.Lock()
|
||||||
|
defer o.access.Unlock()
|
||||||
|
o.settings.target = target
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear clears the override settings
|
||||||
|
func (o *override) Clear() {
|
||||||
|
o.access.Lock()
|
||||||
|
defer o.access.Unlock()
|
||||||
|
o.settings.target = ""
|
||||||
|
}
|
|
@ -19,6 +19,55 @@ type routingServer struct {
|
||||||
routingStats stats.Channel
|
routingStats stats.Channel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *routingServer) GetBalancerInfo(ctx context.Context, request *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) {
|
||||||
|
var ret GetBalancerInfoResponse
|
||||||
|
ret.Balancer = &BalancerMsg{}
|
||||||
|
if bo, ok := s.router.(routing.BalancerOverrider); ok {
|
||||||
|
{
|
||||||
|
res, err := bo.GetOverrideTarget(request.GetTag())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret.Balancer.Override = &OverrideInfo{
|
||||||
|
Target: res,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pt, ok := s.router.(routing.BalancerPrincipleTarget); ok {
|
||||||
|
{
|
||||||
|
res, err := pt.GetPrincipleTarget(request.GetTag())
|
||||||
|
if err != nil {
|
||||||
|
newError("unable to obtain principle target").Base(err).AtInfo().WriteToLog()
|
||||||
|
} else {
|
||||||
|
ret.Balancer.PrincipleTarget = &PrincipleTargetInfo{Tag: res}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *routingServer) OverrideBalancerTarget(ctx context.Context, request *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) {
|
||||||
|
if bo, ok := s.router.(routing.BalancerOverrider); ok {
|
||||||
|
return &OverrideBalancerTargetResponse{}, bo.SetOverrideTarget(request.BalancerTag, request.Target)
|
||||||
|
}
|
||||||
|
return nil, newError("unsupported router implementation")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *routingServer) AddRule(ctx context.Context, request *AddRuleRequest) (*AddRuleResponse, error) {
|
||||||
|
if bo, ok := s.router.(routing.Router); ok {
|
||||||
|
return &AddRuleResponse{}, bo.AddRule(request.Config, request.ShouldAppend)
|
||||||
|
}
|
||||||
|
return nil, newError("unsupported router implementation")
|
||||||
|
|
||||||
|
}
|
||||||
|
func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleRequest) (*RemoveRuleResponse, error) {
|
||||||
|
if bo, ok := s.router.(routing.Router); ok {
|
||||||
|
return &RemoveRuleResponse{}, bo.RemoveRule(request.RuleTag)
|
||||||
|
}
|
||||||
|
return nil, newError("unsupported router implementation")
|
||||||
|
}
|
||||||
|
|
||||||
// NewRoutingServer creates a statistics service with statistics manager.
|
// NewRoutingServer creates a statistics service with statistics manager.
|
||||||
func NewRoutingServer(router routing.Router, routingStats stats.Channel) RoutingServiceServer {
|
func NewRoutingServer(router routing.Router, routingStats stats.Channel) RoutingServiceServer {
|
||||||
return &routingServer{
|
return &routingServer{
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,6 +7,7 @@ option java_package = "com.xray.app.router.command";
|
||||||
option java_multiple_files = true;
|
option java_multiple_files = true;
|
||||||
|
|
||||||
import "common/net/network.proto";
|
import "common/net/network.proto";
|
||||||
|
import "common/serial/typed_message.proto";
|
||||||
|
|
||||||
// RoutingContext is the context with information relative to routing process.
|
// RoutingContext is the context with information relative to routing process.
|
||||||
// It conforms to the structure of xray.features.routing.Context and
|
// It conforms to the structure of xray.features.routing.Context and
|
||||||
|
@ -60,10 +61,56 @@ message TestRouteRequest {
|
||||||
bool PublishResult = 3;
|
bool PublishResult = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message PrincipleTargetInfo {
|
||||||
|
repeated string tag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OverrideInfo {
|
||||||
|
string target = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BalancerMsg {
|
||||||
|
OverrideInfo override = 5;
|
||||||
|
PrincipleTargetInfo principle_target = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetBalancerInfoRequest {
|
||||||
|
string tag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetBalancerInfoResponse {
|
||||||
|
BalancerMsg balancer = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OverrideBalancerTargetRequest {
|
||||||
|
string balancerTag = 1;
|
||||||
|
string target = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OverrideBalancerTargetResponse {}
|
||||||
|
|
||||||
|
message AddRuleRequest {
|
||||||
|
xray.common.serial.TypedMessage config = 1;
|
||||||
|
bool shouldAppend = 2;
|
||||||
|
}
|
||||||
|
message AddRuleResponse {}
|
||||||
|
|
||||||
|
message RemoveRuleRequest {
|
||||||
|
string ruleTag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RemoveRuleResponse {}
|
||||||
|
|
||||||
service RoutingService {
|
service RoutingService {
|
||||||
rpc SubscribeRoutingStats(SubscribeRoutingStatsRequest)
|
rpc SubscribeRoutingStats(SubscribeRoutingStatsRequest)
|
||||||
returns (stream RoutingContext) {}
|
returns (stream RoutingContext) {}
|
||||||
rpc TestRoute(TestRouteRequest) returns (RoutingContext) {}
|
rpc TestRoute(TestRouteRequest) returns (RoutingContext) {}
|
||||||
|
|
||||||
|
rpc GetBalancerInfo(GetBalancerInfoRequest) returns (GetBalancerInfoResponse){}
|
||||||
|
rpc OverrideBalancerTarget(OverrideBalancerTargetRequest) returns (OverrideBalancerTargetResponse) {}
|
||||||
|
|
||||||
|
rpc AddRule(AddRuleRequest) returns (AddRuleResponse) {}
|
||||||
|
rpc RemoveRule(RemoveRuleRequest) returns (RemoveRuleResponse) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
message Config {}
|
message Config {}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// - protoc-gen-go-grpc v1.2.0
|
// - protoc-gen-go-grpc v1.3.0
|
||||||
// - protoc v3.21.12
|
// - protoc v5.27.0
|
||||||
// source: app/router/command/command.proto
|
// source: app/router/command/command.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
@ -18,12 +18,25 @@ import (
|
||||||
// Requires gRPC-Go v1.32.0 or later.
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
const _ = grpc.SupportPackageIsVersion7
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoutingService_SubscribeRoutingStats_FullMethodName = "/xray.app.router.command.RoutingService/SubscribeRoutingStats"
|
||||||
|
RoutingService_TestRoute_FullMethodName = "/xray.app.router.command.RoutingService/TestRoute"
|
||||||
|
RoutingService_GetBalancerInfo_FullMethodName = "/xray.app.router.command.RoutingService/GetBalancerInfo"
|
||||||
|
RoutingService_OverrideBalancerTarget_FullMethodName = "/xray.app.router.command.RoutingService/OverrideBalancerTarget"
|
||||||
|
RoutingService_AddRule_FullMethodName = "/xray.app.router.command.RoutingService/AddRule"
|
||||||
|
RoutingService_RemoveRule_FullMethodName = "/xray.app.router.command.RoutingService/RemoveRule"
|
||||||
|
)
|
||||||
|
|
||||||
// RoutingServiceClient is the client API for RoutingService service.
|
// RoutingServiceClient is the client API for RoutingService service.
|
||||||
//
|
//
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
type RoutingServiceClient interface {
|
type RoutingServiceClient interface {
|
||||||
SubscribeRoutingStats(ctx context.Context, in *SubscribeRoutingStatsRequest, opts ...grpc.CallOption) (RoutingService_SubscribeRoutingStatsClient, error)
|
SubscribeRoutingStats(ctx context.Context, in *SubscribeRoutingStatsRequest, opts ...grpc.CallOption) (RoutingService_SubscribeRoutingStatsClient, error)
|
||||||
TestRoute(ctx context.Context, in *TestRouteRequest, opts ...grpc.CallOption) (*RoutingContext, error)
|
TestRoute(ctx context.Context, in *TestRouteRequest, opts ...grpc.CallOption) (*RoutingContext, error)
|
||||||
|
GetBalancerInfo(ctx context.Context, in *GetBalancerInfoRequest, opts ...grpc.CallOption) (*GetBalancerInfoResponse, error)
|
||||||
|
OverrideBalancerTarget(ctx context.Context, in *OverrideBalancerTargetRequest, opts ...grpc.CallOption) (*OverrideBalancerTargetResponse, error)
|
||||||
|
AddRule(ctx context.Context, in *AddRuleRequest, opts ...grpc.CallOption) (*AddRuleResponse, error)
|
||||||
|
RemoveRule(ctx context.Context, in *RemoveRuleRequest, opts ...grpc.CallOption) (*RemoveRuleResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type routingServiceClient struct {
|
type routingServiceClient struct {
|
||||||
|
@ -35,7 +48,7 @@ func NewRoutingServiceClient(cc grpc.ClientConnInterface) RoutingServiceClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *routingServiceClient) SubscribeRoutingStats(ctx context.Context, in *SubscribeRoutingStatsRequest, opts ...grpc.CallOption) (RoutingService_SubscribeRoutingStatsClient, error) {
|
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], RoutingService_SubscribeRoutingStats_FullMethodName, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -68,7 +81,43 @@ func (x *routingServiceSubscribeRoutingStatsClient) Recv() (*RoutingContext, err
|
||||||
|
|
||||||
func (c *routingServiceClient) TestRoute(ctx context.Context, in *TestRouteRequest, opts ...grpc.CallOption) (*RoutingContext, error) {
|
func (c *routingServiceClient) TestRoute(ctx context.Context, in *TestRouteRequest, opts ...grpc.CallOption) (*RoutingContext, error) {
|
||||||
out := new(RoutingContext)
|
out := new(RoutingContext)
|
||||||
err := c.cc.Invoke(ctx, "/xray.app.router.command.RoutingService/TestRoute", in, out, opts...)
|
err := c.cc.Invoke(ctx, RoutingService_TestRoute_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *routingServiceClient) GetBalancerInfo(ctx context.Context, in *GetBalancerInfoRequest, opts ...grpc.CallOption) (*GetBalancerInfoResponse, error) {
|
||||||
|
out := new(GetBalancerInfoResponse)
|
||||||
|
err := c.cc.Invoke(ctx, RoutingService_GetBalancerInfo_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *routingServiceClient) OverrideBalancerTarget(ctx context.Context, in *OverrideBalancerTargetRequest, opts ...grpc.CallOption) (*OverrideBalancerTargetResponse, error) {
|
||||||
|
out := new(OverrideBalancerTargetResponse)
|
||||||
|
err := c.cc.Invoke(ctx, RoutingService_OverrideBalancerTarget_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *routingServiceClient) AddRule(ctx context.Context, in *AddRuleRequest, opts ...grpc.CallOption) (*AddRuleResponse, error) {
|
||||||
|
out := new(AddRuleResponse)
|
||||||
|
err := c.cc.Invoke(ctx, RoutingService_AddRule_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *routingServiceClient) RemoveRule(ctx context.Context, in *RemoveRuleRequest, opts ...grpc.CallOption) (*RemoveRuleResponse, error) {
|
||||||
|
out := new(RemoveRuleResponse)
|
||||||
|
err := c.cc.Invoke(ctx, RoutingService_RemoveRule_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -81,6 +130,10 @@ func (c *routingServiceClient) TestRoute(ctx context.Context, in *TestRouteReque
|
||||||
type RoutingServiceServer interface {
|
type RoutingServiceServer interface {
|
||||||
SubscribeRoutingStats(*SubscribeRoutingStatsRequest, RoutingService_SubscribeRoutingStatsServer) error
|
SubscribeRoutingStats(*SubscribeRoutingStatsRequest, RoutingService_SubscribeRoutingStatsServer) error
|
||||||
TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error)
|
TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error)
|
||||||
|
GetBalancerInfo(context.Context, *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error)
|
||||||
|
OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error)
|
||||||
|
AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error)
|
||||||
|
RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error)
|
||||||
mustEmbedUnimplementedRoutingServiceServer()
|
mustEmbedUnimplementedRoutingServiceServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +147,18 @@ func (UnimplementedRoutingServiceServer) SubscribeRoutingStats(*SubscribeRouting
|
||||||
func (UnimplementedRoutingServiceServer) TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error) {
|
func (UnimplementedRoutingServiceServer) TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method TestRoute not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method TestRoute not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedRoutingServiceServer) GetBalancerInfo(context.Context, *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetBalancerInfo not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedRoutingServiceServer) OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method OverrideBalancerTarget not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedRoutingServiceServer) AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method AddRule not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedRoutingServiceServer) RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method RemoveRule not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedRoutingServiceServer) mustEmbedUnimplementedRoutingServiceServer() {}
|
func (UnimplementedRoutingServiceServer) mustEmbedUnimplementedRoutingServiceServer() {}
|
||||||
|
|
||||||
// UnsafeRoutingServiceServer may be embedded to opt out of forward compatibility for this service.
|
// UnsafeRoutingServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
@ -138,7 +203,7 @@ func _RoutingService_TestRoute_Handler(srv interface{}, ctx context.Context, dec
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/xray.app.router.command.RoutingService/TestRoute",
|
FullMethod: RoutingService_TestRoute_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(RoutingServiceServer).TestRoute(ctx, req.(*TestRouteRequest))
|
return srv.(RoutingServiceServer).TestRoute(ctx, req.(*TestRouteRequest))
|
||||||
|
@ -146,6 +211,78 @@ func _RoutingService_TestRoute_Handler(srv interface{}, ctx context.Context, dec
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _RoutingService_GetBalancerInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetBalancerInfoRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(RoutingServiceServer).GetBalancerInfo(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: RoutingService_GetBalancerInfo_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(RoutingServiceServer).GetBalancerInfo(ctx, req.(*GetBalancerInfoRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _RoutingService_OverrideBalancerTarget_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(OverrideBalancerTargetRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(RoutingServiceServer).OverrideBalancerTarget(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: RoutingService_OverrideBalancerTarget_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(RoutingServiceServer).OverrideBalancerTarget(ctx, req.(*OverrideBalancerTargetRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _RoutingService_AddRule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(AddRuleRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(RoutingServiceServer).AddRule(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: RoutingService_AddRule_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(RoutingServiceServer).AddRule(ctx, req.(*AddRuleRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _RoutingService_RemoveRule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(RemoveRuleRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(RoutingServiceServer).RemoveRule(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: RoutingService_RemoveRule_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(RoutingServiceServer).RemoveRule(ctx, req.(*RemoveRuleRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
// RoutingService_ServiceDesc is the grpc.ServiceDesc for RoutingService service.
|
// RoutingService_ServiceDesc is the grpc.ServiceDesc for RoutingService service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
@ -157,6 +294,22 @@ var RoutingService_ServiceDesc = grpc.ServiceDesc{
|
||||||
MethodName: "TestRoute",
|
MethodName: "TestRoute",
|
||||||
Handler: _RoutingService_TestRoute_Handler,
|
Handler: _RoutingService_TestRoute_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetBalancerInfo",
|
||||||
|
Handler: _RoutingService_GetBalancerInfo_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "OverrideBalancerTarget",
|
||||||
|
Handler: _RoutingService_OverrideBalancerTarget_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "AddRule",
|
||||||
|
Handler: _RoutingService_AddRule_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "RemoveRule",
|
||||||
|
Handler: _RoutingService_RemoveRule_Handler,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{
|
Streams: []grpc.StreamDesc{
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
"github.com/xtls/xray-core/testing/mocks"
|
"github.com/xtls/xray-core/testing/mocks"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
"google.golang.org/grpc/test/bufconn"
|
"google.golang.org/grpc/test/bufconn"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,7 +81,7 @@ func TestServiceSubscribeRoutingStats(t *testing.T) {
|
||||||
// Client goroutine
|
// Client goroutine
|
||||||
go func() {
|
go func() {
|
||||||
defer lis.Close()
|
defer lis.Close()
|
||||||
conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure())
|
conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCh <- err
|
errCh <- err
|
||||||
return
|
return
|
||||||
|
@ -214,7 +215,7 @@ func TestServiceSubscribeSubsetOfFields(t *testing.T) {
|
||||||
// Client goroutine
|
// Client goroutine
|
||||||
go func() {
|
go func() {
|
||||||
defer lis.Close()
|
defer lis.Close()
|
||||||
conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure())
|
conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCh <- err
|
errCh <- err
|
||||||
return
|
return
|
||||||
|
@ -318,7 +319,7 @@ func TestSerivceTestRoute(t *testing.T) {
|
||||||
TargetTag: &router.RoutingRule_Tag{Tag: "out"},
|
TargetTag: &router.RoutingRule_Tag{Tag: "out"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, mocks.NewDNSClient(mockCtl), mocks.NewOutboundManager(mockCtl)))
|
}, mocks.NewDNSClient(mockCtl), mocks.NewOutboundManager(mockCtl), nil))
|
||||||
|
|
||||||
lis := bufconn.Listen(1024 * 1024)
|
lis := bufconn.Listen(1024 * 1024)
|
||||||
bufDialer := func(context.Context, string) (net.Conn, error) {
|
bufDialer := func(context.Context, string) (net.Conn, error) {
|
||||||
|
@ -337,7 +338,7 @@ func TestSerivceTestRoute(t *testing.T) {
|
||||||
// Client goroutine
|
// Client goroutine
|
||||||
go func() {
|
go func() {
|
||||||
defer lis.Close()
|
defer lis.Close()
|
||||||
conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure())
|
conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCh <- err
|
errCh <- err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/strmatcher"
|
"github.com/xtls/xray-core/common/strmatcher"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
"go.starlark.net/starlark"
|
|
||||||
"go.starlark.net/syntax"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Condition interface {
|
type Condition interface {
|
||||||
|
@ -284,44 +283,22 @@ func (m *ProtocolMatcher) Apply(ctx routing.Context) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AttributeMatcher struct {
|
type AttributeMatcher struct {
|
||||||
program *starlark.Program
|
configuredKeys map[string]*regexp.Regexp
|
||||||
}
|
|
||||||
|
|
||||||
func NewAttributeMatcher(code string) (*AttributeMatcher, error) {
|
|
||||||
starFile, err := syntax.Parse("attr.star", "satisfied=("+code+")", 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newError("attr rule").Base(err)
|
|
||||||
}
|
|
||||||
p, err := starlark.FileProgram(starFile, func(name string) bool {
|
|
||||||
return name == "attrs"
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &AttributeMatcher{
|
|
||||||
program: p,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match implements attributes matching.
|
// Match implements attributes matching.
|
||||||
func (m *AttributeMatcher) Match(attrs map[string]string) bool {
|
func (m *AttributeMatcher) Match(attrs map[string]string) bool {
|
||||||
attrsDict := new(starlark.Dict)
|
// header keys are case insensitive most likely. So we do a convert
|
||||||
|
httpHeaders := make(map[string]string)
|
||||||
for key, value := range attrs {
|
for key, value := range attrs {
|
||||||
attrsDict.SetKey(starlark.String(key), starlark.String(value))
|
httpHeaders[strings.ToLower(key)] = value
|
||||||
}
|
}
|
||||||
|
for key, regex := range m.configuredKeys {
|
||||||
predefined := make(starlark.StringDict)
|
if a, ok := httpHeaders[key]; !ok || !regex.MatchString(a) {
|
||||||
predefined["attrs"] = attrsDict
|
return false
|
||||||
|
}
|
||||||
thread := &starlark.Thread{
|
|
||||||
Name: "matcher",
|
|
||||||
}
|
}
|
||||||
results, err := m.program.Init(thread, predefined)
|
return true
|
||||||
if err != nil {
|
|
||||||
newError("attr matcher").Base(err).WriteToLog()
|
|
||||||
}
|
|
||||||
satisfied := results["satisfied"]
|
|
||||||
return satisfied != nil && bool(satisfied.Truth())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply implements Condition.
|
// Apply implements Condition.
|
||||||
|
|
|
@ -1,81 +1,49 @@
|
||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"net/netip"
|
||||||
"sort"
|
"strconv"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"go4.org/netipx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ipv6 struct {
|
|
||||||
a uint64
|
|
||||||
b uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type GeoIPMatcher struct {
|
type GeoIPMatcher struct {
|
||||||
countryCode string
|
countryCode string
|
||||||
reverseMatch bool
|
reverseMatch bool
|
||||||
ip4 []uint32
|
ip4 *netipx.IPSet
|
||||||
prefix4 []uint8
|
ip6 *netipx.IPSet
|
||||||
ip6 []ipv6
|
|
||||||
prefix6 []uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalize4(ip uint32, prefix uint8) uint32 {
|
|
||||||
return (ip >> (32 - prefix)) << (32 - prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalize6(ip ipv6, prefix uint8) ipv6 {
|
|
||||||
if prefix <= 64 {
|
|
||||||
ip.a = (ip.a >> (64 - prefix)) << (64 - prefix)
|
|
||||||
ip.b = 0
|
|
||||||
} else {
|
|
||||||
ip.b = (ip.b >> (128 - prefix)) << (128 - prefix)
|
|
||||||
}
|
|
||||||
return ip
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
|
func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
|
||||||
ip4Count := 0
|
var builder4, builder6 netipx.IPSetBuilder
|
||||||
ip6Count := 0
|
|
||||||
|
|
||||||
for _, cidr := range cidrs {
|
for _, cidr := range cidrs {
|
||||||
ip := cidr.Ip
|
ip := net.IP(cidr.GetIp())
|
||||||
|
ipPrefixString := ip.String() + "/" + strconv.Itoa(int(cidr.GetPrefix()))
|
||||||
|
ipPrefix, err := netip.ParsePrefix(ipPrefixString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
switch len(ip) {
|
switch len(ip) {
|
||||||
case 4:
|
case net.IPv4len:
|
||||||
ip4Count++
|
builder4.AddPrefix(ipPrefix)
|
||||||
case 16:
|
case net.IPv6len:
|
||||||
ip6Count++
|
builder6.AddPrefix(ipPrefix)
|
||||||
default:
|
|
||||||
return newError("unexpect ip length: ", len(ip))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cidrList := CIDRList(cidrs)
|
if ip4, err := builder4.IPSet(); err != nil {
|
||||||
sort.Sort(&cidrList)
|
return err
|
||||||
|
} else {
|
||||||
|
m.ip4 = ip4
|
||||||
|
}
|
||||||
|
|
||||||
m.ip4 = make([]uint32, 0, ip4Count)
|
if ip6, err := builder6.IPSet(); err != nil {
|
||||||
m.prefix4 = make([]uint8, 0, ip4Count)
|
return err
|
||||||
m.ip6 = make([]ipv6, 0, ip6Count)
|
} else {
|
||||||
m.prefix6 = make([]uint8, 0, ip6Count)
|
m.ip6 = ip6
|
||||||
|
|
||||||
for _, cidr := range cidrList {
|
|
||||||
ip := cidr.Ip
|
|
||||||
prefix := uint8(cidr.Prefix)
|
|
||||||
switch len(ip) {
|
|
||||||
case 4:
|
|
||||||
m.ip4 = append(m.ip4, normalize4(binary.BigEndian.Uint32(ip), prefix))
|
|
||||||
m.prefix4 = append(m.prefix4, prefix)
|
|
||||||
case 16:
|
|
||||||
ip6 := ipv6{
|
|
||||||
a: binary.BigEndian.Uint64(ip[0:8]),
|
|
||||||
b: binary.BigEndian.Uint64(ip[8:16]),
|
|
||||||
}
|
|
||||||
ip6 = normalize6(ip6, prefix)
|
|
||||||
|
|
||||||
m.ip6 = append(m.ip6, ip6)
|
|
||||||
m.prefix6 = append(m.prefix6, prefix)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -85,91 +53,37 @@ func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
|
||||||
m.reverseMatch = isReverseMatch
|
m.reverseMatch = isReverseMatch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *GeoIPMatcher) match4(ip uint32) bool {
|
func (m *GeoIPMatcher) match4(ip net.IP) bool {
|
||||||
if len(m.ip4) == 0 {
|
nip, ok := netipx.FromStdIP(ip)
|
||||||
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if ip < m.ip4[0] {
|
return m.ip4.Contains(nip)
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
size := uint32(len(m.ip4))
|
|
||||||
l := uint32(0)
|
|
||||||
r := size
|
|
||||||
for l < r {
|
|
||||||
x := ((l + r) >> 1)
|
|
||||||
if ip < m.ip4[x] {
|
|
||||||
r = x
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
nip := normalize4(ip, m.prefix4[x])
|
|
||||||
if nip == m.ip4[x] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
l = x + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return l > 0 && normalize4(ip, m.prefix4[l-1]) == m.ip4[l-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func less6(a ipv6, b ipv6) bool {
|
func (m *GeoIPMatcher) match6(ip net.IP) bool {
|
||||||
return a.a < b.a || (a.a == b.a && a.b < b.b)
|
nip, ok := netipx.FromStdIP(ip)
|
||||||
}
|
if !ok {
|
||||||
|
|
||||||
func (m *GeoIPMatcher) match6(ip ipv6) bool {
|
|
||||||
if len(m.ip6) == 0 {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if less6(ip, m.ip6[0]) {
|
return m.ip6.Contains(nip)
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
size := uint32(len(m.ip6))
|
|
||||||
l := uint32(0)
|
|
||||||
r := size
|
|
||||||
for l < r {
|
|
||||||
x := (l + r) / 2
|
|
||||||
if less6(ip, m.ip6[x]) {
|
|
||||||
r = x
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if normalize6(ip, m.prefix6[x]) == m.ip6[x] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
l = x + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return l > 0 && normalize6(ip, m.prefix6[l-1]) == m.ip6[l-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match returns true if the given ip is included by the GeoIP.
|
// Match returns true if the given ip is included by the GeoIP.
|
||||||
func (m *GeoIPMatcher) Match(ip net.IP) bool {
|
func (m *GeoIPMatcher) Match(ip net.IP) bool {
|
||||||
|
isMatched := false
|
||||||
switch len(ip) {
|
switch len(ip) {
|
||||||
case 4:
|
case net.IPv4len:
|
||||||
if m.reverseMatch {
|
isMatched = m.match4(ip)
|
||||||
return !m.match4(binary.BigEndian.Uint32(ip))
|
case net.IPv6len:
|
||||||
}
|
isMatched = m.match6(ip)
|
||||||
return m.match4(binary.BigEndian.Uint32(ip))
|
|
||||||
case 16:
|
|
||||||
if m.reverseMatch {
|
|
||||||
return !m.match6(ipv6{
|
|
||||||
a: binary.BigEndian.Uint64(ip[0:8]),
|
|
||||||
b: binary.BigEndian.Uint64(ip[8:16]),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return m.match6(ipv6{
|
|
||||||
a: binary.BigEndian.Uint64(ip[0:8]),
|
|
||||||
b: binary.BigEndian.Uint64(ip[8:16]),
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
if m.reverseMatch {
|
||||||
|
return !isMatched
|
||||||
|
}
|
||||||
|
return isMatched
|
||||||
}
|
}
|
||||||
|
|
||||||
// GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code.
|
// GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code.
|
||||||
|
|
|
@ -5,12 +5,12 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
"github.com/xtls/xray-core/app/router"
|
"github.com/xtls/xray-core/app/router"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/platform"
|
"github.com/xtls/xray-core/common/platform"
|
||||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -53,7 +53,7 @@ func TestGeoIPMatcherContainer(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGeoIPMatcher(t *testing.T) {
|
func TestGeoIPMatcher(t *testing.T) {
|
||||||
cidrList := router.CIDRList{
|
cidrList := []*router.CIDR{
|
||||||
{Ip: []byte{0, 0, 0, 0}, Prefix: 8},
|
{Ip: []byte{0, 0, 0, 0}, Prefix: 8},
|
||||||
{Ip: []byte{10, 0, 0, 0}, Prefix: 8},
|
{Ip: []byte{10, 0, 0, 0}, Prefix: 8},
|
||||||
{Ip: []byte{100, 64, 0, 0}, Prefix: 10},
|
{Ip: []byte{100, 64, 0, 0}, Prefix: 10},
|
||||||
|
@ -124,8 +124,40 @@ func TestGeoIPMatcher(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGeoIPMatcherRegression(t *testing.T) {
|
||||||
|
cidrList := []*router.CIDR{
|
||||||
|
{Ip: []byte{98, 108, 20, 0}, Prefix: 22},
|
||||||
|
{Ip: []byte{98, 108, 20, 0}, Prefix: 23},
|
||||||
|
}
|
||||||
|
|
||||||
|
matcher := &router.GeoIPMatcher{}
|
||||||
|
common.Must(matcher.Init(cidrList))
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
Input string
|
||||||
|
Output bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Input: "98.108.22.11",
|
||||||
|
Output: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "98.108.25.0",
|
||||||
|
Output: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
ip := net.ParseAddress(testCase.Input).IP()
|
||||||
|
actual := matcher.Match(ip)
|
||||||
|
if actual != testCase.Output {
|
||||||
|
t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGeoIPReverseMatcher(t *testing.T) {
|
func TestGeoIPReverseMatcher(t *testing.T) {
|
||||||
cidrList := router.CIDRList{
|
cidrList := []*router.CIDR{
|
||||||
{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
|
{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
|
||||||
{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
|
{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
. "github.com/xtls/xray-core/app/router"
|
. "github.com/xtls/xray-core/app/router"
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
@ -18,6 +17,7 @@ import (
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
routing_session "github.com/xtls/xray-core/features/routing/session"
|
routing_session "github.com/xtls/xray-core/features/routing/session"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -307,8 +307,10 @@ func TestRoutingRule(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rule: &RoutingRule{
|
rule: &RoutingRule{
|
||||||
Protocol: []string{"http"},
|
Protocol: []string{"http"},
|
||||||
Attributes: "attrs[':path'].startswith('/test')",
|
Attributes: map[string]string{
|
||||||
|
":path": "/test",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
test: []ruleTest{
|
test: []ruleTest{
|
||||||
{
|
{
|
||||||
|
@ -317,6 +319,19 @@ func TestRoutingRule(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
rule: &RoutingRule{
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"Custom": "p([a-z]+)ch",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
test: []ruleTest{
|
||||||
|
{
|
||||||
|
input: withContent(&session.Content{Attributes: map[string]string{"custom": "peach"}}),
|
||||||
|
output: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range cases {
|
for _, test := range cases {
|
||||||
|
|
|
@ -1,52 +1,17 @@
|
||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CIDRList is an alias of []*CIDR to provide sort.Interface.
|
|
||||||
type CIDRList []*CIDR
|
|
||||||
|
|
||||||
// Len implements sort.Interface.
|
|
||||||
func (l *CIDRList) Len() int {
|
|
||||||
return len(*l)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Less implements sort.Interface.
|
|
||||||
func (l *CIDRList) Less(i int, j int) bool {
|
|
||||||
ci := (*l)[i]
|
|
||||||
cj := (*l)[j]
|
|
||||||
|
|
||||||
if len(ci.Ip) < len(cj.Ip) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ci.Ip) > len(cj.Ip) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for k := 0; k < len(ci.Ip); k++ {
|
|
||||||
if ci.Ip[k] < cj.Ip[k] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if ci.Ip[k] > cj.Ip[k] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ci.Prefix < cj.Prefix
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap implements sort.Interface.
|
|
||||||
func (l *CIDRList) Swap(i int, j int) {
|
|
||||||
(*l)[i], (*l)[j] = (*l)[j], (*l)[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
type Rule struct {
|
type Rule struct {
|
||||||
Tag string
|
Tag string
|
||||||
|
RuleTag string
|
||||||
Balancer *Balancer
|
Balancer *Balancer
|
||||||
Condition Condition
|
Condition Condition
|
||||||
}
|
}
|
||||||
|
@ -143,11 +108,11 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rr.Attributes) > 0 {
|
if len(rr.Attributes) > 0 {
|
||||||
cond, err := NewAttributeMatcher(rr.Attributes)
|
configuredKeys := make(map[string]*regexp.Regexp)
|
||||||
if err != nil {
|
for key, value := range rr.Attributes {
|
||||||
return nil, err
|
configuredKeys[strings.ToLower(key)] = regexp.MustCompile(value)
|
||||||
}
|
}
|
||||||
conds.Add(cond)
|
conds.Add(&AttributeMatcher{configuredKeys})
|
||||||
}
|
}
|
||||||
|
|
||||||
if conds.Len() == 0 {
|
if conds.Len() == 0 {
|
||||||
|
@ -157,22 +122,49 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
||||||
return conds, nil
|
return conds, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (br *BalancingRule) Build(ohm outbound.Manager) (*Balancer, error) {
|
// Build builds the balancing rule
|
||||||
switch br.Strategy {
|
func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatcher) (*Balancer, error) {
|
||||||
case "leastPing":
|
switch strings.ToLower(br.Strategy) {
|
||||||
|
case "leastping":
|
||||||
return &Balancer{
|
return &Balancer{
|
||||||
selectors: br.OutboundSelector,
|
selectors: br.OutboundSelector,
|
||||||
strategy: &LeastPingStrategy{},
|
strategy: &LeastPingStrategy{},
|
||||||
ohm: ohm,
|
fallbackTag: br.FallbackTag,
|
||||||
|
ohm: ohm,
|
||||||
|
}, nil
|
||||||
|
case "roundrobin":
|
||||||
|
return &Balancer{
|
||||||
|
selectors: br.OutboundSelector,
|
||||||
|
strategy: &RoundRobinStrategy{FallbackTag: br.FallbackTag},
|
||||||
|
fallbackTag: br.FallbackTag,
|
||||||
|
ohm: ohm,
|
||||||
|
}, nil
|
||||||
|
case "leastload":
|
||||||
|
i, err := br.StrategySettings.GetInstance()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s, ok := i.(*StrategyLeastLoadConfig)
|
||||||
|
if !ok {
|
||||||
|
return nil, newError("not a StrategyLeastLoadConfig").AtError()
|
||||||
|
}
|
||||||
|
leastLoadStrategy := NewLeastLoadStrategy(s)
|
||||||
|
return &Balancer{
|
||||||
|
selectors: br.OutboundSelector,
|
||||||
|
ohm: ohm,
|
||||||
|
fallbackTag: br.FallbackTag,
|
||||||
|
strategy: leastLoadStrategy,
|
||||||
}, nil
|
}, nil
|
||||||
case "random":
|
case "random":
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
case "":
|
||||||
return &Balancer{
|
return &Balancer{
|
||||||
selectors: br.OutboundSelector,
|
selectors: br.OutboundSelector,
|
||||||
strategy: &RandomStrategy{},
|
ohm: ohm,
|
||||||
ohm: ohm,
|
fallbackTag: br.FallbackTag,
|
||||||
|
strategy: &RandomStrategy{FallbackTag: br.FallbackTag},
|
||||||
}, nil
|
}, nil
|
||||||
|
default:
|
||||||
|
return nil, newError("unrecognized balancer type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/router/config.proto
|
// source: app/router/config.proto
|
||||||
|
|
||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
net "github.com/xtls/xray-core/common/net"
|
net "github.com/xtls/xray-core/common/net"
|
||||||
|
serial "github.com/xtls/xray-core/common/serial"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
@ -131,7 +132,7 @@ func (x Config_DomainStrategy) Number() protoreflect.EnumNumber {
|
||||||
|
|
||||||
// Deprecated: Use Config_DomainStrategy.Descriptor instead.
|
// Deprecated: Use Config_DomainStrategy.Descriptor instead.
|
||||||
func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) {
|
func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) {
|
||||||
return file_app_router_config_proto_rawDescGZIP(), []int{8, 0}
|
return file_app_router_config_proto_rawDescGZIP(), []int{10, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Domain for routing decision.
|
// Domain for routing decision.
|
||||||
|
@ -481,12 +482,13 @@ type RoutingRule struct {
|
||||||
// *RoutingRule_Tag
|
// *RoutingRule_Tag
|
||||||
// *RoutingRule_BalancingTag
|
// *RoutingRule_BalancingTag
|
||||||
TargetTag isRoutingRule_TargetTag `protobuf_oneof:"target_tag"`
|
TargetTag isRoutingRule_TargetTag `protobuf_oneof:"target_tag"`
|
||||||
|
RuleTag string `protobuf:"bytes,18,opt,name=rule_tag,json=ruleTag,proto3" json:"rule_tag,omitempty"`
|
||||||
// List of domains for target domain matching.
|
// List of domains for target domain matching.
|
||||||
Domain []*Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"`
|
Domain []*Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"`
|
||||||
// List of CIDRs for target IP address matching.
|
// List of CIDRs for target IP address matching.
|
||||||
// Deprecated. Use geoip below.
|
// Deprecated. Use geoip below.
|
||||||
//
|
//
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/router/config.proto.
|
||||||
Cidr []*CIDR `protobuf:"bytes,3,rep,name=cidr,proto3" json:"cidr,omitempty"`
|
Cidr []*CIDR `protobuf:"bytes,3,rep,name=cidr,proto3" json:"cidr,omitempty"`
|
||||||
// List of GeoIPs for target IP address matching. If this entry exists, the
|
// List of GeoIPs for target IP address matching. If this entry exists, the
|
||||||
// cidr above will have no effect. GeoIP fields with the same country code are
|
// cidr above will have no effect. GeoIP fields with the same country code are
|
||||||
|
@ -496,30 +498,30 @@ type RoutingRule struct {
|
||||||
// A range of port [from, to]. If the destination port is in this range, this
|
// A range of port [from, to]. If the destination port is in this range, this
|
||||||
// rule takes effect. Deprecated. Use port_list.
|
// rule takes effect. Deprecated. Use port_list.
|
||||||
//
|
//
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/router/config.proto.
|
||||||
PortRange *net.PortRange `protobuf:"bytes,4,opt,name=port_range,json=portRange,proto3" json:"port_range,omitempty"`
|
PortRange *net.PortRange `protobuf:"bytes,4,opt,name=port_range,json=portRange,proto3" json:"port_range,omitempty"`
|
||||||
// List of ports.
|
// List of ports.
|
||||||
PortList *net.PortList `protobuf:"bytes,14,opt,name=port_list,json=portList,proto3" json:"port_list,omitempty"`
|
PortList *net.PortList `protobuf:"bytes,14,opt,name=port_list,json=portList,proto3" json:"port_list,omitempty"`
|
||||||
// List of networks. Deprecated. Use networks.
|
// List of networks. Deprecated. Use networks.
|
||||||
//
|
//
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/router/config.proto.
|
||||||
NetworkList *net.NetworkList `protobuf:"bytes,5,opt,name=network_list,json=networkList,proto3" json:"network_list,omitempty"`
|
NetworkList *net.NetworkList `protobuf:"bytes,5,opt,name=network_list,json=networkList,proto3" json:"network_list,omitempty"`
|
||||||
// List of networks for matching.
|
// List of networks for matching.
|
||||||
Networks []net.Network `protobuf:"varint,13,rep,packed,name=networks,proto3,enum=xray.common.net.Network" json:"networks,omitempty"`
|
Networks []net.Network `protobuf:"varint,13,rep,packed,name=networks,proto3,enum=xray.common.net.Network" json:"networks,omitempty"`
|
||||||
// List of CIDRs for source IP address matching.
|
// List of CIDRs for source IP address matching.
|
||||||
//
|
//
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/router/config.proto.
|
||||||
SourceCidr []*CIDR `protobuf:"bytes,6,rep,name=source_cidr,json=sourceCidr,proto3" json:"source_cidr,omitempty"`
|
SourceCidr []*CIDR `protobuf:"bytes,6,rep,name=source_cidr,json=sourceCidr,proto3" json:"source_cidr,omitempty"`
|
||||||
// List of GeoIPs for source IP address matching. If this entry exists, the
|
// List of GeoIPs for source IP address matching. If this entry exists, the
|
||||||
// source_cidr above will have no effect.
|
// source_cidr above will have no effect.
|
||||||
SourceGeoip []*GeoIP `protobuf:"bytes,11,rep,name=source_geoip,json=sourceGeoip,proto3" json:"source_geoip,omitempty"`
|
SourceGeoip []*GeoIP `protobuf:"bytes,11,rep,name=source_geoip,json=sourceGeoip,proto3" json:"source_geoip,omitempty"`
|
||||||
// List of ports for source port matching.
|
// List of ports for source port matching.
|
||||||
SourcePortList *net.PortList `protobuf:"bytes,16,opt,name=source_port_list,json=sourcePortList,proto3" json:"source_port_list,omitempty"`
|
SourcePortList *net.PortList `protobuf:"bytes,16,opt,name=source_port_list,json=sourcePortList,proto3" json:"source_port_list,omitempty"`
|
||||||
UserEmail []string `protobuf:"bytes,7,rep,name=user_email,json=userEmail,proto3" json:"user_email,omitempty"`
|
UserEmail []string `protobuf:"bytes,7,rep,name=user_email,json=userEmail,proto3" json:"user_email,omitempty"`
|
||||||
InboundTag []string `protobuf:"bytes,8,rep,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"`
|
InboundTag []string `protobuf:"bytes,8,rep,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"`
|
||||||
Protocol []string `protobuf:"bytes,9,rep,name=protocol,proto3" json:"protocol,omitempty"`
|
Protocol []string `protobuf:"bytes,9,rep,name=protocol,proto3" json:"protocol,omitempty"`
|
||||||
Attributes string `protobuf:"bytes,15,opt,name=attributes,proto3" json:"attributes,omitempty"`
|
Attributes map[string]string `protobuf:"bytes,15,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
DomainMatcher string `protobuf:"bytes,17,opt,name=domain_matcher,json=domainMatcher,proto3" json:"domain_matcher,omitempty"`
|
DomainMatcher string `protobuf:"bytes,17,opt,name=domain_matcher,json=domainMatcher,proto3" json:"domain_matcher,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RoutingRule) Reset() {
|
func (x *RoutingRule) Reset() {
|
||||||
|
@ -575,6 +577,13 @@ func (x *RoutingRule) GetBalancingTag() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *RoutingRule) GetRuleTag() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.RuleTag
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (x *RoutingRule) GetDomain() []*Domain {
|
func (x *RoutingRule) GetDomain() []*Domain {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Domain
|
return x.Domain
|
||||||
|
@ -582,7 +591,7 @@ func (x *RoutingRule) GetDomain() []*Domain {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/router/config.proto.
|
||||||
func (x *RoutingRule) GetCidr() []*CIDR {
|
func (x *RoutingRule) GetCidr() []*CIDR {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Cidr
|
return x.Cidr
|
||||||
|
@ -597,7 +606,7 @@ func (x *RoutingRule) GetGeoip() []*GeoIP {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/router/config.proto.
|
||||||
func (x *RoutingRule) GetPortRange() *net.PortRange {
|
func (x *RoutingRule) GetPortRange() *net.PortRange {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.PortRange
|
return x.PortRange
|
||||||
|
@ -612,7 +621,7 @@ func (x *RoutingRule) GetPortList() *net.PortList {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/router/config.proto.
|
||||||
func (x *RoutingRule) GetNetworkList() *net.NetworkList {
|
func (x *RoutingRule) GetNetworkList() *net.NetworkList {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.NetworkList
|
return x.NetworkList
|
||||||
|
@ -627,7 +636,7 @@ func (x *RoutingRule) GetNetworks() []net.Network {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Do not use.
|
// Deprecated: Marked as deprecated in app/router/config.proto.
|
||||||
func (x *RoutingRule) GetSourceCidr() []*CIDR {
|
func (x *RoutingRule) GetSourceCidr() []*CIDR {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.SourceCidr
|
return x.SourceCidr
|
||||||
|
@ -670,11 +679,11 @@ func (x *RoutingRule) GetProtocol() []string {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RoutingRule) GetAttributes() string {
|
func (x *RoutingRule) GetAttributes() map[string]string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Attributes
|
return x.Attributes
|
||||||
}
|
}
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RoutingRule) GetDomainMatcher() string {
|
func (x *RoutingRule) GetDomainMatcher() string {
|
||||||
|
@ -707,9 +716,11 @@ type BalancingRule struct {
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
||||||
OutboundSelector []string `protobuf:"bytes,2,rep,name=outbound_selector,json=outboundSelector,proto3" json:"outbound_selector,omitempty"`
|
OutboundSelector []string `protobuf:"bytes,2,rep,name=outbound_selector,json=outboundSelector,proto3" json:"outbound_selector,omitempty"`
|
||||||
Strategy string `protobuf:"bytes,3,opt,name=strategy,proto3" json:"strategy,omitempty"`
|
Strategy string `protobuf:"bytes,3,opt,name=strategy,proto3" json:"strategy,omitempty"`
|
||||||
|
StrategySettings *serial.TypedMessage `protobuf:"bytes,4,opt,name=strategy_settings,json=strategySettings,proto3" json:"strategy_settings,omitempty"`
|
||||||
|
FallbackTag string `protobuf:"bytes,5,opt,name=fallback_tag,json=fallbackTag,proto3" json:"fallback_tag,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *BalancingRule) Reset() {
|
func (x *BalancingRule) Reset() {
|
||||||
|
@ -765,6 +776,167 @@ func (x *BalancingRule) GetStrategy() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *BalancingRule) GetStrategySettings() *serial.TypedMessage {
|
||||||
|
if x != nil {
|
||||||
|
return x.StrategySettings
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *BalancingRule) GetFallbackTag() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.FallbackTag
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type StrategyWeight struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Regexp bool `protobuf:"varint,1,opt,name=regexp,proto3" json:"regexp,omitempty"`
|
||||||
|
Match string `protobuf:"bytes,2,opt,name=match,proto3" json:"match,omitempty"`
|
||||||
|
Value float32 `protobuf:"fixed32,3,opt,name=value,proto3" json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StrategyWeight) Reset() {
|
||||||
|
*x = StrategyWeight{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_app_router_config_proto_msgTypes[8]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StrategyWeight) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*StrategyWeight) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *StrategyWeight) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_app_router_config_proto_msgTypes[8]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use StrategyWeight.ProtoReflect.Descriptor instead.
|
||||||
|
func (*StrategyWeight) Descriptor() ([]byte, []int) {
|
||||||
|
return file_app_router_config_proto_rawDescGZIP(), []int{8}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StrategyWeight) GetRegexp() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Regexp
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StrategyWeight) GetMatch() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Match
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StrategyWeight) GetValue() float32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Value
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type StrategyLeastLoadConfig struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// weight settings
|
||||||
|
Costs []*StrategyWeight `protobuf:"bytes,2,rep,name=costs,proto3" json:"costs,omitempty"`
|
||||||
|
// RTT baselines for selecting, int64 values of time.Duration
|
||||||
|
Baselines []int64 `protobuf:"varint,3,rep,packed,name=baselines,proto3" json:"baselines,omitempty"`
|
||||||
|
// expected nodes count to select
|
||||||
|
Expected int32 `protobuf:"varint,4,opt,name=expected,proto3" json:"expected,omitempty"`
|
||||||
|
// max acceptable rtt, filter away high delay nodes. default 0
|
||||||
|
MaxRTT int64 `protobuf:"varint,5,opt,name=maxRTT,proto3" json:"maxRTT,omitempty"`
|
||||||
|
// acceptable failure rate
|
||||||
|
Tolerance float32 `protobuf:"fixed32,6,opt,name=tolerance,proto3" json:"tolerance,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StrategyLeastLoadConfig) Reset() {
|
||||||
|
*x = StrategyLeastLoadConfig{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_app_router_config_proto_msgTypes[9]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StrategyLeastLoadConfig) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*StrategyLeastLoadConfig) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *StrategyLeastLoadConfig) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_app_router_config_proto_msgTypes[9]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use StrategyLeastLoadConfig.ProtoReflect.Descriptor instead.
|
||||||
|
func (*StrategyLeastLoadConfig) Descriptor() ([]byte, []int) {
|
||||||
|
return file_app_router_config_proto_rawDescGZIP(), []int{9}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StrategyLeastLoadConfig) GetCosts() []*StrategyWeight {
|
||||||
|
if x != nil {
|
||||||
|
return x.Costs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StrategyLeastLoadConfig) GetBaselines() []int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Baselines
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StrategyLeastLoadConfig) GetExpected() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Expected
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StrategyLeastLoadConfig) GetMaxRTT() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.MaxRTT
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *StrategyLeastLoadConfig) GetTolerance() float32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Tolerance
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@ -778,7 +950,7 @@ type Config struct {
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
*x = Config{}
|
*x = Config{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_app_router_config_proto_msgTypes[8]
|
mi := &file_app_router_config_proto_msgTypes[10]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -791,7 +963,7 @@ func (x *Config) String() string {
|
||||||
func (*Config) ProtoMessage() {}
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_app_router_config_proto_msgTypes[8]
|
mi := &file_app_router_config_proto_msgTypes[10]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -804,7 +976,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
func (*Config) Descriptor() ([]byte, []int) {
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
return file_app_router_config_proto_rawDescGZIP(), []int{8}
|
return file_app_router_config_proto_rawDescGZIP(), []int{10}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetDomainStrategy() Config_DomainStrategy {
|
func (x *Config) GetDomainStrategy() Config_DomainStrategy {
|
||||||
|
@ -844,7 +1016,7 @@ type Domain_Attribute struct {
|
||||||
func (x *Domain_Attribute) Reset() {
|
func (x *Domain_Attribute) Reset() {
|
||||||
*x = Domain_Attribute{}
|
*x = Domain_Attribute{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_app_router_config_proto_msgTypes[9]
|
mi := &file_app_router_config_proto_msgTypes[11]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -857,7 +1029,7 @@ func (x *Domain_Attribute) String() string {
|
||||||
func (*Domain_Attribute) ProtoMessage() {}
|
func (*Domain_Attribute) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Domain_Attribute) ProtoReflect() protoreflect.Message {
|
func (x *Domain_Attribute) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_app_router_config_proto_msgTypes[9]
|
mi := &file_app_router_config_proto_msgTypes[11]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -922,136 +1094,171 @@ var File_app_router_config_proto protoreflect.FileDescriptor
|
||||||
var file_app_router_config_proto_rawDesc = []byte{
|
var file_app_router_config_proto_rawDesc = []byte{
|
||||||
0x0a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e,
|
0x0a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e,
|
||||||
0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x1a, 0x15, 0x63, 0x6f, 0x6d, 0x6d,
|
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d,
|
||||||
0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f,
|
||||||
0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65,
|
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x63,
|
||||||
0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb3, 0x02, 0x0a, 0x06,
|
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x70,
|
||||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x30, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01,
|
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74,
|
||||||
0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb3,
|
||||||
0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x79,
|
0x02, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x30, 0x0a, 0x04, 0x74, 0x79, 0x70,
|
||||||
0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
|
||||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3f,
|
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28,
|
0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76,
|
||||||
0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75,
|
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||||
0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69,
|
0x65, 0x12, 0x3f, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03,
|
||||||
0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x1a,
|
0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
||||||
0x6c, 0x0a, 0x09, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03,
|
0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x41, 0x74,
|
||||||
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1f,
|
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
|
||||||
0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
|
0x74, 0x65, 0x1a, 0x6c, 0x0a, 0x09, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12,
|
||||||
0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
|
0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
|
||||||
0x1d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01,
|
0x79, 0x12, 0x1f, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
|
||||||
0x28, 0x03, 0x48, 0x00, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0d,
|
0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c,
|
||||||
0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x32, 0x0a,
|
0x75, 0x65, 0x12, 0x1d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
|
||||||
0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x10, 0x00,
|
0x03, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75,
|
||||||
0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x44,
|
0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10,
|
0x22, 0x32, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x6c, 0x61, 0x69,
|
||||||
0x03, 0x22, 0x2e, 0x0a, 0x04, 0x43, 0x49, 0x44, 0x52, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18,
|
0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x01, 0x12, 0x0a,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65,
|
0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75,
|
||||||
0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69,
|
0x6c, 0x6c, 0x10, 0x03, 0x22, 0x2e, 0x0a, 0x04, 0x43, 0x49, 0x44, 0x52, 0x12, 0x0e, 0x0a, 0x02,
|
||||||
0x78, 0x22, 0x7a, 0x0a, 0x05, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f,
|
0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x16, 0x0a, 0x06,
|
||||||
0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72,
|
||||||
0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x29, 0x0a,
|
0x65, 0x66, 0x69, 0x78, 0x22, 0x7a, 0x0a, 0x05, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x12, 0x21, 0x0a,
|
||||||
0x04, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x78, 0x72,
|
0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20,
|
||||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x49,
|
0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65,
|
||||||
0x44, 0x52, 0x52, 0x04, 0x63, 0x69, 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x76, 0x65,
|
0x12, 0x29, 0x0a, 0x04, 0x63, 0x69, 0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15,
|
||||||
0x72, 0x73, 0x65, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
|
|
||||||
0x0c, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x22, 0x39, 0x0a,
|
|
||||||
0x09, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x05, 0x65, 0x6e,
|
|
||||||
0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
|
||||||
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49,
|
|
||||||
0x50, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x5d, 0x0a, 0x07, 0x47, 0x65, 0x6f, 0x53,
|
|
||||||
0x69, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63,
|
|
||||||
0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
|
||||||
0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
|
||||||
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
|
||||||
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52,
|
|
||||||
0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x3d, 0x0a, 0x0b, 0x47, 0x65, 0x6f, 0x53, 0x69,
|
|
||||||
0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18,
|
|
||||||
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
|
||||||
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x52,
|
|
||||||
0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xb5, 0x06, 0x0a, 0x0b, 0x52, 0x6f, 0x75, 0x74, 0x69,
|
|
||||||
0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20,
|
|
||||||
0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x25, 0x0a, 0x0d, 0x62, 0x61,
|
|
||||||
0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28,
|
|
||||||
0x09, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x54, 0x61,
|
|
||||||
0x67, 0x12, 0x2f, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28,
|
|
||||||
0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75,
|
|
||||||
0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61,
|
|
||||||
0x69, 0x6e, 0x12, 0x2d, 0x0a, 0x04, 0x63, 0x69, 0x64, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
|
|
||||||
0x32, 0x15, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
|
||||||
0x65, 0x72, 0x2e, 0x43, 0x49, 0x44, 0x52, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, 0x63, 0x69, 0x64,
|
|
||||||
0x72, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b,
|
|
||||||
0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
|
||||||
0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x12,
|
|
||||||
0x3d, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x04, 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, 0x42,
|
|
||||||
0x02, 0x18, 0x01, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x36,
|
|
||||||
0x0a, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28,
|
|
||||||
0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
|
|
||||||
0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x08, 0x70, 0x6f,
|
|
||||||
0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72,
|
|
||||||
0x6b, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78,
|
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e,
|
|
||||||
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b,
|
|
||||||
0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x08, 0x6e,
|
|
||||||
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x0d, 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, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
|
|
||||||
0x73, 0x12, 0x3a, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x69, 0x64, 0x72,
|
|
||||||
0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
|
|
||||||
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x49, 0x44, 0x52, 0x42, 0x02, 0x18,
|
|
||||||
0x01, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x69, 0x64, 0x72, 0x12, 0x39, 0x0a,
|
|
||||||
0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0b, 0x20,
|
|
||||||
0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
|
|
||||||
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0b, 0x73, 0x6f, 0x75,
|
|
||||||
0x72, 0x63, 0x65, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x43, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72,
|
|
||||||
0x63, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x10, 0x20, 0x01,
|
|
||||||
0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
|
||||||
0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0e, 0x73,
|
|
||||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1d, 0x0a,
|
|
||||||
0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x03, 0x28,
|
|
||||||
0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1f, 0x0a, 0x0b,
|
|
||||||
0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x08, 0x20, 0x03, 0x28,
|
|
||||||
0x09, 0x52, 0x0a, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x1a, 0x0a,
|
|
||||||
0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52,
|
|
||||||
0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x74, 0x74,
|
|
||||||
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61,
|
|
||||||
0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x6f, 0x6d,
|
|
||||||
0x61, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28,
|
|
||||||
0x09, 0x52, 0x0d, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72,
|
|
||||||
0x42, 0x0c, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x22, 0x6a,
|
|
||||||
0x0a, 0x0d, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12,
|
|
||||||
0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61,
|
|
||||||
0x67, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x65,
|
|
||||||
0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x75,
|
|
||||||
0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a,
|
|
||||||
0x0a, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
|
||||||
0x52, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0x9b, 0x02, 0x0a, 0x06, 0x43,
|
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f,
|
|
||||||
0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26,
|
|
||||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72,
|
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72,
|
||||||
0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74,
|
0x2e, 0x43, 0x49, 0x44, 0x52, 0x52, 0x04, 0x63, 0x69, 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x72,
|
||||||
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74,
|
0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01,
|
||||||
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x02,
|
0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x4d, 0x61, 0x74, 0x63, 0x68,
|
||||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
0x22, 0x39, 0x0a, 0x09, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2c, 0x0a,
|
||||||
0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75,
|
0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78,
|
||||||
0x6c, 0x65, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x62, 0x61, 0x6c, 0x61,
|
0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47,
|
||||||
0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
|
0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x5d, 0x0a, 0x07, 0x47,
|
||||||
0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
|
0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72,
|
||||||
0x65, 0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65,
|
0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f,
|
||||||
0x52, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x22,
|
0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x64, 0x6f, 0x6d,
|
||||||
0x47, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67,
|
0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||||
0x79, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x55,
|
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61,
|
||||||
0x73, 0x65, 0x49, 0x70, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x70, 0x49, 0x66, 0x4e, 0x6f,
|
0x69, 0x6e, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x3d, 0x0a, 0x0b, 0x47, 0x65,
|
||||||
0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x70, 0x4f, 0x6e,
|
0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x65, 0x6e, 0x74,
|
||||||
0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e,
|
0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x50,
|
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x53, 0x69,
|
||||||
0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74,
|
0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x22, 0xbd, 0x07, 0x0a, 0x0b, 0x52, 0x6f,
|
||||||
0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70,
|
0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x74, 0x61, 0x67,
|
||||||
0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x25, 0x0a,
|
||||||
0x70, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x0c,
|
||||||
0x33,
|
0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e,
|
||||||
|
0x67, 0x54, 0x61, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x61, 0x67,
|
||||||
|
0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x12,
|
||||||
|
0x2f, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||||
|
0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
|
||||||
|
0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
|
0x12, 0x2d, 0x0a, 0x04, 0x63, 0x69, 0x64, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15,
|
||||||
|
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72,
|
||||||
|
0x2e, 0x43, 0x49, 0x44, 0x52, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, 0x63, 0x69, 0x64, 0x72, 0x12,
|
||||||
|
0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16,
|
||||||
|
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72,
|
||||||
|
0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x3d, 0x0a,
|
||||||
|
0x0a, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x04, 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, 0x42, 0x02, 0x18,
|
||||||
|
0x01, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x36, 0x0a, 0x09,
|
||||||
|
0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
|
0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65,
|
||||||
|
0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x08, 0x70, 0x6f, 0x72, 0x74,
|
||||||
|
0x4c, 0x69, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f,
|
||||||
|
0x6c, 0x69, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61,
|
||||||
|
0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74,
|
||||||
|
0x77, 0x6f, 0x72, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x6e, 0x65,
|
||||||
|
0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x08, 0x6e, 0x65, 0x74,
|
||||||
|
0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x0d, 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, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12,
|
||||||
|
0x3a, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x06,
|
||||||
|
0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
||||||
|
0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x49, 0x44, 0x52, 0x42, 0x02, 0x18, 0x01, 0x52,
|
||||||
|
0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x69, 0x64, 0x72, 0x12, 0x39, 0x0a, 0x0c, 0x73,
|
||||||
|
0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0b, 0x20, 0x03, 0x28,
|
||||||
|
0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75,
|
||||||
|
0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||||
|
0x65, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x43, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||||
|
0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b,
|
||||||
|
0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e,
|
||||||
|
0x65, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0e, 0x73, 0x6f, 0x75,
|
||||||
|
0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75,
|
||||||
|
0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||||
|
0x09, 0x75, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e,
|
||||||
|
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||||
|
0x0a, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x4c, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69,
|
||||||
|
0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72,
|
||||||
|
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x6f,
|
||||||
|
0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62,
|
||||||
|
0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69,
|
||||||
|
0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f,
|
||||||
|
0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64,
|
||||||
|
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x1a, 0x3d, 0x0a, 0x0f,
|
||||||
|
0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
|
||||||
|
0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
|
||||||
|
0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||||
|
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x74,
|
||||||
|
0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x22, 0xdc, 0x01, 0x0a, 0x0d, 0x42, 0x61,
|
||||||
|
0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74,
|
||||||
|
0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x2b, 0x0a,
|
||||||
|
0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74,
|
||||||
|
0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75,
|
||||||
|
0x6e, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x74,
|
||||||
|
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74,
|
||||||
|
0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x4d, 0x0a, 0x11, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65,
|
||||||
|
0x67, 0x79, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 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, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x53, 0x65, 0x74,
|
||||||
|
0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63,
|
||||||
|
0x6b, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x61, 0x6c,
|
||||||
|
0x6c, 0x62, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x67, 0x22, 0x54, 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x61,
|
||||||
|
0x74, 0x65, 0x67, 0x79, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65,
|
||||||
|
0x67, 0x65, 0x78, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x67, 0x65,
|
||||||
|
0x78, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
|
0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||||
|
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc0,
|
||||||
|
0x01, 0x0a, 0x17, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x4c, 0x65, 0x61, 0x73, 0x74,
|
||||||
|
0x4c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x05, 0x63, 0x6f,
|
||||||
|
0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||||
|
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x72, 0x61,
|
||||||
|
0x74, 0x65, 0x67, 0x79, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x52, 0x05, 0x63, 0x6f, 0x73, 0x74,
|
||||||
|
0x73, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x03,
|
||||||
|
0x20, 0x03, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x12,
|
||||||
|
0x1a, 0x0a, 0x08, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||||
|
0x05, 0x52, 0x08, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6d,
|
||||||
|
0x61, 0x78, 0x52, 0x54, 0x54, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6d, 0x61, 0x78,
|
||||||
|
0x52, 0x54, 0x54, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65,
|
||||||
|
0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63,
|
||||||
|
0x65, 0x22, 0x9b, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x0f,
|
||||||
|
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18,
|
||||||
|
0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||||
|
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44,
|
||||||
|
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64,
|
||||||
|
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x30, 0x0a,
|
||||||
|
0x04, 0x72, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72,
|
||||||
|
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x52, 0x6f,
|
||||||
|
0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12,
|
||||||
|
0x45, 0x0a, 0x0e, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6c,
|
||||||
|
0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
|
||||||
|
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
|
||||||
|
0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69,
|
||||||
|
0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x22, 0x47, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
|
0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, 0x73,
|
||||||
|
0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x49, 0x70, 0x10, 0x01, 0x12, 0x10, 0x0a,
|
||||||
|
0x0c, 0x49, 0x70, 0x49, 0x66, 0x4e, 0x6f, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x10, 0x02, 0x12,
|
||||||
|
0x0e, 0x0a, 0x0a, 0x49, 0x70, 0x4f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x10, 0x03, 0x42,
|
||||||
|
0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
|
||||||
|
0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x24, 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, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, 0x02,
|
||||||
|
0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72,
|
||||||
|
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -1067,28 +1274,32 @@ func file_app_router_config_proto_rawDescGZIP() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_app_router_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
var file_app_router_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||||
var file_app_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
var file_app_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
|
||||||
var file_app_router_config_proto_goTypes = []interface{}{
|
var file_app_router_config_proto_goTypes = []interface{}{
|
||||||
(Domain_Type)(0), // 0: xray.app.router.Domain.Type
|
(Domain_Type)(0), // 0: xray.app.router.Domain.Type
|
||||||
(Config_DomainStrategy)(0), // 1: xray.app.router.Config.DomainStrategy
|
(Config_DomainStrategy)(0), // 1: xray.app.router.Config.DomainStrategy
|
||||||
(*Domain)(nil), // 2: xray.app.router.Domain
|
(*Domain)(nil), // 2: xray.app.router.Domain
|
||||||
(*CIDR)(nil), // 3: xray.app.router.CIDR
|
(*CIDR)(nil), // 3: xray.app.router.CIDR
|
||||||
(*GeoIP)(nil), // 4: xray.app.router.GeoIP
|
(*GeoIP)(nil), // 4: xray.app.router.GeoIP
|
||||||
(*GeoIPList)(nil), // 5: xray.app.router.GeoIPList
|
(*GeoIPList)(nil), // 5: xray.app.router.GeoIPList
|
||||||
(*GeoSite)(nil), // 6: xray.app.router.GeoSite
|
(*GeoSite)(nil), // 6: xray.app.router.GeoSite
|
||||||
(*GeoSiteList)(nil), // 7: xray.app.router.GeoSiteList
|
(*GeoSiteList)(nil), // 7: xray.app.router.GeoSiteList
|
||||||
(*RoutingRule)(nil), // 8: xray.app.router.RoutingRule
|
(*RoutingRule)(nil), // 8: xray.app.router.RoutingRule
|
||||||
(*BalancingRule)(nil), // 9: xray.app.router.BalancingRule
|
(*BalancingRule)(nil), // 9: xray.app.router.BalancingRule
|
||||||
(*Config)(nil), // 10: xray.app.router.Config
|
(*StrategyWeight)(nil), // 10: xray.app.router.StrategyWeight
|
||||||
(*Domain_Attribute)(nil), // 11: xray.app.router.Domain.Attribute
|
(*StrategyLeastLoadConfig)(nil), // 11: xray.app.router.StrategyLeastLoadConfig
|
||||||
(*net.PortRange)(nil), // 12: xray.common.net.PortRange
|
(*Config)(nil), // 12: xray.app.router.Config
|
||||||
(*net.PortList)(nil), // 13: xray.common.net.PortList
|
(*Domain_Attribute)(nil), // 13: xray.app.router.Domain.Attribute
|
||||||
(*net.NetworkList)(nil), // 14: xray.common.net.NetworkList
|
nil, // 14: xray.app.router.RoutingRule.AttributesEntry
|
||||||
(net.Network)(0), // 15: xray.common.net.Network
|
(*net.PortRange)(nil), // 15: xray.common.net.PortRange
|
||||||
|
(*net.PortList)(nil), // 16: xray.common.net.PortList
|
||||||
|
(*net.NetworkList)(nil), // 17: xray.common.net.NetworkList
|
||||||
|
(net.Network)(0), // 18: xray.common.net.Network
|
||||||
|
(*serial.TypedMessage)(nil), // 19: xray.common.serial.TypedMessage
|
||||||
}
|
}
|
||||||
var file_app_router_config_proto_depIdxs = []int32{
|
var file_app_router_config_proto_depIdxs = []int32{
|
||||||
0, // 0: xray.app.router.Domain.type:type_name -> xray.app.router.Domain.Type
|
0, // 0: xray.app.router.Domain.type:type_name -> xray.app.router.Domain.Type
|
||||||
11, // 1: xray.app.router.Domain.attribute:type_name -> xray.app.router.Domain.Attribute
|
13, // 1: xray.app.router.Domain.attribute:type_name -> xray.app.router.Domain.Attribute
|
||||||
3, // 2: xray.app.router.GeoIP.cidr:type_name -> xray.app.router.CIDR
|
3, // 2: xray.app.router.GeoIP.cidr:type_name -> xray.app.router.CIDR
|
||||||
4, // 3: xray.app.router.GeoIPList.entry:type_name -> xray.app.router.GeoIP
|
4, // 3: xray.app.router.GeoIPList.entry:type_name -> xray.app.router.GeoIP
|
||||||
2, // 4: xray.app.router.GeoSite.domain:type_name -> xray.app.router.Domain
|
2, // 4: xray.app.router.GeoSite.domain:type_name -> xray.app.router.Domain
|
||||||
|
@ -1096,21 +1307,24 @@ var file_app_router_config_proto_depIdxs = []int32{
|
||||||
2, // 6: xray.app.router.RoutingRule.domain:type_name -> xray.app.router.Domain
|
2, // 6: xray.app.router.RoutingRule.domain:type_name -> xray.app.router.Domain
|
||||||
3, // 7: xray.app.router.RoutingRule.cidr:type_name -> xray.app.router.CIDR
|
3, // 7: xray.app.router.RoutingRule.cidr:type_name -> xray.app.router.CIDR
|
||||||
4, // 8: xray.app.router.RoutingRule.geoip:type_name -> xray.app.router.GeoIP
|
4, // 8: xray.app.router.RoutingRule.geoip:type_name -> xray.app.router.GeoIP
|
||||||
12, // 9: xray.app.router.RoutingRule.port_range:type_name -> xray.common.net.PortRange
|
15, // 9: xray.app.router.RoutingRule.port_range:type_name -> xray.common.net.PortRange
|
||||||
13, // 10: xray.app.router.RoutingRule.port_list:type_name -> xray.common.net.PortList
|
16, // 10: xray.app.router.RoutingRule.port_list:type_name -> xray.common.net.PortList
|
||||||
14, // 11: xray.app.router.RoutingRule.network_list:type_name -> xray.common.net.NetworkList
|
17, // 11: xray.app.router.RoutingRule.network_list:type_name -> xray.common.net.NetworkList
|
||||||
15, // 12: xray.app.router.RoutingRule.networks:type_name -> xray.common.net.Network
|
18, // 12: xray.app.router.RoutingRule.networks:type_name -> xray.common.net.Network
|
||||||
3, // 13: xray.app.router.RoutingRule.source_cidr:type_name -> xray.app.router.CIDR
|
3, // 13: xray.app.router.RoutingRule.source_cidr:type_name -> xray.app.router.CIDR
|
||||||
4, // 14: xray.app.router.RoutingRule.source_geoip:type_name -> xray.app.router.GeoIP
|
4, // 14: xray.app.router.RoutingRule.source_geoip:type_name -> xray.app.router.GeoIP
|
||||||
13, // 15: xray.app.router.RoutingRule.source_port_list:type_name -> xray.common.net.PortList
|
16, // 15: xray.app.router.RoutingRule.source_port_list:type_name -> xray.common.net.PortList
|
||||||
1, // 16: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy
|
14, // 16: xray.app.router.RoutingRule.attributes:type_name -> xray.app.router.RoutingRule.AttributesEntry
|
||||||
8, // 17: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule
|
19, // 17: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage
|
||||||
9, // 18: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule
|
10, // 18: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight
|
||||||
19, // [19:19] is the sub-list for method output_type
|
1, // 19: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy
|
||||||
19, // [19:19] is the sub-list for method input_type
|
8, // 20: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule
|
||||||
19, // [19:19] is the sub-list for extension type_name
|
9, // 21: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule
|
||||||
19, // [19:19] is the sub-list for extension extendee
|
22, // [22:22] is the sub-list for method output_type
|
||||||
0, // [0:19] is the sub-list for field type_name
|
22, // [22:22] is the sub-list for method input_type
|
||||||
|
22, // [22:22] is the sub-list for extension type_name
|
||||||
|
22, // [22:22] is the sub-list for extension extendee
|
||||||
|
0, // [0:22] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_app_router_config_proto_init() }
|
func init() { file_app_router_config_proto_init() }
|
||||||
|
@ -1216,7 +1430,7 @@ func file_app_router_config_proto_init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_app_router_config_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
file_app_router_config_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Config); i {
|
switch v := v.(*StrategyWeight); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -1228,6 +1442,30 @@ func file_app_router_config_proto_init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_app_router_config_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
file_app_router_config_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*StrategyLeastLoadConfig); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_app_router_config_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Config); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_app_router_config_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Domain_Attribute); i {
|
switch v := v.(*Domain_Attribute); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -1244,7 +1482,7 @@ func file_app_router_config_proto_init() {
|
||||||
(*RoutingRule_Tag)(nil),
|
(*RoutingRule_Tag)(nil),
|
||||||
(*RoutingRule_BalancingTag)(nil),
|
(*RoutingRule_BalancingTag)(nil),
|
||||||
}
|
}
|
||||||
file_app_router_config_proto_msgTypes[9].OneofWrappers = []interface{}{
|
file_app_router_config_proto_msgTypes[11].OneofWrappers = []interface{}{
|
||||||
(*Domain_Attribute_BoolValue)(nil),
|
(*Domain_Attribute_BoolValue)(nil),
|
||||||
(*Domain_Attribute_IntValue)(nil),
|
(*Domain_Attribute_IntValue)(nil),
|
||||||
}
|
}
|
||||||
|
@ -1254,7 +1492,7 @@ func file_app_router_config_proto_init() {
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_app_router_config_proto_rawDesc,
|
RawDescriptor: file_app_router_config_proto_rawDesc,
|
||||||
NumEnums: 2,
|
NumEnums: 2,
|
||||||
NumMessages: 10,
|
NumMessages: 13,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,6 +6,7 @@ option go_package = "github.com/xtls/xray-core/app/router";
|
||||||
option java_package = "com.xray.app.router";
|
option java_package = "com.xray.app.router";
|
||||||
option java_multiple_files = true;
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
import "common/serial/typed_message.proto";
|
||||||
import "common/net/port.proto";
|
import "common/net/port.proto";
|
||||||
import "common/net/network.proto";
|
import "common/net/network.proto";
|
||||||
|
|
||||||
|
@ -78,6 +79,7 @@ message RoutingRule {
|
||||||
// Tag of routing balancer.
|
// Tag of routing balancer.
|
||||||
string balancing_tag = 12;
|
string balancing_tag = 12;
|
||||||
}
|
}
|
||||||
|
string rule_tag = 18;
|
||||||
|
|
||||||
// List of domains for target domain matching.
|
// List of domains for target domain matching.
|
||||||
repeated Domain domain = 2;
|
repeated Domain domain = 2;
|
||||||
|
@ -119,7 +121,7 @@ message RoutingRule {
|
||||||
repeated string inbound_tag = 8;
|
repeated string inbound_tag = 8;
|
||||||
repeated string protocol = 9;
|
repeated string protocol = 9;
|
||||||
|
|
||||||
string attributes = 15;
|
map<string, string> attributes = 15;
|
||||||
|
|
||||||
string domain_matcher = 17;
|
string domain_matcher = 17;
|
||||||
}
|
}
|
||||||
|
@ -128,6 +130,27 @@ message BalancingRule {
|
||||||
string tag = 1;
|
string tag = 1;
|
||||||
repeated string outbound_selector = 2;
|
repeated string outbound_selector = 2;
|
||||||
string strategy = 3;
|
string strategy = 3;
|
||||||
|
xray.common.serial.TypedMessage strategy_settings = 4;
|
||||||
|
string fallback_tag = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StrategyWeight {
|
||||||
|
bool regexp = 1;
|
||||||
|
string match = 2;
|
||||||
|
float value =3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StrategyLeastLoadConfig {
|
||||||
|
// weight settings
|
||||||
|
repeated StrategyWeight costs = 2;
|
||||||
|
// RTT baselines for selecting, int64 values of time.Duration
|
||||||
|
repeated int64 baselines = 3;
|
||||||
|
// expected nodes count to select
|
||||||
|
int32 expected = 4;
|
||||||
|
// max acceptable rtt, filter away high delay nodes. default 0
|
||||||
|
int64 maxRTT = 5;
|
||||||
|
// acceptable failure rate
|
||||||
|
float tolerance = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
|
|
|
@ -4,8 +4,10 @@ package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
sync "sync"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/serial"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/features/dns"
|
"github.com/xtls/xray-core/features/dns"
|
||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
|
@ -19,6 +21,11 @@ type Router struct {
|
||||||
rules []*Rule
|
rules []*Rule
|
||||||
balancers map[string]*Balancer
|
balancers map[string]*Balancer
|
||||||
dns dns.Client
|
dns dns.Client
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
ohm outbound.Manager
|
||||||
|
dispatcher routing.Dispatcher
|
||||||
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route is an implementation of routing.Route.
|
// Route is an implementation of routing.Route.
|
||||||
|
@ -29,13 +36,16 @@ type Route struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the Router.
|
// Init initializes the Router.
|
||||||
func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm outbound.Manager) error {
|
func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm outbound.Manager, dispatcher routing.Dispatcher) error {
|
||||||
r.domainStrategy = config.DomainStrategy
|
r.domainStrategy = config.DomainStrategy
|
||||||
r.dns = d
|
r.dns = d
|
||||||
|
r.ctx = ctx
|
||||||
|
r.ohm = ohm
|
||||||
|
r.dispatcher = dispatcher
|
||||||
|
|
||||||
r.balancers = make(map[string]*Balancer, len(config.BalancingRule))
|
r.balancers = make(map[string]*Balancer, len(config.BalancingRule))
|
||||||
for _, rule := range config.BalancingRule {
|
for _, rule := range config.BalancingRule {
|
||||||
balancer, err := rule.Build(ohm)
|
balancer, err := rule.Build(ohm, dispatcher)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -52,6 +62,7 @@ func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm out
|
||||||
rr := &Rule{
|
rr := &Rule{
|
||||||
Condition: cond,
|
Condition: cond,
|
||||||
Tag: rule.GetTag(),
|
Tag: rule.GetTag(),
|
||||||
|
RuleTag: rule.GetRuleTag(),
|
||||||
}
|
}
|
||||||
btag := rule.GetBalancingTag()
|
btag := rule.GetBalancingTag()
|
||||||
if len(btag) > 0 {
|
if len(btag) > 0 {
|
||||||
|
@ -80,6 +91,96 @@ func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
|
||||||
return &Route{Context: ctx, outboundTag: tag}, nil
|
return &Route{Context: ctx, outboundTag: tag}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddRule implements routing.Router.
|
||||||
|
func (r *Router) AddRule(config *serial.TypedMessage, shouldAppend bool) error {
|
||||||
|
|
||||||
|
inst, err := config.GetInstance()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c, ok := inst.(*Config); ok {
|
||||||
|
return r.ReloadRules(c, shouldAppend)
|
||||||
|
}
|
||||||
|
return newError("AddRule: config type error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) ReloadRules(config *Config, shouldAppend bool) error {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
|
if !shouldAppend {
|
||||||
|
r.balancers = make(map[string]*Balancer, len(config.BalancingRule))
|
||||||
|
r.rules = make([]*Rule, 0, len(config.Rule))
|
||||||
|
}
|
||||||
|
for _, rule := range config.BalancingRule {
|
||||||
|
_, found := r.balancers[rule.Tag]
|
||||||
|
if found {
|
||||||
|
return newError("duplicate balancer tag")
|
||||||
|
}
|
||||||
|
balancer, err := rule.Build(r.ohm, r.dispatcher)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
balancer.InjectContext(r.ctx)
|
||||||
|
r.balancers[rule.Tag] = balancer
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rule := range config.Rule {
|
||||||
|
if r.RuleExists(rule.GetRuleTag()) {
|
||||||
|
return newError("duplicate ruleTag ", rule.GetRuleTag())
|
||||||
|
}
|
||||||
|
cond, err := rule.BuildCondition()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rr := &Rule{
|
||||||
|
Condition: cond,
|
||||||
|
Tag: rule.GetTag(),
|
||||||
|
RuleTag: rule.GetRuleTag(),
|
||||||
|
}
|
||||||
|
btag := rule.GetBalancingTag()
|
||||||
|
if len(btag) > 0 {
|
||||||
|
brule, found := r.balancers[btag]
|
||||||
|
if !found {
|
||||||
|
return newError("balancer ", btag, " not found")
|
||||||
|
}
|
||||||
|
rr.Balancer = brule
|
||||||
|
}
|
||||||
|
r.rules = append(r.rules, rr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) RuleExists(tag string) bool {
|
||||||
|
if tag != "" {
|
||||||
|
for _, rule := range r.rules {
|
||||||
|
if rule.RuleTag == tag {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveRule implements routing.Router.
|
||||||
|
func (r *Router) RemoveRule(tag string) error {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
|
newRules := []*Rule{}
|
||||||
|
if tag != "" {
|
||||||
|
for _, rule := range r.rules {
|
||||||
|
if rule.RuleTag != tag {
|
||||||
|
newRules = append(newRules, rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.rules = newRules
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return newError("empty tag name!")
|
||||||
|
|
||||||
|
}
|
||||||
func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {
|
func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {
|
||||||
// SkipDNSResolve is set from DNS module.
|
// SkipDNSResolve is set from DNS module.
|
||||||
// the DOH remote server maybe a domain name,
|
// the DOH remote server maybe a domain name,
|
||||||
|
@ -113,12 +214,12 @@ func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start implements common.Runnable.
|
// Start implements common.Runnable.
|
||||||
func (*Router) Start() error {
|
func (r *Router) Start() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements common.Closable.
|
// Close implements common.Closable.
|
||||||
func (*Router) Close() error {
|
func (r *Router) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,8 +241,8 @@ func (r *Route) GetOutboundTag() string {
|
||||||
func init() {
|
func init() {
|
||||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
r := new(Router)
|
r := new(Router)
|
||||||
if err := core.RequireFeatures(ctx, func(d dns.Client, ohm outbound.Manager) error {
|
if err := core.RequireFeatures(ctx, func(d dns.Client, ohm outbound.Manager, dispatcher routing.Dispatcher) error {
|
||||||
return r.Init(ctx, config.(*Config), d, ohm)
|
return r.Init(ctx, config.(*Config), d, ohm, dispatcher)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,9 +43,11 @@ func TestSimpleRouter(t *testing.T) {
|
||||||
common.Must(r.Init(context.TODO(), config, mockDNS, &mockOutboundManager{
|
common.Must(r.Init(context.TODO(), config, mockDNS, &mockOutboundManager{
|
||||||
Manager: mockOhm,
|
Manager: mockOhm,
|
||||||
HandlerSelector: mockHs,
|
HandlerSelector: mockHs,
|
||||||
}))
|
}, nil))
|
||||||
|
|
||||||
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)})
|
ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
|
Target: net.TCPDestination(net.DomainAddress("example.com"), 80),
|
||||||
|
}})
|
||||||
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if tag := route.GetOutboundTag(); tag != "test" {
|
if tag := route.GetOutboundTag(); tag != "test" {
|
||||||
|
@ -84,9 +86,11 @@ func TestSimpleBalancer(t *testing.T) {
|
||||||
common.Must(r.Init(context.TODO(), config, mockDNS, &mockOutboundManager{
|
common.Must(r.Init(context.TODO(), config, mockDNS, &mockOutboundManager{
|
||||||
Manager: mockOhm,
|
Manager: mockOhm,
|
||||||
HandlerSelector: mockHs,
|
HandlerSelector: mockHs,
|
||||||
}))
|
}, nil))
|
||||||
|
|
||||||
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)})
|
ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
|
Target: net.TCPDestination(net.DomainAddress("example.com"), 80),
|
||||||
|
}})
|
||||||
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if tag := route.GetOutboundTag(); tag != "test" {
|
if tag := route.GetOutboundTag(); tag != "test" {
|
||||||
|
@ -94,6 +98,55 @@ func TestSimpleBalancer(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Do not work right now: need a full client setup
|
||||||
|
|
||||||
|
func TestLeastLoadBalancer(t *testing.T) {
|
||||||
|
config := &Config{
|
||||||
|
Rule: []*RoutingRule{
|
||||||
|
{
|
||||||
|
TargetTag: &RoutingRule_BalancingTag{
|
||||||
|
BalancingTag: "balance",
|
||||||
|
},
|
||||||
|
Networks: []net.Network{net.Network_TCP},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BalancingRule: []*BalancingRule{
|
||||||
|
{
|
||||||
|
Tag: "balance",
|
||||||
|
OutboundSelector: []string{"test-"},
|
||||||
|
Strategy: "leastLoad",
|
||||||
|
StrategySettings: serial.ToTypedMessage(&StrategyLeastLoadConfig{
|
||||||
|
Baselines: nil,
|
||||||
|
Expected: 1,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockCtl := gomock.NewController(t)
|
||||||
|
defer mockCtl.Finish()
|
||||||
|
|
||||||
|
mockDNS := mocks.NewDNSClient(mockCtl)
|
||||||
|
mockOhm := mocks.NewOutboundManager(mockCtl)
|
||||||
|
mockHs := mocks.NewOutboundHandlerSelector(mockCtl)
|
||||||
|
|
||||||
|
mockHs.EXPECT().Select(gomock.Eq([]string{"test-"})).Return([]string{"test1"})
|
||||||
|
|
||||||
|
r := new(Router)
|
||||||
|
common.Must(r.Init(context.TODO(), config, mockDNS, &mockOutboundManager{
|
||||||
|
Manager: mockOhm,
|
||||||
|
HandlerSelector: mockHs,
|
||||||
|
}, nil))
|
||||||
|
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)})
|
||||||
|
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
||||||
|
common.Must(err)
|
||||||
|
if tag := route.GetOutboundTag(); tag != "test1" {
|
||||||
|
t.Error("expect tag 'test1', bug actually ", tag)
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
func TestIPOnDemand(t *testing.T) {
|
func TestIPOnDemand(t *testing.T) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
DomainStrategy: Config_IpOnDemand,
|
DomainStrategy: Config_IpOnDemand,
|
||||||
|
@ -123,9 +176,11 @@ func TestIPOnDemand(t *testing.T) {
|
||||||
}).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
|
}).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
|
||||||
|
|
||||||
r := new(Router)
|
r := new(Router)
|
||||||
common.Must(r.Init(context.TODO(), config, mockDNS, nil))
|
common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil))
|
||||||
|
|
||||||
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)})
|
ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
|
Target: net.TCPDestination(net.DomainAddress("example.com"), 80),
|
||||||
|
}})
|
||||||
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if tag := route.GetOutboundTag(); tag != "test" {
|
if tag := route.GetOutboundTag(); tag != "test" {
|
||||||
|
@ -162,9 +217,11 @@ func TestIPIfNonMatchDomain(t *testing.T) {
|
||||||
}).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
|
}).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
|
||||||
|
|
||||||
r := new(Router)
|
r := new(Router)
|
||||||
common.Must(r.Init(context.TODO(), config, mockDNS, nil))
|
common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil))
|
||||||
|
|
||||||
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)})
|
ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
|
Target: net.TCPDestination(net.DomainAddress("example.com"), 80),
|
||||||
|
}})
|
||||||
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if tag := route.GetOutboundTag(); tag != "test" {
|
if tag := route.GetOutboundTag(); tag != "test" {
|
||||||
|
@ -196,9 +253,11 @@ func TestIPIfNonMatchIP(t *testing.T) {
|
||||||
mockDNS := mocks.NewDNSClient(mockCtl)
|
mockDNS := mocks.NewDNSClient(mockCtl)
|
||||||
|
|
||||||
r := new(Router)
|
r := new(Router)
|
||||||
common.Must(r.Init(context.TODO(), config, mockDNS, nil))
|
common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil))
|
||||||
|
|
||||||
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 80)})
|
ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
|
Target: net.TCPDestination(net.LocalHostIP, 80),
|
||||||
|
}})
|
||||||
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
route, err := r.PickRoute(routing_session.AsRoutingContext(ctx))
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
if tag := route.GetOutboundTag(); tag != "test" {
|
if tag := route.GetOutboundTag(); tag != "test" {
|
||||||
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/app/observatory"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/dice"
|
||||||
|
"github.com/xtls/xray-core/core"
|
||||||
|
"github.com/xtls/xray-core/features/extension"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LeastLoadStrategy represents a least load balancing strategy
|
||||||
|
type LeastLoadStrategy struct {
|
||||||
|
settings *StrategyLeastLoadConfig
|
||||||
|
costs *WeightManager
|
||||||
|
|
||||||
|
observer extension.Observatory
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LeastLoadStrategy) GetPrincipleTarget(strings []string) []string {
|
||||||
|
var ret []string
|
||||||
|
nodes := l.pickOutbounds(strings)
|
||||||
|
for _, v := range nodes {
|
||||||
|
ret = append(ret, v.Tag)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLeastLoadStrategy creates a new LeastLoadStrategy with settings
|
||||||
|
func NewLeastLoadStrategy(settings *StrategyLeastLoadConfig) *LeastLoadStrategy {
|
||||||
|
return &LeastLoadStrategy{
|
||||||
|
settings: settings,
|
||||||
|
costs: NewWeightManager(
|
||||||
|
settings.Costs, 1,
|
||||||
|
func(value, cost float64) float64 {
|
||||||
|
return value * math.Pow(cost, 0.5)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// node is a minimal copy of HealthCheckResult
|
||||||
|
// we don't use HealthCheckResult directly because
|
||||||
|
// it may change by health checker during routing
|
||||||
|
type node struct {
|
||||||
|
Tag string
|
||||||
|
CountAll int
|
||||||
|
CountFail int
|
||||||
|
RTTAverage time.Duration
|
||||||
|
RTTDeviation time.Duration
|
||||||
|
RTTDeviationCost time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LeastLoadStrategy) InjectContext(ctx context.Context) {
|
||||||
|
l.ctx = ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LeastLoadStrategy) PickOutbound(candidates []string) string {
|
||||||
|
selects := s.pickOutbounds(candidates)
|
||||||
|
count := len(selects)
|
||||||
|
if count == 0 {
|
||||||
|
// goes to fallbackTag
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return selects[dice.Roll(count)].Tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LeastLoadStrategy) pickOutbounds(candidates []string) []*node {
|
||||||
|
qualified := s.getNodes(candidates, time.Duration(s.settings.MaxRTT))
|
||||||
|
selects := s.selectLeastLoad(qualified)
|
||||||
|
return selects
|
||||||
|
}
|
||||||
|
|
||||||
|
// selectLeastLoad selects nodes according to Baselines and Expected Count.
|
||||||
|
//
|
||||||
|
// The strategy always improves network response speed, not matter which mode below is configured.
|
||||||
|
// But they can still have different priorities.
|
||||||
|
//
|
||||||
|
// 1. Bandwidth priority: no Baseline + Expected Count > 0.: selects `Expected Count` of nodes.
|
||||||
|
// (one if Expected Count <= 0)
|
||||||
|
//
|
||||||
|
// 2. Bandwidth priority advanced: Baselines + Expected Count > 0.
|
||||||
|
// Select `Expected Count` amount of nodes, and also those near them according to baselines.
|
||||||
|
// In other words, it selects according to different Baselines, until one of them matches
|
||||||
|
// the Expected Count, if no Baseline matches, Expected Count applied.
|
||||||
|
//
|
||||||
|
// 3. Speed priority: Baselines + `Expected Count <= 0`.
|
||||||
|
// go through all baselines until find selects, if not, select none. Used in combination
|
||||||
|
// with 'balancer.fallbackTag', it means: selects qualified nodes or use the fallback.
|
||||||
|
func (s *LeastLoadStrategy) selectLeastLoad(nodes []*node) []*node {
|
||||||
|
if len(nodes) == 0 {
|
||||||
|
newError("least load: no qualified outbound").AtInfo().WriteToLog()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
expected := int(s.settings.Expected)
|
||||||
|
availableCount := len(nodes)
|
||||||
|
if expected > availableCount {
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected <= 0 {
|
||||||
|
expected = 1
|
||||||
|
}
|
||||||
|
if len(s.settings.Baselines) == 0 {
|
||||||
|
return nodes[:expected]
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
// go through all base line until find expected selects
|
||||||
|
for _, b := range s.settings.Baselines {
|
||||||
|
baseline := time.Duration(b)
|
||||||
|
for i := count; i < availableCount; i++ {
|
||||||
|
if nodes[i].RTTDeviationCost >= baseline {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
count = i + 1
|
||||||
|
}
|
||||||
|
// don't continue if find expected selects
|
||||||
|
if count >= expected {
|
||||||
|
newError("applied baseline: ", baseline).AtDebug().WriteToLog()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.settings.Expected > 0 && count < expected {
|
||||||
|
count = expected
|
||||||
|
}
|
||||||
|
return nodes[:count]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LeastLoadStrategy) getNodes(candidates []string, maxRTT time.Duration) []*node {
|
||||||
|
if s.observer == nil {
|
||||||
|
common.Must(core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error {
|
||||||
|
s.observer = observatory
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
observeResult, err := s.observer.GetObservation(s.ctx)
|
||||||
|
if err != nil {
|
||||||
|
newError("cannot get observation").Base(err).WriteToLog()
|
||||||
|
return make([]*node, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
results := observeResult.(*observatory.ObservationResult)
|
||||||
|
|
||||||
|
outboundlist := outboundList(candidates)
|
||||||
|
|
||||||
|
var ret []*node
|
||||||
|
|
||||||
|
for _, v := range results.Status {
|
||||||
|
if v.Alive && (v.Delay < maxRTT.Milliseconds() || maxRTT == 0) && outboundlist.contains(v.OutboundTag) {
|
||||||
|
record := &node{
|
||||||
|
Tag: v.OutboundTag,
|
||||||
|
CountAll: 1,
|
||||||
|
CountFail: 1,
|
||||||
|
RTTAverage: time.Duration(v.Delay) * time.Millisecond,
|
||||||
|
RTTDeviation: time.Duration(v.Delay) * time.Millisecond,
|
||||||
|
RTTDeviationCost: time.Duration(s.costs.Apply(v.OutboundTag, float64(time.Duration(v.Delay)*time.Millisecond))),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.HealthPing != nil {
|
||||||
|
record.RTTAverage = time.Duration(v.HealthPing.Average)
|
||||||
|
record.RTTDeviation = time.Duration(v.HealthPing.Deviation)
|
||||||
|
record.RTTDeviationCost = time.Duration(s.costs.Apply(v.OutboundTag, float64(v.HealthPing.Deviation)))
|
||||||
|
record.CountAll = int(v.HealthPing.All)
|
||||||
|
record.CountFail = int(v.HealthPing.Fail)
|
||||||
|
|
||||||
|
}
|
||||||
|
ret = append(ret, record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
leastloadSort(ret)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func leastloadSort(nodes []*node) {
|
||||||
|
sort.Slice(nodes, func(i, j int) bool {
|
||||||
|
left := nodes[i]
|
||||||
|
right := nodes[j]
|
||||||
|
if left.RTTDeviationCost != right.RTTDeviationCost {
|
||||||
|
return left.RTTDeviationCost < right.RTTDeviationCost
|
||||||
|
}
|
||||||
|
if left.RTTAverage != right.RTTAverage {
|
||||||
|
return left.RTTAverage < right.RTTAverage
|
||||||
|
}
|
||||||
|
if left.CountFail != right.CountFail {
|
||||||
|
return left.CountFail < right.CountFail
|
||||||
|
}
|
||||||
|
if left.CountAll != right.CountAll {
|
||||||
|
return left.CountAll > right.CountAll
|
||||||
|
}
|
||||||
|
return left.Tag < right.Tag
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Split into multiple package, need to be tested separately
|
||||||
|
|
||||||
|
func TestSelectLeastLoad(t *testing.T) {
|
||||||
|
settings := &StrategyLeastLoadConfig{
|
||||||
|
HealthCheck: &HealthPingConfig{
|
||||||
|
SamplingCount: 10,
|
||||||
|
},
|
||||||
|
Expected: 1,
|
||||||
|
MaxRTT: int64(time.Millisecond * time.Duration(800)),
|
||||||
|
}
|
||||||
|
strategy := NewLeastLoadStrategy(settings)
|
||||||
|
// std 40
|
||||||
|
strategy.PutResult("a", time.Millisecond*time.Duration(60))
|
||||||
|
strategy.PutResult("a", time.Millisecond*time.Duration(140))
|
||||||
|
strategy.PutResult("a", time.Millisecond*time.Duration(60))
|
||||||
|
strategy.PutResult("a", time.Millisecond*time.Duration(140))
|
||||||
|
// std 60
|
||||||
|
strategy.PutResult("b", time.Millisecond*time.Duration(40))
|
||||||
|
strategy.PutResult("b", time.Millisecond*time.Duration(160))
|
||||||
|
strategy.PutResult("b", time.Millisecond*time.Duration(40))
|
||||||
|
strategy.PutResult("b", time.Millisecond*time.Duration(160))
|
||||||
|
// std 0, but >MaxRTT
|
||||||
|
strategy.PutResult("c", time.Millisecond*time.Duration(1000))
|
||||||
|
strategy.PutResult("c", time.Millisecond*time.Duration(1000))
|
||||||
|
strategy.PutResult("c", time.Millisecond*time.Duration(1000))
|
||||||
|
strategy.PutResult("c", time.Millisecond*time.Duration(1000))
|
||||||
|
expected := "a"
|
||||||
|
actual := strategy.SelectAndPick([]string{"a", "b", "c", "untested"})
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("expected: %v, actual: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectLeastLoadWithCost(t *testing.T) {
|
||||||
|
settings := &StrategyLeastLoadConfig{
|
||||||
|
HealthCheck: &HealthPingConfig{
|
||||||
|
SamplingCount: 10,
|
||||||
|
},
|
||||||
|
Costs: []*StrategyWeight{
|
||||||
|
{Match: "a", Value: 9},
|
||||||
|
},
|
||||||
|
Expected: 1,
|
||||||
|
}
|
||||||
|
strategy := NewLeastLoadStrategy(settings, nil)
|
||||||
|
// std 40, std+c 120
|
||||||
|
strategy.PutResult("a", time.Millisecond*time.Duration(60))
|
||||||
|
strategy.PutResult("a", time.Millisecond*time.Duration(140))
|
||||||
|
strategy.PutResult("a", time.Millisecond*time.Duration(60))
|
||||||
|
strategy.PutResult("a", time.Millisecond*time.Duration(140))
|
||||||
|
// std 60
|
||||||
|
strategy.PutResult("b", time.Millisecond*time.Duration(40))
|
||||||
|
strategy.PutResult("b", time.Millisecond*time.Duration(160))
|
||||||
|
strategy.PutResult("b", time.Millisecond*time.Duration(40))
|
||||||
|
strategy.PutResult("b", time.Millisecond*time.Duration(160))
|
||||||
|
expected := "b"
|
||||||
|
actual := strategy.SelectAndPick([]string{"a", "b", "untested"})
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("expected: %v, actual: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
func TestSelectLeastExpected(t *testing.T) {
|
||||||
|
strategy := &LeastLoadStrategy{
|
||||||
|
settings: &StrategyLeastLoadConfig{
|
||||||
|
Baselines: nil,
|
||||||
|
Expected: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodes := []*node{
|
||||||
|
{Tag: "a", RTTDeviationCost: 100},
|
||||||
|
{Tag: "b", RTTDeviationCost: 200},
|
||||||
|
{Tag: "c", RTTDeviationCost: 300},
|
||||||
|
{Tag: "d", RTTDeviationCost: 350},
|
||||||
|
}
|
||||||
|
expected := 3
|
||||||
|
ns := strategy.selectLeastLoad(nodes)
|
||||||
|
if len(ns) != expected {
|
||||||
|
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestSelectLeastExpected2(t *testing.T) {
|
||||||
|
strategy := &LeastLoadStrategy{
|
||||||
|
settings: &StrategyLeastLoadConfig{
|
||||||
|
Baselines: nil,
|
||||||
|
Expected: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodes := []*node{
|
||||||
|
{Tag: "a", RTTDeviationCost: 100},
|
||||||
|
{Tag: "b", RTTDeviationCost: 200},
|
||||||
|
}
|
||||||
|
expected := 2
|
||||||
|
ns := strategy.selectLeastLoad(nodes)
|
||||||
|
if len(ns) != expected {
|
||||||
|
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestSelectLeastExpectedAndBaselines(t *testing.T) {
|
||||||
|
strategy := &LeastLoadStrategy{
|
||||||
|
settings: &StrategyLeastLoadConfig{
|
||||||
|
Baselines: []int64{200, 300, 400},
|
||||||
|
Expected: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodes := []*node{
|
||||||
|
{Tag: "a", RTTDeviationCost: 100},
|
||||||
|
{Tag: "b", RTTDeviationCost: 200},
|
||||||
|
{Tag: "c", RTTDeviationCost: 250},
|
||||||
|
{Tag: "d", RTTDeviationCost: 300},
|
||||||
|
{Tag: "e", RTTDeviationCost: 310},
|
||||||
|
}
|
||||||
|
expected := 3
|
||||||
|
ns := strategy.selectLeastLoad(nodes)
|
||||||
|
if len(ns) != expected {
|
||||||
|
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestSelectLeastExpectedAndBaselines2(t *testing.T) {
|
||||||
|
strategy := &LeastLoadStrategy{
|
||||||
|
settings: &StrategyLeastLoadConfig{
|
||||||
|
Baselines: []int64{200, 300, 400},
|
||||||
|
Expected: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodes := []*node{
|
||||||
|
{Tag: "a", RTTDeviationCost: 500},
|
||||||
|
{Tag: "b", RTTDeviationCost: 600},
|
||||||
|
{Tag: "c", RTTDeviationCost: 700},
|
||||||
|
{Tag: "d", RTTDeviationCost: 800},
|
||||||
|
{Tag: "e", RTTDeviationCost: 900},
|
||||||
|
}
|
||||||
|
expected := 3
|
||||||
|
ns := strategy.selectLeastLoad(nodes)
|
||||||
|
if len(ns) != expected {
|
||||||
|
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestSelectLeastLoadBaselines(t *testing.T) {
|
||||||
|
strategy := &LeastLoadStrategy{
|
||||||
|
settings: &StrategyLeastLoadConfig{
|
||||||
|
Baselines: []int64{200, 400, 600},
|
||||||
|
Expected: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodes := []*node{
|
||||||
|
{Tag: "a", RTTDeviationCost: 100},
|
||||||
|
{Tag: "b", RTTDeviationCost: 200},
|
||||||
|
{Tag: "c", RTTDeviationCost: 300},
|
||||||
|
}
|
||||||
|
expected := 1
|
||||||
|
ns := strategy.selectLeastLoad(nodes)
|
||||||
|
if len(ns) != expected {
|
||||||
|
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestSelectLeastLoadBaselinesNoQualified(t *testing.T) {
|
||||||
|
strategy := &LeastLoadStrategy{
|
||||||
|
settings: &StrategyLeastLoadConfig{
|
||||||
|
Baselines: []int64{200, 400, 600},
|
||||||
|
Expected: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodes := []*node{
|
||||||
|
{Tag: "a", RTTDeviationCost: 800},
|
||||||
|
{Tag: "b", RTTDeviationCost: 1000},
|
||||||
|
}
|
||||||
|
expected := 0
|
||||||
|
ns := strategy.selectLeastLoad(nodes)
|
||||||
|
if len(ns) != expected {
|
||||||
|
t.Errorf("expected: %v, actual: %v", expected, len(ns))
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,10 @@ type LeastPingStrategy struct {
|
||||||
observatory extension.Observatory
|
observatory extension.Observatory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *LeastPingStrategy) GetPrincipleTarget(strings []string) []string {
|
||||||
|
return []string{l.PickOutbound(strings)}
|
||||||
|
}
|
||||||
|
|
||||||
func (l *LeastPingStrategy) InjectContext(ctx context.Context) {
|
func (l *LeastPingStrategy) InjectContext(ctx context.Context) {
|
||||||
l.ctx = ctx
|
l.ctx = ctx
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/app/observatory"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/dice"
|
||||||
|
"github.com/xtls/xray-core/core"
|
||||||
|
"github.com/xtls/xray-core/features/extension"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RandomStrategy represents a random balancing strategy
|
||||||
|
type RandomStrategy struct{
|
||||||
|
FallbackTag string
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
observatory extension.Observatory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RandomStrategy) InjectContext(ctx context.Context) {
|
||||||
|
s.ctx = ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RandomStrategy) GetPrincipleTarget(strings []string) []string {
|
||||||
|
return strings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RandomStrategy) PickOutbound(candidates []string) string {
|
||||||
|
if len(s.FallbackTag) > 0 && s.observatory == nil {
|
||||||
|
common.Must(core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error {
|
||||||
|
s.observatory = observatory
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
if s.observatory != nil {
|
||||||
|
observeReport, err := s.observatory.GetObservation(s.ctx)
|
||||||
|
if err == nil {
|
||||||
|
aliveTags := make([]string, 0)
|
||||||
|
if result, ok := observeReport.(*observatory.ObservationResult); ok {
|
||||||
|
status := result.Status
|
||||||
|
statusMap := make(map[string]*observatory.OutboundStatus)
|
||||||
|
for _, outboundStatus := range status {
|
||||||
|
statusMap[outboundStatus.OutboundTag] = outboundStatus
|
||||||
|
}
|
||||||
|
for _, candidate := range candidates {
|
||||||
|
if outboundStatus, found := statusMap[candidate]; found {
|
||||||
|
if outboundStatus.Alive {
|
||||||
|
aliveTags = append(aliveTags, candidate)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// unfound candidate is considered alive
|
||||||
|
aliveTags = append(aliveTags, candidate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
candidates = aliveTags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count := len(candidates)
|
||||||
|
if count == 0 {
|
||||||
|
// goes to fallbackTag
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return candidates[dice.Roll(count)]
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type weightScaler func(value, weight float64) float64
|
||||||
|
|
||||||
|
var numberFinder = regexp.MustCompile(`\d+(\.\d+)?`)
|
||||||
|
|
||||||
|
// NewWeightManager creates a new WeightManager with settings
|
||||||
|
func NewWeightManager(s []*StrategyWeight, defaultWeight float64, scaler weightScaler) *WeightManager {
|
||||||
|
return &WeightManager{
|
||||||
|
settings: s,
|
||||||
|
cache: make(map[string]float64),
|
||||||
|
scaler: scaler,
|
||||||
|
defaultWeight: defaultWeight,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeightManager manages weights for specific settings
|
||||||
|
type WeightManager struct {
|
||||||
|
settings []*StrategyWeight
|
||||||
|
cache map[string]float64
|
||||||
|
scaler weightScaler
|
||||||
|
defaultWeight float64
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get get the weight of specified tag
|
||||||
|
func (s *WeightManager) Get(tag string) float64 {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
weight, ok := s.cache[tag]
|
||||||
|
if ok {
|
||||||
|
return weight
|
||||||
|
}
|
||||||
|
weight = s.findValue(tag)
|
||||||
|
s.cache[tag] = weight
|
||||||
|
return weight
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply applies weight to the value
|
||||||
|
func (s *WeightManager) Apply(tag string, value float64) float64 {
|
||||||
|
return s.scaler(value, s.Get(tag))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WeightManager) findValue(tag string) float64 {
|
||||||
|
for _, w := range s.settings {
|
||||||
|
matched := s.getMatch(tag, w.Match, w.Regexp)
|
||||||
|
if matched == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if w.Value > 0 {
|
||||||
|
return float64(w.Value)
|
||||||
|
}
|
||||||
|
// auto weight from matched
|
||||||
|
numStr := numberFinder.FindString(matched)
|
||||||
|
if numStr == "" {
|
||||||
|
return s.defaultWeight
|
||||||
|
}
|
||||||
|
weight, err := strconv.ParseFloat(numStr, 64)
|
||||||
|
if err != nil {
|
||||||
|
newError("unexpected error from ParseFloat: ", err).AtError().WriteToLog()
|
||||||
|
return s.defaultWeight
|
||||||
|
}
|
||||||
|
return weight
|
||||||
|
}
|
||||||
|
return s.defaultWeight
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WeightManager) getMatch(tag, find string, isRegexp bool) string {
|
||||||
|
if !isRegexp {
|
||||||
|
idx := strings.Index(tag, find)
|
||||||
|
if idx < 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return find
|
||||||
|
}
|
||||||
|
r, err := regexp.Compile(find)
|
||||||
|
if err != nil {
|
||||||
|
newError("invalid regexp: ", find, "err: ", err).AtError().WriteToLog()
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return r.FindString(tag)
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package router_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/app/router"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWeight(t *testing.T) {
|
||||||
|
manager := router.NewWeightManager(
|
||||||
|
[]*router.StrategyWeight{
|
||||||
|
{
|
||||||
|
Match: "x5",
|
||||||
|
Value: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Match: "x8",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Regexp: true,
|
||||||
|
Match: `\bx0+(\.\d+)?\b`,
|
||||||
|
Value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Regexp: true,
|
||||||
|
Match: `\bx\d+(\.\d+)?\b`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
1, func(v, w float64) float64 {
|
||||||
|
return v * w
|
||||||
|
},
|
||||||
|
)
|
||||||
|
tags := []string{
|
||||||
|
"node name, x5, and more",
|
||||||
|
"node name, x8",
|
||||||
|
"node name, x15",
|
||||||
|
"node name, x0100, and more",
|
||||||
|
"node name, x10.1",
|
||||||
|
"node name, x00.1, and more",
|
||||||
|
}
|
||||||
|
// test weight
|
||||||
|
expected := []float64{100, 8, 15, 100, 10.1, 1}
|
||||||
|
actual := make([]float64, 0)
|
||||||
|
for _, tag := range tags {
|
||||||
|
actual = append(actual, manager.Get(tag))
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", expected, actual)
|
||||||
|
}
|
||||||
|
// test scale
|
||||||
|
expected2 := []float64{1000, 80, 150, 1000, 101, 10}
|
||||||
|
actual2 := make([]float64, 0)
|
||||||
|
for _, tag := range tags {
|
||||||
|
actual2 = append(actual2, manager.Apply(tag, 10))
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(expected2, actual2) {
|
||||||
|
t.Errorf("expected2: %v, actual2: %v", expected2, actual2)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/stats/command/command.proto
|
// source: app/stats/command/command.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// - protoc-gen-go-grpc v1.2.0
|
// - protoc-gen-go-grpc v1.3.0
|
||||||
// - protoc v3.21.12
|
// - protoc v5.27.0
|
||||||
// source: app/stats/command/command.proto
|
// source: app/stats/command/command.proto
|
||||||
|
|
||||||
package command
|
package command
|
||||||
|
@ -18,6 +18,12 @@ import (
|
||||||
// Requires gRPC-Go v1.32.0 or later.
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
const _ = grpc.SupportPackageIsVersion7
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
const (
|
||||||
|
StatsService_GetStats_FullMethodName = "/xray.app.stats.command.StatsService/GetStats"
|
||||||
|
StatsService_QueryStats_FullMethodName = "/xray.app.stats.command.StatsService/QueryStats"
|
||||||
|
StatsService_GetSysStats_FullMethodName = "/xray.app.stats.command.StatsService/GetSysStats"
|
||||||
|
)
|
||||||
|
|
||||||
// StatsServiceClient is the client API for StatsService service.
|
// StatsServiceClient is the client API for StatsService service.
|
||||||
//
|
//
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
@ -37,7 +43,7 @@ func NewStatsServiceClient(cc grpc.ClientConnInterface) StatsServiceClient {
|
||||||
|
|
||||||
func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) {
|
func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) {
|
||||||
out := new(GetStatsResponse)
|
out := new(GetStatsResponse)
|
||||||
err := c.cc.Invoke(ctx, "/xray.app.stats.command.StatsService/GetStats", in, out, opts...)
|
err := c.cc.Invoke(ctx, StatsService_GetStats_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -46,7 +52,7 @@ func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest,
|
||||||
|
|
||||||
func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) {
|
func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) {
|
||||||
out := new(QueryStatsResponse)
|
out := new(QueryStatsResponse)
|
||||||
err := c.cc.Invoke(ctx, "/xray.app.stats.command.StatsService/QueryStats", in, out, opts...)
|
err := c.cc.Invoke(ctx, StatsService_QueryStats_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -55,7 +61,7 @@ func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsReque
|
||||||
|
|
||||||
func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error) {
|
func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error) {
|
||||||
out := new(SysStatsResponse)
|
out := new(SysStatsResponse)
|
||||||
err := c.cc.Invoke(ctx, "/xray.app.stats.command.StatsService/GetSysStats", in, out, opts...)
|
err := c.cc.Invoke(ctx, StatsService_GetSysStats_FullMethodName, in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -108,7 +114,7 @@ func _StatsService_GetStats_Handler(srv interface{}, ctx context.Context, dec fu
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/xray.app.stats.command.StatsService/GetStats",
|
FullMethod: StatsService_GetStats_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(StatsServiceServer).GetStats(ctx, req.(*GetStatsRequest))
|
return srv.(StatsServiceServer).GetStats(ctx, req.(*GetStatsRequest))
|
||||||
|
@ -126,7 +132,7 @@ func _StatsService_QueryStats_Handler(srv interface{}, ctx context.Context, dec
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/xray.app.stats.command.StatsService/QueryStats",
|
FullMethod: StatsService_QueryStats_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(StatsServiceServer).QueryStats(ctx, req.(*QueryStatsRequest))
|
return srv.(StatsServiceServer).QueryStats(ctx, req.(*QueryStatsRequest))
|
||||||
|
@ -144,7 +150,7 @@ func _StatsService_GetSysStats_Handler(srv interface{}, ctx context.Context, dec
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/xray.app.stats.command.StatsService/GetSysStats",
|
FullMethod: StatsService_GetSysStats_FullMethodName,
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(StatsServiceServer).GetSysStats(ctx, req.(*SysStatsRequest))
|
return srv.(StatsServiceServer).GetSysStats(ctx, req.(*SysStatsRequest))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: app/stats/config.proto
|
// source: app/stats/config.proto
|
||||||
|
|
||||||
package stats
|
package stats
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/signal"
|
"github.com/xtls/xray-core/common/signal"
|
||||||
|
"github.com/xtls/xray-core/features/stats"
|
||||||
)
|
)
|
||||||
|
|
||||||
type dataHandler func(MultiBuffer)
|
type dataHandler func(MultiBuffer)
|
||||||
|
@ -40,6 +41,17 @@ func CountSize(sc *SizeCounter) CopyOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddToStatCounter a CopyOption add to stat counter
|
||||||
|
func AddToStatCounter(sc stats.Counter) CopyOption {
|
||||||
|
return func(handler *copyHandler) {
|
||||||
|
handler.onData = append(handler.onData, func(b MultiBuffer) {
|
||||||
|
if sc != nil {
|
||||||
|
sc.Add(int64(b.Len()))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type readError struct {
|
type readError struct {
|
||||||
error
|
error
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package buf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EndpointOverrideReader struct {
|
||||||
|
Reader
|
||||||
|
Dest net.Address
|
||||||
|
OriginalDest net.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *EndpointOverrideReader) ReadMultiBuffer() (MultiBuffer, error) {
|
||||||
|
mb, err := r.Reader.ReadMultiBuffer()
|
||||||
|
if err == nil {
|
||||||
|
for _, b := range mb {
|
||||||
|
if b.UDP != nil && b.UDP.Address == r.OriginalDest {
|
||||||
|
b.UDP.Address = r.Dest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mb, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type EndpointOverrideWriter struct {
|
||||||
|
Writer
|
||||||
|
Dest net.Address
|
||||||
|
OriginalDest net.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *EndpointOverrideWriter) WriteMultiBuffer(mb MultiBuffer) error {
|
||||||
|
for _, b := range mb {
|
||||||
|
if b.UDP != nil && b.UDP.Address == w.Dest {
|
||||||
|
b.UDP.Address = w.OriginalDest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w.Writer.WriteMultiBuffer(mb)
|
||||||
|
}
|
|
@ -147,7 +147,7 @@ var useReadv bool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
const defaultFlagValue = "NOT_DEFINED_AT_ALL"
|
const defaultFlagValue = "NOT_DEFINED_AT_ALL"
|
||||||
value := platform.NewEnvFlag("xray.buf.readv").GetValue(func() string { return defaultFlagValue })
|
value := platform.NewEnvFlag(platform.UseReadV).GetValue(func() string { return defaultFlagValue })
|
||||||
switch value {
|
switch value {
|
||||||
case defaultFlagValue, "auto", "enable":
|
case defaultFlagValue, "auto", "enable":
|
||||||
useReadv = true
|
useReadv = true
|
||||||
|
|
|
@ -178,7 +178,7 @@ func (r *AuthenticationReader) readInternal(soft bool, mb *buf.MultiBuffer) erro
|
||||||
if size <= buf.Size {
|
if size <= buf.Size {
|
||||||
b, err := r.readBuffer(int32(size), int32(padding))
|
b, err := r.readBuffer(int32(size), int32(padding))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
*mb = append(*mb, b)
|
*mb = append(*mb, b)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -4,7 +4,6 @@ package dice // import "github.com/xtls/xray-core/common/dice"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Roll returns a non-negative number between 0 (inclusive) and n (exclusive).
|
// Roll returns a non-negative number between 0 (inclusive) and n (exclusive).
|
||||||
|
@ -46,7 +45,3 @@ func (dd *DeterministicDice) Roll(n int) int {
|
||||||
}
|
}
|
||||||
return dd.Intn(n)
|
return dd.Intn(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
rand.Seed(time.Now().Unix())
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package drain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/dice"
|
"github.com/xtls/xray-core/common/dice"
|
||||||
)
|
)
|
||||||
|
@ -36,7 +35,7 @@ func (d *BehaviorSeedLimitedDrainer) Drain(reader io.Reader) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func drainReadN(reader io.Reader, n int) error {
|
func drainReadN(reader io.Reader, n int) error {
|
||||||
_, err := io.CopyN(ioutil.Discard, reader, int64(n))
|
_, err := io.CopyN(io.Discard, reader, int64(n))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,3 +28,20 @@ func Combine(maybeError ...error) error {
|
||||||
}
|
}
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AllEqual(expected error, actual error) bool {
|
||||||
|
switch errs := actual.(type) {
|
||||||
|
case multiError:
|
||||||
|
if len(errs) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, err := range errs {
|
||||||
|
if err != expected {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return errs == expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.28.1
|
// protoc-gen-go v1.34.1
|
||||||
// protoc v3.21.12
|
// protoc v5.27.0
|
||||||
// source: common/log/log.proto
|
// source: common/log/log.proto
|
||||||
|
|
||||||
package log
|
package log
|
||||||
|
|
|
@ -27,6 +27,11 @@ type generalLogger struct {
|
||||||
done *done.Instance
|
done *done.Instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type serverityLogger struct {
|
||||||
|
inner *generalLogger
|
||||||
|
logLevel Severity
|
||||||
|
}
|
||||||
|
|
||||||
// NewLogger returns a generic log handler that can handle all type of messages.
|
// NewLogger returns a generic log handler that can handle all type of messages.
|
||||||
func NewLogger(logWriterCreator WriterCreator) Handler {
|
func NewLogger(logWriterCreator WriterCreator) Handler {
|
||||||
return &generalLogger{
|
return &generalLogger{
|
||||||
|
@ -37,6 +42,32 @@ func NewLogger(logWriterCreator WriterCreator) Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReplaceWithSeverityLogger(serverity Severity) {
|
||||||
|
w := CreateStdoutLogWriter()
|
||||||
|
g := &generalLogger{
|
||||||
|
creator: w,
|
||||||
|
buffer: make(chan Message, 16),
|
||||||
|
access: semaphore.New(1),
|
||||||
|
done: done.New(),
|
||||||
|
}
|
||||||
|
s := &serverityLogger{
|
||||||
|
inner: g,
|
||||||
|
logLevel: serverity,
|
||||||
|
}
|
||||||
|
RegisterHandler(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *serverityLogger) Handle(msg Message) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case *GeneralMessage:
|
||||||
|
if msg.Severity <= l.logLevel {
|
||||||
|
l.inner.Handle(msg)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
l.inner.Handle(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (l *generalLogger) run() {
|
func (l *generalLogger) run() {
|
||||||
defer l.access.Signal()
|
defer l.access.Signal()
|
||||||
|
|
||||||
|
@ -67,6 +98,7 @@ func (l *generalLogger) run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *generalLogger) Handle(msg Message) {
|
func (l *generalLogger) Handle(msg Message) {
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case l.buffer <- msg:
|
case l.buffer <- msg:
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
"github.com/xtls/xray-core/common/signal/done"
|
"github.com/xtls/xray-core/common/signal/done"
|
||||||
"github.com/xtls/xray-core/common/task"
|
"github.com/xtls/xray-core/common/task"
|
||||||
|
"github.com/xtls/xray-core/common/xudp"
|
||||||
"github.com/xtls/xray-core/proxy"
|
"github.com/xtls/xray-core/proxy"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
@ -147,9 +148,10 @@ func (f *DialingWorkerFactory) Create() (*ClientWorker, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
go func(p proxy.Outbound, d internet.Dialer, c common.Closable) {
|
go func(p proxy.Outbound, d internet.Dialer, c common.Closable) {
|
||||||
ctx := session.ContextWithOutbound(context.Background(), &session.Outbound{
|
outbounds := []*session.Outbound{{
|
||||||
Target: net.TCPDestination(muxCoolAddress, muxCoolPort),
|
Target: net.TCPDestination(muxCoolAddress, muxCoolPort),
|
||||||
})
|
}}
|
||||||
|
ctx := session.ContextWithOutbounds(context.Background(), outbounds)
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
if err := p.Process(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}, d); err != nil {
|
if err := p.Process(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter}, d); err != nil {
|
||||||
|
@ -241,28 +243,27 @@ func writeFirstPayload(reader buf.Reader, writer *Writer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
|
func fetchInput(ctx context.Context, s *Session, output buf.Writer) {
|
||||||
dest := session.OutboundFromContext(ctx).Target
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
ob := outbounds[len(outbounds) - 1]
|
||||||
transferType := protocol.TransferTypeStream
|
transferType := protocol.TransferTypeStream
|
||||||
if dest.Network == net.Network_UDP {
|
if ob.Target.Network == net.Network_UDP {
|
||||||
transferType = protocol.TransferTypePacket
|
transferType = protocol.TransferTypePacket
|
||||||
}
|
}
|
||||||
s.transferType = transferType
|
s.transferType = transferType
|
||||||
writer := NewWriter(s.ID, dest, output, transferType)
|
writer := NewWriter(s.ID, ob.Target, output, transferType, xudp.GetGlobalID(ctx))
|
||||||
defer s.Close()
|
defer s.Close(false)
|
||||||
defer writer.Close()
|
defer writer.Close()
|
||||||
|
|
||||||
newError("dispatching request to ", dest).WriteToLog(session.ExportIDToError(ctx))
|
newError("dispatching request to ", ob.Target).WriteToLog(session.ExportIDToError(ctx))
|
||||||
if err := writeFirstPayload(s.input, writer); err != nil {
|
if err := writeFirstPayload(s.input, writer); err != nil {
|
||||||
newError("failed to write first payload").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
newError("failed to write first payload").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||||
writer.hasError = true
|
writer.hasError = true
|
||||||
common.Interrupt(s.input)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := buf.Copy(s.input, writer); err != nil {
|
if err := buf.Copy(s.input, writer); err != nil {
|
||||||
newError("failed to fetch all input").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
newError("failed to fetch all input").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||||
writer.hasError = true
|
writer.hasError = true
|
||||||
common.Interrupt(s.input)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,15 +336,8 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||||
err := buf.Copy(rr, s.output)
|
err := buf.Copy(rr, s.output)
|
||||||
if err != nil && buf.IsWriteError(err) {
|
if err != nil && buf.IsWriteError(err) {
|
||||||
newError("failed to write to downstream. closing session ", s.ID).Base(err).WriteToLog()
|
newError("failed to write to downstream. closing session ", s.ID).Base(err).WriteToLog()
|
||||||
|
s.Close(false)
|
||||||
// Notify remote peer to close this session.
|
return buf.Copy(rr, buf.Discard)
|
||||||
closingWriter := NewResponseWriter(meta.SessionID, m.link.Writer, protocol.TransferTypeStream)
|
|
||||||
closingWriter.Close()
|
|
||||||
|
|
||||||
drainErr := buf.Copy(rr, buf.Discard)
|
|
||||||
common.Interrupt(s.input)
|
|
||||||
s.Close()
|
|
||||||
return drainErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -351,12 +345,7 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||||
|
|
||||||
func (m *ClientWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
func (m *ClientWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||||
if s, found := m.sessionManager.Get(meta.SessionID); found {
|
if s, found := m.sessionManager.Get(meta.SessionID); found {
|
||||||
if meta.Option.Has(OptionError) {
|
s.Close(false)
|
||||||
common.Interrupt(s.input)
|
|
||||||
common.Interrupt(s.output)
|
|
||||||
}
|
|
||||||
common.Interrupt(s.input)
|
|
||||||
s.Close()
|
|
||||||
}
|
}
|
||||||
if meta.Option.Has(OptionData) {
|
if meta.Option.Has(OptionData) {
|
||||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||||
|
|
|
@ -86,9 +86,9 @@ func TestClientWorkerClose(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tr1, tw1 := pipe.New(pipe.WithoutSizeLimit())
|
tr1, tw1 := pipe.New(pipe.WithoutSizeLimit())
|
||||||
ctx1 := session.ContextWithOutbound(context.Background(), &session.Outbound{
|
ctx1 := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
Target: net.TCPDestination(net.DomainAddress("www.example.com"), 80),
|
Target: net.TCPDestination(net.DomainAddress("www.example.com"), 80),
|
||||||
})
|
}})
|
||||||
common.Must(manager.Dispatch(ctx1, &transport.Link{
|
common.Must(manager.Dispatch(ctx1, &transport.Link{
|
||||||
Reader: tr1,
|
Reader: tr1,
|
||||||
Writer: tw1,
|
Writer: tw1,
|
||||||
|
@ -103,9 +103,9 @@ func TestClientWorkerClose(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tr2, tw2 := pipe.New(pipe.WithoutSizeLimit())
|
tr2, tw2 := pipe.New(pipe.WithoutSizeLimit())
|
||||||
ctx2 := session.ContextWithOutbound(context.Background(), &session.Outbound{
|
ctx2 := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{
|
||||||
Target: net.TCPDestination(net.DomainAddress("www.example.com"), 80),
|
Target: net.TCPDestination(net.DomainAddress("www.example.com"), 80),
|
||||||
})
|
}})
|
||||||
common.Must(manager.Dispatch(ctx2, &transport.Link{
|
common.Must(manager.Dispatch(ctx2, &transport.Link{
|
||||||
Reader: tr2,
|
Reader: tr2,
|
||||||
Writer: tw2,
|
Writer: tw2,
|
||||||
|
|
|
@ -58,6 +58,7 @@ type FrameMetadata struct {
|
||||||
SessionID uint16
|
SessionID uint16
|
||||||
Option bitmask.Byte
|
Option bitmask.Byte
|
||||||
SessionStatus SessionStatus
|
SessionStatus SessionStatus
|
||||||
|
GlobalID [8]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||||
|
@ -81,6 +82,9 @@ func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||||
if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
|
if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if b.UDP != nil { // make sure it's user's proxy request
|
||||||
|
b.Write(f.GlobalID[:]) // no need to check whether it's empty
|
||||||
|
}
|
||||||
} else if b.UDP != nil {
|
} else if b.UDP != nil {
|
||||||
b.WriteByte(byte(TargetNetworkUDP))
|
b.WriteByte(byte(TargetNetworkUDP))
|
||||||
addrParser.WriteAddressPort(b, b.UDP.Address, b.UDP.Port)
|
addrParser.WriteAddressPort(b, b.UDP.Address, b.UDP.Port)
|
||||||
|
@ -122,7 +126,8 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
|
||||||
f.Option = bitmask.Byte(b.Byte(3))
|
f.Option = bitmask.Byte(b.Byte(3))
|
||||||
f.Target.Network = net.Network_Unknown
|
f.Target.Network = net.Network_Unknown
|
||||||
|
|
||||||
if f.SessionStatus == SessionStatusNew || (f.SessionStatus == SessionStatusKeep && b.Len() != 4) {
|
if f.SessionStatus == SessionStatusNew || (f.SessionStatus == SessionStatusKeep && b.Len() > 4 &&
|
||||||
|
TargetNetwork(b.Byte(4)) == TargetNetworkUDP) { // MUST check the flag first
|
||||||
if b.Len() < 8 {
|
if b.Len() < 8 {
|
||||||
return newError("insufficient buffer: ", b.Len())
|
return newError("insufficient buffer: ", b.Len())
|
||||||
}
|
}
|
||||||
|
@ -144,5 +149,11 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Application data is essential, to test whether the pipe is closed.
|
||||||
|
if f.SessionStatus == SessionStatusNew && f.Option.Has(OptionData) &&
|
||||||
|
f.Target.Network == net.Network_UDP && b.Len() >= 8 {
|
||||||
|
copy(f.GlobalID[:], b.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,13 +32,13 @@ func TestReaderWriter(t *testing.T) {
|
||||||
pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024))
|
pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024))
|
||||||
|
|
||||||
dest := net.TCPDestination(net.DomainAddress("example.com"), 80)
|
dest := net.TCPDestination(net.DomainAddress("example.com"), 80)
|
||||||
writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream)
|
writer := NewWriter(1, dest, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||||
|
|
||||||
dest2 := net.TCPDestination(net.LocalHostIP, 443)
|
dest2 := net.TCPDestination(net.LocalHostIP, 443)
|
||||||
writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream)
|
writer2 := NewWriter(2, dest2, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||||
|
|
||||||
dest3 := net.TCPDestination(net.LocalHostIPv6, 18374)
|
dest3 := net.TCPDestination(net.LocalHostIPv6, 18374)
|
||||||
writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream)
|
writer3 := NewWriter(3, dest3, pWriter, protocol.TransferTypeStream, [8]byte{})
|
||||||
|
|
||||||
writePayload := func(writer *Writer, payload ...byte) error {
|
writePayload := func(writer *Writer, payload ...byte) error {
|
||||||
b := buf.New()
|
b := buf.New()
|
||||||
|
|
|
@ -99,7 +99,7 @@ func handle(ctx context.Context, s *Session, output buf.Writer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Close()
|
writer.Close()
|
||||||
s.Close()
|
s.Close(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *ServerWorker) ActiveConnections() uint32 {
|
func (w *ServerWorker) ActiveConnections() uint32 {
|
||||||
|
@ -131,6 +131,81 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
|
||||||
}
|
}
|
||||||
ctx = log.ContextWithAccessMessage(ctx, msg)
|
ctx = log.ContextWithAccessMessage(ctx, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if network := session.AllowedNetworkFromContext(ctx); network != net.Network_Unknown {
|
||||||
|
if meta.Target.Network != network {
|
||||||
|
return newError("unexpected network ", meta.Target.Network) // it will break the whole Mux connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if meta.GlobalID != [8]byte{} { // MUST ignore empty Global ID
|
||||||
|
mb, err := NewPacketReader(reader, &meta.Target).ReadMultiBuffer()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
XUDPManager.Lock()
|
||||||
|
x := XUDPManager.Map[meta.GlobalID]
|
||||||
|
if x == nil {
|
||||||
|
x = &XUDP{GlobalID: meta.GlobalID}
|
||||||
|
XUDPManager.Map[meta.GlobalID] = x
|
||||||
|
XUDPManager.Unlock()
|
||||||
|
} else {
|
||||||
|
if x.Status == Initializing { // nearly impossible
|
||||||
|
XUDPManager.Unlock()
|
||||||
|
newError("XUDP hit ", meta.GlobalID).Base(errors.New("conflict")).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
// It's not a good idea to return an err here, so just let client wait.
|
||||||
|
// Client will receive an End frame after sending a Keep frame.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
x.Status = Initializing
|
||||||
|
XUDPManager.Unlock()
|
||||||
|
x.Mux.Close(false) // detach from previous Mux
|
||||||
|
b := buf.New()
|
||||||
|
b.Write(mb[0].Bytes())
|
||||||
|
b.UDP = mb[0].UDP
|
||||||
|
if err = x.Mux.output.WriteMultiBuffer(mb); err != nil {
|
||||||
|
x.Interrupt()
|
||||||
|
mb = buf.MultiBuffer{b}
|
||||||
|
} else {
|
||||||
|
b.Release()
|
||||||
|
mb = nil
|
||||||
|
}
|
||||||
|
newError("XUDP hit ", meta.GlobalID).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
}
|
||||||
|
if mb != nil {
|
||||||
|
ctx = session.ContextWithTimeoutOnly(ctx, true)
|
||||||
|
// Actually, it won't return an error in Xray-core's implementations.
|
||||||
|
link, err := w.dispatcher.Dispatch(ctx, meta.Target)
|
||||||
|
if err != nil {
|
||||||
|
XUDPManager.Lock()
|
||||||
|
delete(XUDPManager.Map, x.GlobalID)
|
||||||
|
XUDPManager.Unlock()
|
||||||
|
err = newError("XUDP new ", meta.GlobalID).Base(errors.New("failed to dispatch request to ", meta.Target).Base(err))
|
||||||
|
return err // it will break the whole Mux connection
|
||||||
|
}
|
||||||
|
link.Writer.WriteMultiBuffer(mb) // it's meaningless to test a new pipe
|
||||||
|
x.Mux = &Session{
|
||||||
|
input: link.Reader,
|
||||||
|
output: link.Writer,
|
||||||
|
}
|
||||||
|
newError("XUDP new ", meta.GlobalID).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
}
|
||||||
|
x.Mux = &Session{
|
||||||
|
input: x.Mux.input,
|
||||||
|
output: x.Mux.output,
|
||||||
|
parent: w.sessionManager,
|
||||||
|
ID: meta.SessionID,
|
||||||
|
transferType: protocol.TransferTypePacket,
|
||||||
|
XUDP: x,
|
||||||
|
}
|
||||||
|
go handle(ctx, x.Mux, w.link.Writer)
|
||||||
|
x.Status = Active
|
||||||
|
if !w.sessionManager.Add(x.Mux) {
|
||||||
|
x.Mux.Close(false)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
link, err := w.dispatcher.Dispatch(ctx, meta.Target)
|
link, err := w.dispatcher.Dispatch(ctx, meta.Target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if meta.Option.Has(OptionData) {
|
if meta.Option.Has(OptionData) {
|
||||||
|
@ -157,8 +232,7 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
|
||||||
rr := s.NewReader(reader, &meta.Target)
|
rr := s.NewReader(reader, &meta.Target)
|
||||||
if err := buf.Copy(rr, s.output); err != nil {
|
if err := buf.Copy(rr, s.output); err != nil {
|
||||||
buf.Copy(rr, buf.Discard)
|
buf.Copy(rr, buf.Discard)
|
||||||
common.Interrupt(s.input)
|
return s.Close(false)
|
||||||
return s.Close()
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -182,15 +256,8 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||||
|
|
||||||
if err != nil && buf.IsWriteError(err) {
|
if err != nil && buf.IsWriteError(err) {
|
||||||
newError("failed to write to downstream writer. closing session ", s.ID).Base(err).WriteToLog()
|
newError("failed to write to downstream writer. closing session ", s.ID).Base(err).WriteToLog()
|
||||||
|
s.Close(false)
|
||||||
// Notify remote peer to close this session.
|
return buf.Copy(rr, buf.Discard)
|
||||||
closingWriter := NewResponseWriter(meta.SessionID, w.link.Writer, protocol.TransferTypeStream)
|
|
||||||
closingWriter.Close()
|
|
||||||
|
|
||||||
drainErr := buf.Copy(rr, buf.Discard)
|
|
||||||
common.Interrupt(s.input)
|
|
||||||
s.Close()
|
|
||||||
return drainErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -198,12 +265,7 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||||
|
|
||||||
func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||||
if s, found := w.sessionManager.Get(meta.SessionID); found {
|
if s, found := w.sessionManager.Get(meta.SessionID); found {
|
||||||
if meta.Option.Has(OptionError) {
|
s.Close(false)
|
||||||
common.Interrupt(s.input)
|
|
||||||
common.Interrupt(s.output)
|
|
||||||
}
|
|
||||||
common.Interrupt(s.input)
|
|
||||||
s.Close()
|
|
||||||
}
|
}
|
||||||
if meta.Option.Has(OptionData) {
|
if meta.Option.Has(OptionData) {
|
||||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue