Compare commits

..

169 Commits
v4.2 ... v4.7.2

Author SHA1 Message Date
Darien Raymond
72f5e9de16 fix vendor directory 2018-12-04 18:59:14 +01:00
Darien Raymond
8567c66842 flatten vendor directory 2018-12-04 18:50:53 +01:00
Darien Raymond
bea521537e errors.Combine 2018-12-04 14:17:08 +01:00
Darien Raymond
d4613f156b defer at right place 2018-12-04 14:16:31 +01:00
Darien Raymond
0bc22154e5 defer server close 2018-12-04 14:15:35 +01:00
Darien Raymond
c49b93b39e simplify buffer allocation 2018-12-04 11:07:33 +01:00
Darien Raymond
a6d81cc56d api doc 2018-12-03 23:39:21 +01:00
Darien Raymond
28fa84ce69 API doc 2018-12-03 22:44:42 +01:00
Darien Raymond
05f8de1b8f allow custom controller function 2018-12-03 17:41:24 +01:00
Darien Raymond
0f63be6340 open ReadBuffer for quic connections 2018-12-03 16:01:14 +01:00
Darien Raymond
3340f81d03 fix reader/writer for packet conn 2018-12-03 14:20:57 +01:00
Darien Raymond
e392f8ed3d use WriteByte instead of copy 2018-12-03 12:50:24 +01:00
Darien Raymond
844f6937a4 reduce memory consumption on readOne 2018-12-03 12:40:38 +01:00
Darien Raymond
c5a384195a fix #1451 2018-12-02 19:40:43 +01:00
Darien Raymond
e5314cfb56 domain property 2018-12-01 21:02:36 +01:00
Darien Raymond
057e271588 update proto lib 2018-12-01 17:39:51 +01:00
Darien Raymond
9379b2f934 fix srtp header 2018-11-30 17:37:38 +01:00
Darien Raymond
7244dc928e max buffers in one read 2018-11-30 14:48:44 +01:00
Darien Raymond
afba7a45c9 add missing files 2018-11-30 14:43:51 +01:00
Darien Raymond
61ad81c326 implement ReadMultiBuffer in quic conn 2018-11-30 14:41:11 +01:00
Darien Raymond
d649de172e Update version 2018-11-29 17:17:39 +01:00
Darien Raymond
fd060a0880 temp fix deadlock in quic lib 2018-11-29 17:17:07 +01:00
Darien Raymond
3b1aaa9a3d update quic parameters 2018-11-28 23:14:41 +01:00
Darien Raymond
1d54fd7433 update timeout and window size in quic 2018-11-27 22:39:54 +01:00
Darien Raymond
828cfac630 remove unused file 2018-11-27 15:33:53 +01:00
Darien Raymond
135bf169c0 update quic vendor 2018-11-27 15:29:03 +01:00
Darien Raymond
90ab42b1cb close connection without error 2018-11-27 10:56:07 +01:00
Darien Raymond
fe0f32e0f1 vendor websocket 2018-11-27 10:08:34 +01:00
Darien Raymond
f70712a1a3 remove warning messages in quic 2018-11-26 18:00:41 +01:00
Darien Raymond
45fbf6f059 update quic connection handling 2018-11-25 21:56:24 +01:00
Darien Raymond
e0093206e7 update quic 2018-11-25 21:55:45 +01:00
Darien Raymond
5ecec6afd8 reduce concurrency 2018-11-24 23:10:04 +01:00
Darien Raymond
4ff26a36ad update quic connection handling 2018-11-24 22:18:17 +01:00
Darien Raymond
ad4908be9e disable logging completely 2018-11-24 22:17:41 +01:00
Darien Raymond
a1801b5d6f update quic config 2018-11-23 23:51:07 +01:00
Darien Raymond
275ab2ae8c fix inactive session removal 2018-11-23 20:47:36 +01:00
Darien Raymond
19926b8e4f update quic 2018-11-23 17:04:53 +01:00
Darien Raymond
6870ead73e remove unless check 2018-11-23 17:04:32 +01:00
Darien Raymond
18e7d107ba Revert "reduce mtu in quic"
This reverts commit 71a5cf09da.
2018-11-23 13:12:13 +01:00
Darien Raymond
71a5cf09da reduce mtu in quic 2018-11-23 12:20:13 +01:00
Darien Raymond
7dee0a71c1 update quic test 2018-11-22 23:13:52 +01:00
Darien Raymond
480175691c comment 2018-11-22 22:49:45 +01:00
Darien Raymond
db83f23f00 Update version 2018-11-22 19:22:08 +01:00
Darien Raymond
39a092b178 fix #1427 2018-11-22 19:07:49 +01:00
Darien Raymond
9d17221266 fix test last time 2018-11-22 17:30:06 +01:00
Darien Raymond
39c84c8dcd fix broken test again 2018-11-22 17:19:24 +01:00
Darien Raymond
9cad27e9bd skip answer if it is not A or AAAA record 2018-11-22 17:16:44 +01:00
Darien Raymond
4b6e12815d fix sockopt test 2018-11-22 16:33:32 +01:00
Darien Raymond
1cc3a4832d fix logic for adding localhost dns 2018-11-22 16:29:09 +01:00
Darien Raymond
fa7ce36aa8 update WriteMultiBuffer 2018-11-22 13:36:36 +01:00
Darien Raymond
3a1cf06dc1 add WriteMultiBuffer for interConn 2018-11-22 12:31:11 +01:00
Darien Raymond
010964f272 tweak quic parameters 2018-11-22 11:57:17 +01:00
Darien Raymond
e26ae20065 update settings 2018-11-21 22:10:17 +01:00
Darien Raymond
9d4a1df40b integration test for vmess/quic 2018-11-21 22:08:21 +01:00
Darien Raymond
5bd47c89c4 enable quic transport 2018-11-21 22:03:00 +01:00
Darien Raymond
3335f77c70 implement header and auth for quic 2018-11-21 22:02:19 +01:00
Darien Raymond
92a6699706 fix broken test again 2018-11-21 17:15:41 +01:00
Darien Raymond
cfa7ee88ce fix broken test 2018-11-21 16:58:37 +01:00
Darien Raymond
096bbd2c51 prototype of quic transport 2018-11-21 16:47:06 +01:00
Darien Raymond
3eac22c27d add missing changes 2018-11-21 14:55:05 +01:00
Darien Raymond
5279296f03 remove use of context.WithValue in transport 2018-11-21 14:54:40 +01:00
Darien Raymond
d2d0c69f17 benchmark certificate issuing 2018-11-21 13:00:26 +01:00
Darien Raymond
e99dd29946 update putPacketBuffer 2018-11-21 11:03:47 +01:00
Darien Raymond
649f4da92a update mtu in quic 2018-11-21 10:59:37 +01:00
Darien Raymond
b2cbc369ac fix build break 2018-11-21 10:52:07 +01:00
Darien Raymond
14c2d415d2 update vendor directory 2018-11-21 10:49:41 +01:00
Darien Raymond
786290a31d remove all vendor tests 2018-11-20 23:59:01 +01:00
Darien Raymond
84f8bca01c vendor quic 44 2018-11-20 23:51:25 +01:00
Darien Raymond
bb8cab9cc7 change from map to slice 2018-11-20 17:15:11 +01:00
Darien Raymond
234c8081f4 remove unused functions 2018-11-20 17:05:32 +01:00
Darien Raymond
769f770cf7 migrate NetworkList to []Network 2018-11-20 16:58:26 +01:00
Darien Raymond
7b80322b60 fix function signature 2018-11-20 16:12:14 +01:00
Darien Raymond
d1f318c82a remove unused functions 2018-11-20 16:11:55 +01:00
Darien Raymond
6543f5825b remove usage of NetworkList in NetworkMatcher 2018-11-20 12:27:33 +01:00
Darien Raymond
b9c8506c23 optimize network matcher 2018-11-20 12:25:56 +01:00
Darien Raymond
120058310a only yield goroutine after second write 2018-11-20 10:45:02 +01:00
Darien Raymond
4b885f5775 fix broken test 2018-11-19 21:58:03 +01:00
Darien Raymond
8a82a3664c pick IP family by local address in freedom 2018-11-19 21:36:46 +01:00
Darien Raymond
9a83561504 update library usage 2018-11-19 21:07:47 +01:00
Darien Raymond
bb1efdebd1 support querying either IPv4 or IPv6 dns 2018-11-19 20:42:02 +01:00
Darien Raymond
8d8eb0f35a update pipe test 2018-11-19 16:29:27 +01:00
Darien Raymond
afc613f8f3 test case for edns0_subnet 2018-11-19 14:13:20 +01:00
Darien Raymond
d013e8069d switch to stdlib for dns queries 2018-11-19 13:13:02 +01:00
Darien Raymond
d675bb92df update benchmark for address 2018-11-19 11:18:20 +01:00
Darien Raymond
91227581e5 noescape on asm functions 2018-11-19 00:33:20 +01:00
Darien Raymond
97b7c303b0 escape analysis task 2018-11-19 00:33:10 +01:00
Darien Raymond
83c4b20b6e fix lint warnings 2018-11-19 00:33:00 +01:00
Darien Raymond
b1b8cb5ef5 Fix SplitSize 2018-11-18 22:12:31 +01:00
Darien Raymond
5c4e33f759 rewrite SliceBySize 2018-11-18 20:16:14 +01:00
Darien Raymond
bcd5d026fe rewrite SplitFirst 2018-11-18 19:57:29 +01:00
Darien Raymond
cb7646f682 remove unnecessary err object from SplitBytes 2018-11-18 19:44:32 +01:00
Darien Raymond
842a089dad refactor multibuffer 2018-11-18 19:36:36 +01:00
Darien Raymond
0f324a613e remove MultiBuffer.Release 2018-11-17 22:45:07 +01:00
Darien Raymond
b41513f644 benchmark pipe 2018-11-17 19:47:30 +01:00
Darien Raymond
27772a75a7 refactor MultiBuffer append 2018-11-17 09:12:20 +01:00
Darien Raymond
650f5e6350 Update version 2018-11-16 21:18:56 +01:00
Darien Raymond
09d3aaf2ab compatible with semver 2018-11-16 21:18:08 +01:00
Darien Raymond
14646940a0 trigger GC after loading 2018-11-16 17:17:11 +01:00
Darien Raymond
248099eca5 switch to bytes.Equal 2018-11-16 17:00:16 +01:00
Darien Raymond
1089a887e0 consider closed worker is full. fixes #1414 2018-11-16 15:42:18 +01:00
Darien Raymond
48cac1733f remove NewMultiBufferCap 2018-11-16 11:29:16 +01:00
Darien Raymond
2364f5f280 remove AddressFamily.Either() 2018-11-16 11:13:36 +01:00
Darien Raymond
a01fdc29a6 remove NewMultiBufferValue 2018-11-16 11:08:12 +01:00
Darien Raymond
db5259e75b avoid heap allocation for buffer variables 2018-11-16 10:30:26 +01:00
Darien Raymond
f2f67132a7 refactor socks handshake 2018-11-15 23:37:53 +01:00
Darien Raymond
deebb68597 update buf test 2018-11-15 22:06:54 +01:00
Darien Raymond
a20262ef20 allocate buffer on stack 2018-11-15 21:32:27 +01:00
Darien Raymond
a5ed9e00ab revert opt on copy 2018-11-15 21:16:54 +01:00
Darien Raymond
bd7bc63fac prevent stack copy 2018-11-15 21:16:43 +01:00
Darien Raymond
151f316c32 remove unnecessary buf allocation 2018-11-15 21:14:53 +01:00
Darien Raymond
6d770d6f30 remove unused members 2018-11-15 19:59:23 +01:00
Darien Raymond
9bc6a5813e improve performance on copy 2018-11-15 19:44:24 +01:00
Darien Raymond
4de776265b Update version 2018-11-15 17:05:52 +01:00
Darien Raymond
0fd7e9216a fix test break 2018-11-15 16:32:38 +01:00
Darien Raymond
10b4bbf7c6 use stack allocated byte array 2018-11-15 16:29:40 +01:00
Darien Raymond
770a20d266 stack allocated buffer 2018-11-15 16:04:13 +01:00
Darien Raymond
24288a74a2 update tests 2018-11-15 11:17:20 +01:00
Darien Raymond
ac4f868078 introduce go-cmp 2018-11-15 10:30:03 +01:00
Darien Raymond
7560a99d7b check frame size for status new 2018-11-14 22:55:33 +01:00
Darien Raymond
6c7dcc35ab remove buffer.WriteBytes 2018-11-14 22:55:20 +01:00
Darien Raymond
ff7e5a7cdb benchmark mux frame 2018-11-14 22:11:05 +01:00
Darien Raymond
61b1013571 benchmark task 2018-11-14 21:00:51 +01:00
Darien Raymond
5c5816072e use buffer for reading user id in socks 2018-11-14 20:23:52 +01:00
Darien Raymond
585608a796 propagate error 2018-11-14 19:17:11 +01:00
Darien Raymond
16102271dd improve address serialization performance 2018-11-14 19:16:46 +01:00
Darien Raymond
e8faa7d4e3 move pipe option into dedicated struct 2018-11-14 12:31:59 +01:00
Darien Raymond
8c12fb6ff1 update config files 2018-11-13 23:29:36 +01:00
Darien Raymond
a14fae4b35 fix lint warnings 2018-11-13 23:19:58 +01:00
Darien Raymond
c5ccbe6b63 cleanup serial package 2018-11-13 22:46:01 +01:00
Darien Raymond
eeb588ffa0 update wss test 2018-11-13 20:55:34 +01:00
Darien Raymond
29d978d157 add feature request template 2018-11-13 20:40:18 +01:00
Darien Raymond
953bfac572 remove unnecessary assignment 2018-11-13 09:51:55 +01:00
Darien Raymond
6f7c30ad51 update status badge 2018-11-13 09:45:32 +01:00
Darien Raymond
97f8727ad5 update azure pipeline 2018-11-13 00:00:12 +01:00
Darien Raymond
cd73486e93 update azure pipelines 2018-11-12 23:58:26 +01:00
Darien Raymond
d63e576a4c remove unused yaml files 2018-11-12 23:58:11 +01:00
Darien Raymond
265f10e6c3 update badges 2018-11-12 23:13:53 +01:00
Darien Raymond
2de8a0be2e set test timeout 2018-11-12 23:07:04 +01:00
Darien Raymond
b40ae39d56 add coverage token 2018-11-12 23:03:21 +01:00
Darien Raymond
d1deebb6a9 azure pipeline template 2018-11-12 22:37:44 +01:00
Darien Raymond
0cfc13b029 Fix job name 2018-11-12 22:29:36 +01:00
Darien Raymond
9343b8879e add coverage job to azure pipeline 2018-11-12 22:28:13 +01:00
Darien Raymond
4f167bb4d0 fix azure pipeline 2018-11-12 22:12:47 +01:00
Darien Raymond
6dd5365b84 multiple platform 2018-11-12 22:12:01 +01:00
Darien Raymond
ab7930c09b run test in azure pipeline 2018-11-12 21:37:13 +01:00
Darien Raymond
c9fd7c272c Merge branch 'master' of https://github.com/v2ray/v2ray-core 2018-11-12 21:24:14 +01:00
Victoria Raymond
9c0da3bab2 Set up CI with Azure Pipelines 2018-11-12 21:01:30 +01:00
Darien Raymond
a8b3c74945 sort import 2018-11-12 19:51:29 +01:00
Darien Raymond
ee844a2b75 release buffer after use 2018-11-12 19:51:21 +01:00
Darien Raymond
891bdd14fe close conn after use 2018-11-11 21:13:05 +01:00
Darien Raymond
956868ef78 yield goroutine on pipe write 2018-11-11 18:58:58 +01:00
Darien Raymond
3f3d00298a share rand reader across auth readers 2018-11-11 16:34:58 +01:00
Darien Raymond
c26a4b407b adjust default value of buffer size 2018-11-11 10:54:47 +01:00
Darien Raymond
90291730f5 update geoip.dat before release 2018-11-09 11:27:17 +01:00
Darien Raymond
cf21cb13dd Update version 2018-11-09 10:18:33 +01:00
Darien Raymond
df2d5b5d75 fix broken tests 2018-11-08 01:01:53 +01:00
Darien Raymond
58221ebae1 optimize multi-geoip matcher 2018-11-07 23:57:06 +01:00
Darien Raymond
2cc92920fa test case for balancer 2018-11-07 21:25:43 +01:00
Darien Raymond
73d3be424b prototype for balancing rules 2018-11-07 21:08:20 +01:00
Darien Raymond
874fc87498 test long header 2018-11-07 17:16:57 +01:00
Darien Raymond
440cf090d6 notify remote peer to close session 2018-11-07 12:46:20 +01:00
Darien Raymond
9f56d48297 Merge branch 'master' of https://github.com/v2ray/v2ray-core 2018-11-07 09:12:07 +01:00
Victoria Raymond
c98019a9c3 Merge pull request #1366 from wuxiangzhou2010/patch-1
fix a typo
2018-11-07 07:03:24 +01:00
Wuxiang
557330808d fix a typo 2018-11-07 11:15:47 +08:00
Darien Raymond
90b132b434 update ci machine type 2018-11-06 22:02:52 +01:00
Darien Raymond
9d91a97926 golang 1.11.2 2018-11-06 20:54:17 +01:00
Darien Raymond
4b2ee1185d update config file 2018-11-05 22:13:45 +01:00
Darien Raymond
03386f958d update release ci 2018-11-05 17:13:35 +01:00
Darien Raymond
425ac88133 Update version 2018-11-05 16:13:08 +01:00
417 changed files with 34005 additions and 1925 deletions

View File

@@ -1,24 +0,0 @@
---
name: V2Ray 进程崩溃
about: "提交一个 V2Ray 的 panic 日志"
---
提交 Issue 之前请先阅读 [Issue 指引](https://github.com/v2ray/v2ray-core/blob/master/.github/SUPPORT.md),然后回答下面的问题,谢谢。
除非特殊情况,请完整填写所有问题。不按模板发的 issue 将直接被关闭。
如果你遇到的问题不是 V2Ray 的 bug比如你不清楚要如何配置请使用[Discussion](https://github.com/v2ray/discussion/issues)进行讨论。
1) 你正在使用哪个版本的 V2Ray如果服务器和客户端使用了不同版本请注明
2) 你的使用场景是什么?比如使用 Chrome 通过 Socks/VMess 代理观看 YouTube 视频。
3) 请附上 panic 时的完整输出。
在 Linux (Systemd) 上可以通过 `journalctl -u v2ray` 查看最近的 panic 日志。
4) 请附上配置文件(提交 Issue 前请隐藏服务器端IP地址
```javascript
// 在这里附上配置文件
```
请预览一下你填的内容再提交。

View File

@@ -3,7 +3,6 @@ name: Crash report
about: "Create a report for panics"
---
Please read the [instruction](https://github.com/v2ray/v2ray-core/blob/master/.github/SUPPORT.md) and answer the following questions before submitting your issue. Thank you.
Please answer all the questions with enough information. All issues not following this template will be closed immediately.
If you are not sure if your question is truely a bug in V2Ray, please discuss it [here](https://github.com/v2ray/discussion/issues) first.
@@ -15,7 +14,7 @@ If you are not sure if your question is truely a bug in V2Ray, please discuss it
You may get panic log using command `journalctl -u v2ray` if your system is Linux (systemd).
5) Please attach your configuration file (**Mask IP addresses before submit this issue**).
4) Please attach your configuration file (**Mask IP addresses before submit this issue**).
```javascript
// Please attach your configuration here.

6
.github/ISSUE_TEMPLATE/feature_en.md vendored Normal file
View File

@@ -0,0 +1,6 @@
---
name: Feature Request
about: "Open a feature request to V2Ray"
---
Please describe the new feature you want in detail.

View File

@@ -1,24 +0,0 @@
sudo: required
os:
- osx
- linux
- windows
language: go
go:
- "1.11.x"
go_import_path: v2ray.com/core
git:
depth: 5
before_script:
- uname -a
- date
script:
- go test -p 1 -tags json -v v2ray.com/core/...
after_success:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./testing/coverage/coverall; fi

View File

@@ -10,7 +10,8 @@
"--disable=gas",
"--disable=gocyclo",
"--disable=gosec",
"--disable=interfacer"
"--disable=interfacer",
"--deadline=5m"
],
"go.formatTool": "goimports",

18
.vscode/tasks.json vendored
View File

@@ -28,6 +28,24 @@
"label": "test",
"args": ["-p", "1", "v2ray.com/core/..."],
"group": "test"
},
{
"label": "Escape Analysis",
"type": "shell",
"command": "go build -gcflags -m .",
"problemMatcher": {
"pattern": {
"regexp": "^(.*):(.*):(.*): (.*)$",
"file": 1,
"line": 2,
"column": 3,
"message": 4
},
"fileLocation": ["relative", "${fileDirname}"]
},
"options": {
"cwd": "${fileDirname}"
}
}
]
}

View File

@@ -1,23 +1,17 @@
# Project V
[![Build Status][1]][2] [![AppVeyor][3]][4] [![Run Status][5]][6] [![codecov.io][7]][8] [![Go Report][9]][10] [![GoDoc][11]][12] [![codebeat][13]][14] [![Downloads][15]][16]
[![Build Status][1]][2] [![codecov.io][3]][4] [![GoDoc][5]][6] [![codebeat][7]][8] [![Downloads][9]][10]
[1]: https://travis-ci.org/v2ray/v2ray-core.svg?branch=master "Build Status badge"
[2]: https://travis-ci.org/v2ray/v2ray-core "Travis-CI Build Status"
[3]: https://ci.appveyor.com/api/projects/status/bx8o4tvbvhe6p5k5?svg=true "App Veyor Build Status"
[4]: https://ci.appveyor.com/project/DarienRaymond/v2ray-core "App Veyor Link"
[5]: https://api.shippable.com/projects/5b680bc42b26aa08007371fc/badge?branch=master "Shippable Build Status"
[6]: https://app.shippable.com/github/v2ray/v2ray-core "Shippable Link"
[7]: https://codecov.io/github/v2ray/v2ray-core/coverage.svg?branch=master "Coverage badge"
[8]: https://codecov.io/github/v2ray/v2ray-core?branch=master "Codecov Status"
[9]: https://goreportcard.com/badge/v2ray.com/core "Go Report badge"
[10]: https://goreportcard.com/report/v2ray.com/core "Go Report"
[11]: https://godoc.org/v2ray.com/core?status.svg "GoDoc badge"
[12]: https://godoc.org/v2ray.com/core "GoDoc"
[13]: https://codebeat.co/badges/f2354ca8-3e24-463d-a2e3-159af73b2477 "Codebeat badge"
[14]: https://codebeat.co/projects/github-com-v2ray-v2ray-core-master "Codebeat"
[15]: https://img.shields.io/github/downloads/v2ray/v2ray-core/total.svg "All releases badge"
[16]: https://github.com/v2ray/v2ray-core/releases/ "All releases number"
[1]: https://dev.azure.com/v2ray/core/_apis/build/status/v2ray.core "Build Status badge"
[2]: https://dev.azure.com/v2ray/core/_build/latest?definitionId=1 "Azure Build Status"
[3]: https://codecov.io/github/v2ray/v2ray-core/coverage.svg?branch=master "Coverage badge"
[4]: https://codecov.io/github/v2ray/v2ray-core?branch=master "Codecov Status"
[5]: https://godoc.org/v2ray.com/core?status.svg "GoDoc badge"
[6]: https://godoc.org/v2ray.com/core "GoDoc"
[7]: https://codebeat.co/badges/f2354ca8-3e24-463d-a2e3-159af73b2477 "Codebeat badge"
[8]: https://codebeat.co/projects/github-com-v2ray-v2ray-core-master "Codebeat"
[9]: https://img.shields.io/github/downloads/v2ray/v2ray-core/total.svg "All releases badge"
[10]: https://github.com/v2ray/v2ray-core/releases/ "All releases number"
Project V is a set of network tools that help you to build your own computer network. It secures your network connections and thus protects your privacy. See [our website](https://www.v2ray.com/) for more information.
@@ -30,7 +24,8 @@ Project V is a set of network tools that help you to build your own computer net
This repo relies on the following third-party projects:
* In production:
* [miekg/dns](https://github.com/miekg/dns)
* [gorilla/websocket](https://github.com/gorilla/websocket)
* [gRPC](https://google.golang.org/grpc)
* For testing only:
* [miekg/dns](https://github.com/miekg/dns)
* [h12w/socks](https://github.com/h12w/socks)

14
annotations.go Normal file
View File

@@ -0,0 +1,14 @@
package core
// Annotation is a concept in V2Ray. This struct is only for documentation. It is not used anywhere.
// Annotations begin with "v2ray:" in comment, as metadata of functions or types.
type Annotation struct {
// API is for types or functions that can be used in other libs. Possible values are:
//
// * v2ray:api:beta for types or functions that are ready for use, but maybe changed in the future.
// * v2ray:api:stable for types or functions with guarantee of backward compatibility.
// * v2ray:api:deprecated for types or functions that should not be used anymore.
//
// Types or functions without api annotation should not be used externally.
API string
}

View File

@@ -30,9 +30,9 @@ func NewCommander(ctx context.Context, config *Config) (*Commander, error) {
tag: config.Tag,
}
core.RequireFeatures(ctx, func(om outbound.Manager) {
common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) {
c.ohm = om
})
}))
for _, rawConfig := range config.Service {
config, err := rawConfig.GetInstance()

View File

@@ -16,7 +16,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Config is the settings for Commander.
type Config struct {

View File

@@ -15,7 +15,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type SessionConfig struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`

View File

@@ -37,7 +37,7 @@ func (r *cachedReader) Cache(b *buf.Buffer) {
mb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100)
r.Lock()
if !mb.IsEmpty() {
common.Must(r.cache.WriteMultiBuffer(mb))
r.cache, _ = buf.MergeMulti(r.cache, mb)
}
b.Clear()
rawBytes := b.Extend(buf.Size)
@@ -75,8 +75,7 @@ func (r *cachedReader) ReadMultiBufferTimeout(timeout time.Duration) (buf.MultiB
func (r *cachedReader) CloseError() {
r.Lock()
if r.cache != nil {
r.cache.Release()
r.cache = nil
r.cache = buf.ReleaseMulti(r.cache)
}
r.Unlock()
r.reader.CloseError()

View File

@@ -32,12 +32,10 @@ func TestStatsWriter(t *testing.T) {
Writer: buf.Discard,
}
var mb buf.MultiBuffer
common.Must2(mb.Write([]byte("abcd")))
mb := buf.MergeBytes(nil, []byte("abcd"))
common.Must(writer.WriteMultiBuffer(mb))
mb.Release()
common.Must2(mb.Write([]byte("efg")))
mb = buf.MergeBytes(nil, []byte("efg"))
common.Must(writer.WriteMultiBuffer(mb))
if c.Value() != 7 {

View File

@@ -16,7 +16,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type DomainMatchingType int32

View File

@@ -7,8 +7,9 @@ import (
"v2ray.com/core/features"
)
// StaticHosts represents static domain-ip mapping in DNS server.
type StaticHosts struct {
ips map[uint32][]net.IP
ips [][]net.IP
matchers *strmatcher.MatcherGroup
}
@@ -31,10 +32,11 @@ func toStrMatcher(t DomainMatchingType, domain string) (strmatcher.Matcher, erro
return matcher, nil
}
// NewStaticHosts creates a new StaticHosts instance.
func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) {
g := new(strmatcher.MatcherGroup)
sh := &StaticHosts{
ips: make(map[uint32][]net.IP),
ips: make([][]net.IP, len(hosts)+len(legacy)+16),
matchers: g,
}
@@ -71,10 +73,25 @@ func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDoma
return sh, nil
}
func (h *StaticHosts) LookupIP(domain string) []net.IP {
func filterIP(ips []net.IP, option IPOption) []net.IP {
filtered := make([]net.IP, 0, len(ips))
for _, ip := range ips {
parsed := net.IPAddress(ip)
if (parsed.Family().IsIPv4() && option.IPv4Enable) || (parsed.Family().IsIPv6() && option.IPv6Enable) {
filtered = append(filtered, parsed.IP())
}
}
if len(filtered) == 0 {
return nil
}
return filtered
}
// LookupIP returns IP address for the given domain, if exists in this StaticHosts.
func (h *StaticHosts) LookupIP(domain string, option IPOption) []net.IP {
id := h.matchers.Match(domain)
if id == 0 {
return nil
}
return h.ips[id]
return filterIP(h.ips[id], option)
}

View File

@@ -3,13 +3,13 @@ package dns_test
import (
"testing"
"github.com/google/go-cmp/cmp"
. "v2ray.com/core/app/dns"
. "v2ray.com/ext/assert"
"v2ray.com/core/common"
)
func TestStaticHosts(t *testing.T) {
assert := With(t)
pb := []*Config_HostMapping{
{
Type: DomainMatchingType_Full,
@@ -28,17 +28,31 @@ func TestStaticHosts(t *testing.T) {
}
hosts, err := NewStaticHosts(pb, nil)
assert(err, IsNil)
common.Must(err)
{
ips := hosts.LookupIP("v2ray.com")
assert(len(ips), Equals, 1)
assert([]byte(ips[0]), Equals, []byte{1, 1, 1, 1})
ips := hosts.LookupIP("v2ray.com", IPOption{
IPv4Enable: true,
IPv6Enable: true,
})
if len(ips) != 1 {
t.Error("expect 1 IP, but got ", len(ips))
}
if diff := cmp.Diff([]byte(ips[0]), []byte{1, 1, 1, 1}); diff != "" {
t.Error(diff)
}
}
{
ips := hosts.LookupIP("www.v2ray.cn")
assert(len(ips), Equals, 1)
assert([]byte(ips[0]), Equals, []byte{2, 2, 2, 2})
ips := hosts.LookupIP("www.v2ray.cn", IPOption{
IPv4Enable: true,
IPv6Enable: true,
})
if len(ips) != 1 {
t.Error("expect 1 IP, but got ", len(ips))
}
if diff := cmp.Diff([]byte(ips[0]), []byte{2, 2, 2, 2}); diff != "" {
t.Error(diff)
}
}
}

View File

@@ -4,32 +4,45 @@ import (
"context"
"v2ray.com/core/common/net"
"v2ray.com/core/features/dns/localdns"
)
type IPOption struct {
IPv4Enable bool
IPv6Enable bool
}
type NameServerInterface interface {
QueryIP(ctx context.Context, domain string) ([]net.IP, error)
Name() string
QueryIP(ctx context.Context, domain string, option IPOption) ([]net.IP, error)
}
type localNameServer struct {
resolver net.Resolver
client *localdns.Client
}
func (s *localNameServer) QueryIP(ctx context.Context, domain string) ([]net.IP, error) {
ipAddr, err := s.resolver.LookupIPAddr(ctx, domain)
if err != nil {
return nil, err
func (s *localNameServer) QueryIP(ctx context.Context, domain string, option IPOption) ([]net.IP, error) {
if option.IPv4Enable && option.IPv6Enable {
return s.client.LookupIP(domain)
}
var ips []net.IP
for _, addr := range ipAddr {
ips = append(ips, addr.IP)
if option.IPv4Enable {
return s.client.LookupIPv4(domain)
}
return ips, nil
if option.IPv6Enable {
return s.client.LookupIPv6(domain)
}
return nil, newError("neither IPv4 nor IPv6 is enabled")
}
func (s *localNameServer) Name() string {
return "localhost"
}
func NewLocalNameServer() *localNameServer {
return &localNameServer{
resolver: net.Resolver{
PreferGo: true,
},
client: localdns.New(),
}
}

View File

@@ -6,16 +6,19 @@ import (
"time"
. "v2ray.com/core/app/dns"
. "v2ray.com/ext/assert"
"v2ray.com/core/common"
)
func TestLocalNameServer(t *testing.T) {
assert := With(t)
s := NewLocalNameServer()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, err := s.QueryIP(ctx, "google.com")
ips, err := s.QueryIP(ctx, "google.com", IPOption{
IPv4Enable: true,
IPv6Enable: true,
})
cancel()
assert(err, IsNil)
assert(len(ips), GreaterThan, 0)
common.Must(err)
if len(ips) == 0 {
t.Error("expect some ips, but got 0")
}
}

View File

@@ -16,6 +16,7 @@ import (
"v2ray.com/core/features/routing"
)
// Server is a DNS rely server.
type Server struct {
sync.Mutex
hosts *StaticHosts
@@ -25,6 +26,7 @@ type Server struct {
domainIndexMap map[uint32]uint32
}
// New creates a new DNS server with given configuration.
func New(ctx context.Context, config *Config) (*Server, error) {
server := &Server{
servers: make([]NameServerInterface, 0, len(config.NameServers)+len(config.NameServer)),
@@ -55,9 +57,9 @@ func New(ctx context.Context, config *Config) (*Server, error) {
idx := len(server.servers)
server.servers = append(server.servers, nil)
core.RequireFeatures(ctx, func(d routing.Dispatcher) {
common.Must(core.RequireFeatures(ctx, func(d routing.Dispatcher) {
server.servers[idx] = NewClassicNameServer(dest, d, server.clientIP)
})
}))
}
}
return len(server.servers) - 1
@@ -92,7 +94,7 @@ func New(ctx context.Context, config *Config) (*Server, error) {
server.domainIndexMap = domainIndexMap
}
if len(config.NameServers) == 0 {
if len(server.servers) == 0 {
server.servers = append(server.servers, NewLocalNameServer())
}
@@ -114,16 +116,39 @@ func (s *Server) Close() error {
return nil
}
func (s *Server) queryIPTimeout(server NameServerInterface, domain string) ([]net.IP, error) {
func (s *Server) queryIPTimeout(server NameServerInterface, domain string, option IPOption) ([]net.IP, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*4)
ips, err := server.QueryIP(ctx, domain)
ips, err := server.QueryIP(ctx, domain, option)
cancel()
return ips, err
}
// LookupIP implements dns.Client.
func (s *Server) LookupIP(domain string) ([]net.IP, error) {
if ip := s.hosts.LookupIP(domain); len(ip) > 0 {
return s.lookupIPInternal(domain, IPOption{
IPv4Enable: true,
IPv6Enable: true,
})
}
// LookupIPv4 implements dns.IPv4Lookup.
func (s *Server) LookupIPv4(domain string) ([]net.IP, error) {
return s.lookupIPInternal(domain, IPOption{
IPv4Enable: true,
IPv6Enable: false,
})
}
// LookupIPv6 implements dns.IPv6Lookup.
func (s *Server) LookupIPv6(domain string) ([]net.IP, error) {
return s.lookupIPInternal(domain, IPOption{
IPv4Enable: false,
IPv6Enable: true,
})
}
func (s *Server) lookupIPInternal(domain string, option IPOption) ([]net.IP, error) {
if ip := s.hosts.LookupIP(domain, option); len(ip) > 0 {
return ip, nil
}
@@ -132,22 +157,24 @@ func (s *Server) LookupIP(domain string) ([]net.IP, error) {
idx := s.domainMatcher.Match(domain)
if idx > 0 {
ns := s.servers[s.domainIndexMap[idx]]
ips, err := s.queryIPTimeout(ns, domain)
ips, err := s.queryIPTimeout(ns, domain, option)
if len(ips) > 0 {
return ips, nil
}
if err != nil {
newError("failed to lookup ip for domain ", domain, " at server ", ns.Name()).Base(err).WriteToLog()
lastErr = err
}
}
}
for _, server := range s.servers {
ips, err := s.queryIPTimeout(server, domain)
ips, err := s.queryIPTimeout(server, domain, option)
if len(ips) > 0 {
return ips, nil
}
if err != nil {
newError("failed to lookup ip for domain ", domain, " at server ", server.Name()).Base(err).WriteToLog()
lastErr = err
}
}

View File

@@ -11,6 +11,7 @@ import (
"v2ray.com/core/app/policy"
"v2ray.com/core/app/proxyman"
_ "v2ray.com/core/app/proxyman/outbound"
"v2ray.com/core/common"
"v2ray.com/core/common/net"
"v2ray.com/core/common/serial"
feature_dns "v2ray.com/core/features/dns"
@@ -27,18 +28,100 @@ type staticHandler struct {
func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
ans := new(dns.Msg)
ans.Id = r.Id
var clientIP net.IP
opt := r.IsEdns0()
if opt != nil {
for _, o := range opt.Option {
if o.Option() == dns.EDNS0SUBNET {
subnet := o.(*dns.EDNS0_SUBNET)
clientIP = subnet.Address
}
}
}
for _, q := range r.Question {
if q.Name == "google.com." && q.Qtype == dns.TypeA {
rr, _ := dns.NewRR("google.com. IN A 8.8.8.8")
ans.Answer = append(ans.Answer, rr)
if clientIP == nil {
rr, _ := dns.NewRR("google.com. IN A 8.8.8.8")
ans.Answer = append(ans.Answer, rr)
} else {
rr, _ := dns.NewRR("google.com. IN A 8.8.4.4")
ans.Answer = append(ans.Answer, rr)
}
} else if q.Name == "facebook.com." && q.Qtype == dns.TypeA {
rr, _ := dns.NewRR("facebook.com. IN A 9.9.9.9")
ans.Answer = append(ans.Answer, rr)
} else if q.Name == "ipv6.google.com." && q.Qtype == dns.TypeA {
rr, err := dns.NewRR("ipv6.google.com. IN A 8.8.8.7")
common.Must(err)
ans.Answer = append(ans.Answer, rr)
} else if q.Name == "ipv6.google.com." && q.Qtype == dns.TypeAAAA {
rr, err := dns.NewRR("ipv6.google.com. IN AAAA 2001:4860:4860::8888")
common.Must(err)
ans.Answer = append(ans.Answer, rr)
}
}
w.WriteMsg(ans)
}
func TestUDPServerSubnet(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("doesn't work on Windows due to miekg/dns changes.")
}
assert := With(t)
port := udp.PickPort()
dnsServer := dns.Server{
Addr: "127.0.0.1:" + port.String(),
Net: "udp",
Handler: &staticHandler{},
UDPSize: 1200,
}
go dnsServer.ListenAndServe()
time.Sleep(time.Second)
config := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&Config{
NameServers: []*net.Endpoint{
{
Network: net.Network_UDP,
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
},
},
Port: uint32(port),
},
},
ClientIp: []byte{7, 8, 9, 10},
}),
serial.ToTypedMessage(&dispatcher.Config{}),
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
serial.ToTypedMessage(&policy.Config{}),
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
},
},
}
v, err := core.New(config)
assert(err, IsNil)
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
ips, err := client.LookupIP("google.com")
assert(err, IsNil)
assert(len(ips), Equals, 1)
assert([]byte(ips[0]), Equals, []byte{8, 8, 4, 4})
}
func TestUDPServer(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("doesn't work on Windows due to miekg/dns changes.")
@@ -185,3 +268,59 @@ func TestPrioritizedDomain(t *testing.T) {
t.Error("DNS query doesn't finish in 2 seconds.")
}
}
func TestUDPServerIPv6(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("doesn't work on Windows due to miekg/dns changes.")
}
assert := With(t)
port := udp.PickPort()
dnsServer := dns.Server{
Addr: "127.0.0.1:" + port.String(),
Net: "udp",
Handler: &staticHandler{},
UDPSize: 1200,
}
go dnsServer.ListenAndServe()
time.Sleep(time.Second)
config := &core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&Config{
NameServers: []*net.Endpoint{
{
Network: net.Network_UDP,
Address: &net.IPOrDomain{
Address: &net.IPOrDomain_Ip{
Ip: []byte{127, 0, 0, 1},
},
},
Port: uint32(port),
},
},
}),
serial.ToTypedMessage(&dispatcher.Config{}),
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
serial.ToTypedMessage(&policy.Config{}),
},
Outbound: []*core.OutboundHandlerConfig{
{
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
},
},
}
v, err := core.New(config)
assert(err, IsNil)
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
client6 := client.(feature_dns.IPv6Lookup)
ips, err := client6.LookupIPv6("ipv6.google.com")
assert(err, IsNil)
assert(len(ips), Equals, 1)
assert([]byte(ips[0]), Equals, []byte{32, 1, 72, 96, 72, 96, 0, 0, 0, 0, 0, 0, 0, 0, 136, 136})
}

View File

@@ -2,11 +2,12 @@ package dns
import (
"context"
"encoding/binary"
"sync"
"sync/atomic"
"time"
"github.com/miekg/dns"
"golang.org/x/net/dns/dnsmessage"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
@@ -18,14 +19,6 @@ import (
"v2ray.com/core/transport/internet/udp"
)
var (
multiQuestionDNS = map[net.Address]bool{
net.IPAddress([]byte{8, 8, 8, 8}): true,
net.IPAddress([]byte{8, 8, 4, 4}): true,
net.IPAddress([]byte{9, 9, 9, 9}): true,
}
)
type IPRecord struct {
IP net.IP
Expire time.Time
@@ -64,6 +57,10 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
return s
}
func (s *ClassicNameServer) Name() string {
return s.address.String()
}
func (s *ClassicNameServer) Cleanup() error {
now := time.Now()
s.Lock()
@@ -105,16 +102,18 @@ func (s *ClassicNameServer) Cleanup() error {
}
func (s *ClassicNameServer) HandleResponse(ctx context.Context, payload *buf.Buffer) {
msg := new(dns.Msg)
err := msg.Unpack(payload.Bytes())
if err == dns.ErrTruncated {
newError("truncated message received. DNS server should still work. If you see anything abnormal, please submit an issue to v2ray-core.").AtWarning().WriteToLog()
} else if err != nil {
var parser dnsmessage.Parser
header, err := parser.Start(payload.Bytes())
if err != nil {
newError("failed to parse DNS response").Base(err).AtWarning().WriteToLog()
return
}
if err := parser.SkipAllQuestions(); err != nil {
newError("failed to skip questions in DNS response").Base(err).AtWarning().WriteToLog()
return
}
id := msg.Id
id := header.ID
s.Lock()
req, f := s.requests[id]
if f {
@@ -130,23 +129,43 @@ func (s *ClassicNameServer) HandleResponse(ctx context.Context, payload *buf.Buf
ips := make([]IPRecord, 0, 16)
now := time.Now()
for _, rr := range msg.Answer {
var ip net.IP
ttl := rr.Header().Ttl
switch rr := rr.(type) {
case *dns.A:
ip = rr.A
case *dns.AAAA:
ip = rr.AAAA
for {
header, err := parser.AnswerHeader()
if err != nil {
if err != dnsmessage.ErrSectionDone {
newError("failed to parse answer section for domain: ", domain).Base(err).WriteToLog()
}
break
}
ttl := header.TTL
if ttl == 0 {
ttl = 600
}
if len(ip) > 0 {
switch header.Type {
case dnsmessage.TypeA:
ans, err := parser.AResource()
if err != nil {
newError("failed to parse A record for domain: ", domain).Base(err).WriteToLog()
break
}
ips = append(ips, IPRecord{
IP: ip,
Expire: now.Add(time.Second * time.Duration(ttl)),
IP: net.IP(ans.A[:]),
Expire: now.Add(time.Duration(ttl) * time.Second),
})
case dnsmessage.TypeAAAA:
ans, err := parser.AAAAResource()
if err != nil {
newError("failed to parse A record for domain: ", domain).Base(err).WriteToLog()
break
}
ips = append(ips, IPRecord{
IP: net.IP(ans.AAAA[:]),
Expire: now.Add(time.Duration(ttl) * time.Second),
})
default:
if err := parser.SkipAnswer(); err != nil {
newError("failed to skip answer").Base(err).WriteToLog()
}
}
}
@@ -173,31 +192,52 @@ func (s *ClassicNameServer) updateIP(domain string, ips []IPRecord) {
common.Must(s.cleanup.Start())
}
func (s *ClassicNameServer) getMsgOptions() *dns.OPT {
func (s *ClassicNameServer) getMsgOptions() *dnsmessage.Resource {
if len(s.clientIP) == 0 {
return nil
}
o := new(dns.OPT)
o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT
o.SetUDPSize(1350)
var netmask int
var family uint16
e := new(dns.EDNS0_SUBNET)
e.Code = dns.EDNS0SUBNET
if len(s.clientIP) == 4 {
e.Family = 1 // 1 for IPv4 source address, 2 for IPv6
e.SourceNetmask = 24 // 32 for IPV4, 128 for IPv6
family = 1
netmask = 24 // 24 for IPV4, 96 for IPv6
} else {
e.Family = 2
e.SourceNetmask = 96
family = 2
netmask = 96
}
e.SourceScope = 0
e.Address = s.clientIP
o.Option = append(o.Option, e)
b := make([]byte, 4)
binary.BigEndian.PutUint16(b[0:], family)
b[2] = byte(netmask)
b[3] = 0
switch family {
case 1:
ip := s.clientIP.To4().Mask(net.CIDRMask(netmask, net.IPv4len*8))
needLength := (netmask + 8 - 1) / 8 // division rounding up
b = append(b, ip[:needLength]...)
case 2:
ip := s.clientIP.Mask(net.CIDRMask(netmask, net.IPv6len*8))
needLength := (netmask + 8 - 1) / 8 // division rounding up
b = append(b, ip[:needLength]...)
}
return o
const EDNS0SUBNET = 0x08
opt := new(dnsmessage.Resource)
common.Must(opt.Header.SetEDNS0(1350, 0xfe00, true))
opt.Body = &dnsmessage.OPTResource{
Options: []dnsmessage.Option{
{
Code: EDNS0SUBNET,
Data: b,
},
},
}
return opt
}
func (s *ClassicNameServer) addPendingRequest(domain string) uint16 {
@@ -213,44 +253,39 @@ func (s *ClassicNameServer) addPendingRequest(domain string) uint16 {
return id
}
func (s *ClassicNameServer) buildMsgs(domain string) []*dns.Msg {
allowMulti := multiQuestionDNS[s.address.Address]
qA := dns.Question{
Name: domain,
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
func (s *ClassicNameServer) buildMsgs(domain string, option IPOption) []*dnsmessage.Message {
qA := dnsmessage.Question{
Name: dnsmessage.MustNewName(domain),
Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET,
}
qAAAA := dns.Question{
Name: domain,
Qtype: dns.TypeAAAA,
Qclass: dns.ClassINET,
qAAAA := dnsmessage.Question{
Name: dnsmessage.MustNewName(domain),
Type: dnsmessage.TypeAAAA,
Class: dnsmessage.ClassINET,
}
var msgs []*dns.Msg
var msgs []*dnsmessage.Message
{
msg := new(dns.Msg)
msg.Id = s.addPendingRequest(domain)
msg.RecursionDesired = true
msg.Question = []dns.Question{qA}
if allowMulti {
msg.Question = append(msg.Question, qAAAA)
}
if option.IPv4Enable {
msg := new(dnsmessage.Message)
msg.Header.ID = s.addPendingRequest(domain)
msg.Header.RecursionDesired = true
msg.Questions = []dnsmessage.Question{qA}
if opt := s.getMsgOptions(); opt != nil {
msg.Extra = append(msg.Extra, opt)
msg.Additionals = append(msg.Additionals, *opt)
}
msgs = append(msgs, msg)
}
if !allowMulti {
msg := new(dns.Msg)
msg.Id = s.addPendingRequest(domain)
msg.RecursionDesired = true
msg.Question = []dns.Question{qAAAA}
if option.IPv6Enable {
msg := new(dnsmessage.Message)
msg.Header.ID = s.addPendingRequest(domain)
msg.Header.RecursionDesired = true
msg.Questions = []dnsmessage.Question{qAAAA}
if opt := s.getMsgOptions(); opt != nil {
msg.Extra = append(msg.Extra, opt)
msg.Additionals = append(msg.Additionals, *opt)
}
msgs = append(msgs, msg)
}
@@ -258,10 +293,10 @@ func (s *ClassicNameServer) buildMsgs(domain string) []*dns.Msg {
return msgs
}
func msgToBuffer(msg *dns.Msg) (*buf.Buffer, error) {
func msgToBuffer2(msg *dnsmessage.Message) (*buf.Buffer, error) {
buffer := buf.New()
rawBytes := buffer.Extend(buf.Size)
packed, err := msg.PackBuffer(rawBytes)
packed, err := msg.AppendPack(rawBytes[:0])
if err != nil {
buffer.Release()
return nil, err
@@ -270,19 +305,19 @@ func msgToBuffer(msg *dns.Msg) (*buf.Buffer, error) {
return buffer, nil
}
func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string) {
func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, option IPOption) {
newError("querying DNS for: ", domain).AtDebug().WriteToLog(session.ExportIDToError(ctx))
msgs := s.buildMsgs(domain)
msgs := s.buildMsgs(domain, option)
for _, msg := range msgs {
b, err := msgToBuffer(msg)
b, err := msgToBuffer2(msg)
common.Must(err)
s.udpServer.Dispatch(context.Background(), s.address, b)
}
}
func (s *ClassicNameServer) findIPsForDomain(domain string) []net.IP {
func (s *ClassicNameServer) findIPsForDomain(domain string, option IPOption) []net.IP {
s.RLock()
records, found := s.ips[domain]
s.RUnlock()
@@ -295,15 +330,22 @@ func (s *ClassicNameServer) findIPsForDomain(domain string) []net.IP {
ips = append(ips, rec.IP)
}
}
return ips
return filterIP(ips, option)
}
return nil
}
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string) ([]net.IP, error) {
fqdn := dns.Fqdn(domain)
func Fqdn(domain string) string {
if len(domain) > 0 && domain[len(domain)-1] == '.' {
return domain
}
return domain + "."
}
ips := s.findIPsForDomain(fqdn)
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option IPOption) ([]net.IP, error) {
fqdn := Fqdn(domain)
ips := s.findIPsForDomain(fqdn, option)
if len(ips) > 0 {
return ips, nil
}
@@ -311,10 +353,10 @@ func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string) ([]net.I
sub := s.pub.Subscribe(fqdn)
defer sub.Close()
s.sendQuery(ctx, fqdn)
s.sendQuery(ctx, fqdn, option)
for {
ips := s.findIPsForDomain(fqdn)
ips := s.findIPsForDomain(fqdn, option)
if len(ips) > 0 {
return ips, nil
}

View File

@@ -11,13 +11,11 @@ import (
"v2ray.com/core/app/proxyman"
_ "v2ray.com/core/app/proxyman/inbound"
_ "v2ray.com/core/app/proxyman/outbound"
"v2ray.com/core/common"
"v2ray.com/core/common/serial"
. "v2ray.com/ext/assert"
)
func TestLoggerRestart(t *testing.T) {
assert := With(t)
v, err := core.New(&core.Config{
App: []*serial.TypedMessage{
serial.ToTypedMessage(&log.Config{}),
@@ -26,13 +24,11 @@ func TestLoggerRestart(t *testing.T) {
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
},
})
assert(err, IsNil)
assert(v.Start(), IsNil)
common.Must(err)
common.Must(v.Start())
server := &LoggerServer{
V: v,
}
_, err = server.RestartLogger(context.Background(), &RestartLoggerRequest{})
assert(err, IsNil)
common.Must2(server.RestartLogger(context.Background(), &RestartLoggerRequest{}))
}

View File

@@ -17,7 +17,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Config struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`

View File

@@ -16,7 +16,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type LogType int32

View File

@@ -15,7 +15,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Second struct {
Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`

View File

@@ -131,10 +131,10 @@ func (s *service) Register(server *grpc.Server) {
hs := &handlerServer{
s: s.v,
}
s.v.RequireFeatures(func(im inbound.Manager, om outbound.Manager) {
common.Must(s.v.RequireFeatures(func(im inbound.Manager, om outbound.Manager) {
hs.ihm = im
hs.ohm = om
})
}))
RegisterHandlerServiceServer(server, hs)
}

View File

@@ -20,7 +20,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type AddUserOperation struct {
User *protocol.User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`

View File

@@ -18,7 +18,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type KnownProtocols int32

View File

@@ -7,9 +7,9 @@ import (
"v2ray.com/core/app/proxyman"
"v2ray.com/core/common"
"v2ray.com/core/common/dice"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/mux"
"v2ray.com/core/common/net"
"v2ray.com/core/common/serial"
"v2ray.com/core/features/policy"
"v2ray.com/core/features/stats"
"v2ray.com/core/proxy"
@@ -89,7 +89,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
}
for port := pr.From; port <= pr.To; port++ {
if nl.HasNetwork(net.Network_TCP) {
if net.HasNetwork(nl, net.Network_TCP) {
newError("creating stream worker on ", address, ":", port).AtDebug().WriteToLog()
worker := &tcpWorker{
@@ -107,7 +107,7 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
h.workers = append(h.workers, worker)
}
if nl.HasNetwork(net.Network_UDP) {
if net.HasNetwork(nl, net.Network_UDP) {
worker := &udpWorker{
tag: tag,
proxy: p,
@@ -137,17 +137,13 @@ func (h *AlwaysOnInboundHandler) Start() error {
// Close implements common.Closable.
func (h *AlwaysOnInboundHandler) Close() error {
var errors []interface{}
var errs []error
for _, worker := range h.workers {
if err := worker.Close(); err != nil {
errors = append(errors, err)
}
errs = append(errs, worker.Close())
}
if err := h.mux.Close(); err != nil {
errors = append(errors, err)
}
if len(errors) > 0 {
return newError("failed to close all resources").Base(newError(serial.Concat(errors...)))
errs = append(errs, h.mux.Close())
if err := errors.Combine(errs...); err != nil {
return newError("failed to close all resources").Base(err)
}
return nil
}

View File

@@ -122,7 +122,7 @@ func (h *DynamicInboundHandler) refresh() error {
}
p := rawProxy.(proxy.Inbound)
nl := p.Network()
if nl.HasNetwork(net.Network_TCP) {
if net.HasNetwork(nl, net.Network_TCP) {
worker := &tcpWorker{
tag: h.tag,
address: address,
@@ -142,7 +142,7 @@ func (h *DynamicInboundHandler) refresh() error {
workers = append(workers, worker)
}
if nl.HasNetwork(net.Network_UDP) {
if net.HasNetwork(nl, net.Network_UDP) {
worker := &udpWorker{
tag: h.tag,
proxy: p,

View File

@@ -105,8 +105,8 @@ func (w *tcpWorker) Proxy() proxy.Inbound {
}
func (w *tcpWorker) Start() error {
ctx := internet.ContextWithStreamSettings(context.Background(), w.stream)
hub, err := internet.ListenTCP(ctx, w.address, w.port, func(conn internet.Connection) {
ctx := context.Background()
hub, err := internet.ListenTCP(ctx, w.address, w.port, w.stream, func(conn internet.Connection) {
go w.callback(conn)
})
if err != nil {
@@ -275,7 +275,7 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
conn, existing := w.getConnection(id)
// payload will be discarded in pipe is full.
conn.writer.WriteMultiBuffer(buf.NewMultiBufferValue(b)) // nolint: errcheck
conn.writer.WriteMultiBuffer(buf.MultiBuffer{b}) // nolint: errcheck
if !existing {
common.Must(w.checker.Start())
@@ -342,8 +342,8 @@ func (w *udpWorker) clean() error {
func (w *udpWorker) Start() error {
w.activeConn = make(map[connID]*udpConn, 16)
ctx := internet.ContextWithStreamSettings(context.Background(), w.stream)
h, err := udp.ListenUDP(ctx, w.address, w.port, udp.HubCapacity(256))
ctx := context.Background()
h, err := udp.ListenUDP(ctx, w.address, w.port, w.stream, udp.HubCapacity(256))
if err != nil {
return err
}

View File

@@ -114,6 +114,14 @@ func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
}
}
// Address implements internet.Dialer.
func (h *Handler) Address() net.Address {
if h.senderSettings == nil || h.senderSettings.Via == nil {
return nil
}
return h.senderSettings.Via.AsAddress()
}
// Dial implements internet.Dialer.
func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) {
if h.senderSettings != nil {
@@ -145,11 +153,9 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn
}
outbound.Gateway = h.senderSettings.Via.AsAddress()
}
ctx = internet.ContextWithStreamSettings(ctx, h.streamSettings)
}
return internet.Dial(ctx, dest)
return internet.Dial(ctx, dest, h.streamSettings)
}
// GetOutbound implements proxy.GetOutbound.

View File

@@ -4,11 +4,13 @@ package outbound
import (
"context"
"strings"
"sync"
"v2ray.com/core"
"v2ray.com/core/app/proxyman"
"v2ray.com/core/common"
"v2ray.com/core/common/errors"
"v2ray.com/core/features/outbound"
)
@@ -63,15 +65,16 @@ func (m *Manager) Close() error {
m.running = false
var errs []error
for _, h := range m.taggedHandler {
h.Close()
errs = append(errs, h.Close())
}
for _, h := range m.untaggedHandlers {
h.Close()
errs = append(errs, h.Close())
}
return nil
return errors.Combine(errs...)
}
// GetDefaultHandler implements outbound.Manager.
@@ -134,6 +137,29 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
return nil
}
// Select implements outbound.HandlerSelector.
func (m *Manager) Select(selectors []string) []string {
m.access.RLock()
defer m.access.RUnlock()
tags := make([]string, 0, len(selectors))
for tag := range m.taggedHandler {
match := false
for _, selector := range selectors {
if strings.HasPrefix(tag, selector) {
match = true
break
}
}
if match {
tags = append(tags, tag)
}
}
return tags
}
func init() {
common.Must(common.RegisterConfig((*proxyman.OutboundConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return New(ctx, config.(*proxyman.OutboundConfig))

View File

@@ -14,6 +14,7 @@ import (
"v2ray.com/core/transport/pipe"
)
// Bridge is a component in reverse proxy, that relays connections from Portal to local address.
type Bridge struct {
dispatcher routing.Dispatcher
tag string
@@ -22,6 +23,7 @@ type Bridge struct {
monitorTask *task.Periodic
}
// NewBridge creates a new Bridge instance.
func NewBridge(config *BridgeConfig, dispatcher routing.Dispatcher) (*Bridge, error) {
if len(config.Tag) == 0 {
return nil, newError("bridge tag is empty")

View File

@@ -15,7 +15,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Control_State int32

View File

@@ -61,13 +61,13 @@ func (p *Portal) Close() error {
return p.ohm.RemoveHandler(context.Background(), p.tag)
}
func (s *Portal) HandleConnection(ctx context.Context, link *transport.Link) error {
func (p *Portal) HandleConnection(ctx context.Context, link *transport.Link) error {
outboundMeta := session.OutboundFromContext(ctx)
if outboundMeta == nil {
return newError("outbound metadata not found").AtError()
}
if isDomain(outboundMeta.Target, s.domain) {
if isDomain(outboundMeta.Target, p.domain) {
muxClient, err := mux.NewClientWorker(*link, mux.ClientStrategy{})
if err != nil {
return newError("failed to create mux client worker").Base(err).AtWarning()
@@ -78,11 +78,11 @@ func (s *Portal) HandleConnection(ctx context.Context, link *transport.Link) err
return newError("failed to create portal worker").Base(err)
}
s.picker.AddWorker(worker)
p.picker.AddWorker(worker)
return nil
}
return s.client.Dispatch(ctx, link)
return p.client.Dispatch(ctx, link)
}
type Outbound struct {
@@ -251,8 +251,7 @@ func (w *PortalWorker) heartbeat() error {
b, err := proto.Marshal(msg)
common.Must(err)
var mb buf.MultiBuffer
common.Must2(mb.Write(b))
mb := buf.MergeBytes(nil, b)
return w.writer.WriteMultiBuffer(mb)
}

View File

@@ -7,6 +7,7 @@ import (
"v2ray.com/core"
"v2ray.com/core/common"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/net"
"v2ray.com/core/features/outbound"
"v2ray.com/core/features/routing"
@@ -82,17 +83,14 @@ func (r *Reverse) Start() error {
}
func (r *Reverse) Close() error {
var errs []error
for _, b := range r.bridges {
if err := b.Close(); err != nil {
return err
}
errs = append(errs, b.Close())
}
for _, p := range r.portals {
if err := p.Close(); err != nil {
return err
}
errs = append(errs, p.Close())
}
return nil
return errors.Combine(errs...)
}

44
app/router/balancing.go Normal file
View File

@@ -0,0 +1,44 @@
package router
import (
"v2ray.com/core/common/dice"
"v2ray.com/core/features/outbound"
)
type BalancingStrategy interface {
PickOutbound([]string) string
}
type RandomStrategy struct {
}
func (s *RandomStrategy) PickOutbound(tags []string) string {
n := len(tags)
if n == 0 {
panic("0 tags")
}
return tags[dice.Roll(n)]
}
type Balancer struct {
selectors []string
strategy BalancingStrategy
ohm outbound.Manager
}
func (b *Balancer) PickOutbound() (string, error) {
hs, ok := b.ohm.(outbound.HandlerSelector)
if !ok {
return "", newError("outbound.Manager is not a HandlerSelector")
}
tags := hs.Select(b.selectors)
if len(tags) == 0 {
return "", newError("no available outbounds selected")
}
tag := b.strategy.PickOutbound(tags)
if len(tag) == 0 {
return "", newError("balancing strategy returns empty tag")
}
return tag, nil
}

View File

@@ -113,7 +113,7 @@ func targetFromContent(ctx context.Context) net.Destination {
type MultiGeoIPMatcher struct {
matchers []*GeoIPMatcher
onSource bool
destFunc func(context.Context) net.Destination
}
func NewMultiGeoIPMatcher(geoips []*GeoIP, onSource bool) (*MultiGeoIPMatcher, error) {
@@ -126,32 +126,33 @@ func NewMultiGeoIPMatcher(geoips []*GeoIP, onSource bool) (*MultiGeoIPMatcher, e
matchers = append(matchers, matcher)
}
var destFunc func(context.Context) net.Destination
if onSource {
destFunc = sourceFromContext
} else {
destFunc = targetFromContent
}
return &MultiGeoIPMatcher{
matchers: matchers,
onSource: onSource,
destFunc: destFunc,
}, nil
}
func (m *MultiGeoIPMatcher) Apply(ctx context.Context) bool {
ips := make([]net.IP, 0, 4)
if resolver, ok := ResolvedIPsFromContext(ctx); ok {
dest := m.destFunc(ctx)
if dest.IsValid() && dest.Address.Family().IsIP() {
ips = append(ips, dest.Address.IP())
} else if resolver, ok := ResolvedIPsFromContext(ctx); ok {
resolvedIPs := resolver.Resolve()
for _, rip := range resolvedIPs {
ips = append(ips, rip.IP())
}
}
var dest net.Destination
if m.onSource {
dest = sourceFromContext(ctx)
} else {
dest = targetFromContent(ctx)
}
if dest.IsValid() && (dest.Address.Family().IsIPv4() || dest.Address.Family().IsIPv6()) {
ips = append(ips, dest.Address.IP())
}
for _, ip := range ips {
for _, matcher := range m.matchers {
if matcher.Match(ip) {
@@ -181,21 +182,23 @@ func (v *PortMatcher) Apply(ctx context.Context) bool {
}
type NetworkMatcher struct {
network *net.NetworkList
list [8]bool
}
func NewNetworkMatcher(network *net.NetworkList) *NetworkMatcher {
return &NetworkMatcher{
network: network,
func NewNetworkMatcher(network []net.Network) NetworkMatcher {
var matcher NetworkMatcher
for _, n := range network {
matcher.list[int(n)] = true
}
return matcher
}
func (v *NetworkMatcher) Apply(ctx context.Context) bool {
func (v NetworkMatcher) Apply(ctx context.Context) bool {
outbound := session.OutboundFromContext(ctx)
if outbound == nil || !outbound.Target.IsValid() {
return false
}
return v.network.HasNetwork(outbound.Target.Network)
return v.list[int(outbound.Target.Network)]
}
type UserMatcher struct {

View File

@@ -285,3 +285,56 @@ func TestChinaSites(t *testing.T) {
assert(matcher.ApplyDomain(strconv.Itoa(i)+".not-exists.com"), IsFalse)
}
}
func BenchmarkMultiGeoIPMatcher(b *testing.B) {
common.Must(sysio.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(os.Getenv("GOPATH"), "src", "v2ray.com", "core", "release", "config", "geoip.dat")))
var geoips []*GeoIP
{
ips, err := loadGeoIP("CN")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "CN",
Cidr: ips,
})
}
{
ips, err := loadGeoIP("JP")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "JP",
Cidr: ips,
})
}
{
ips, err := loadGeoIP("CA")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "CA",
Cidr: ips,
})
}
{
ips, err := loadGeoIP("US")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "US",
Cidr: ips,
})
}
matcher, err := NewMultiGeoIPMatcher(geoips, false)
common.Must(err)
ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)})
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = matcher.Apply(ctx)
}
}

View File

@@ -2,6 +2,8 @@ package router
import (
"context"
"v2ray.com/core/features/outbound"
)
// CIDRList is an alias of []*CIDR to provide sort.Interface.
@@ -45,9 +47,17 @@ func (l *CIDRList) Swap(i int, j int) {
type Rule struct {
Tag string
Balancer *Balancer
Condition Condition
}
func (r *Rule) GetTag() (string, error) {
if r.Balancer != nil {
return r.Balancer.PickOutbound()
}
return r.Tag, nil
}
func (r *Rule) Apply(ctx context.Context) bool {
return r.Condition.Apply(ctx)
}
@@ -75,8 +85,10 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
conds.Add(NewPortMatcher(*rr.PortRange))
}
if rr.NetworkList != nil {
conds.Add(NewNetworkMatcher(rr.NetworkList))
if len(rr.Networks) > 0 {
conds.Add(NewNetworkMatcher(rr.Networks))
} else if rr.NetworkList != nil {
conds.Add(NewNetworkMatcher(rr.NetworkList.Network))
}
if len(rr.Geoip) > 0 {
@@ -117,3 +129,11 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
return conds, nil
}
func (br *BalancingRule) Build(ohm outbound.Manager) (*Balancer, error) {
return &Balancer{
selectors: br.OutboundSelector,
strategy: &RandomStrategy{},
ohm: ohm,
}, nil
}

View File

@@ -16,7 +16,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Type of domain value.
type Domain_Type int32
@@ -86,7 +86,7 @@ func (x Config_DomainStrategy) String() string {
}
func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_6b1608360690c5fc, []int{7, 0}
return fileDescriptor_6b1608360690c5fc, []int{8, 0}
}
// Domain for routing decision.
@@ -94,10 +94,11 @@ type Domain struct {
// Domain matching type.
Type Domain_Type `protobuf:"varint,1,opt,name=type,proto3,enum=v2ray.core.app.router.Domain_Type" json:"type,omitempty"`
// Domain value.
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
Attribute []*Domain_Attribute `protobuf:"bytes,3,rep,name=attribute,proto3" json:"attribute,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Domain) Reset() { *m = Domain{} }
@@ -139,6 +140,101 @@ func (m *Domain) GetValue() string {
return ""
}
func (m *Domain) GetAttribute() []*Domain_Attribute {
if m != nil {
return m.Attribute
}
return nil
}
type Domain_Attribute struct {
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// Types that are valid to be assigned to TypedValue:
// *Domain_Attribute_BoolValue
// *Domain_Attribute_IntValue
TypedValue isDomain_Attribute_TypedValue `protobuf_oneof:"typed_value"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Domain_Attribute) Reset() { *m = Domain_Attribute{} }
func (m *Domain_Attribute) String() string { return proto.CompactTextString(m) }
func (*Domain_Attribute) ProtoMessage() {}
func (*Domain_Attribute) Descriptor() ([]byte, []int) {
return fileDescriptor_6b1608360690c5fc, []int{0, 0}
}
func (m *Domain_Attribute) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Domain_Attribute.Unmarshal(m, b)
}
func (m *Domain_Attribute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Domain_Attribute.Marshal(b, m, deterministic)
}
func (m *Domain_Attribute) XXX_Merge(src proto.Message) {
xxx_messageInfo_Domain_Attribute.Merge(m, src)
}
func (m *Domain_Attribute) XXX_Size() int {
return xxx_messageInfo_Domain_Attribute.Size(m)
}
func (m *Domain_Attribute) XXX_DiscardUnknown() {
xxx_messageInfo_Domain_Attribute.DiscardUnknown(m)
}
var xxx_messageInfo_Domain_Attribute proto.InternalMessageInfo
func (m *Domain_Attribute) GetKey() string {
if m != nil {
return m.Key
}
return ""
}
type isDomain_Attribute_TypedValue interface {
isDomain_Attribute_TypedValue()
}
type Domain_Attribute_BoolValue struct {
BoolValue bool `protobuf:"varint,2,opt,name=bool_value,json=boolValue,proto3,oneof"`
}
type Domain_Attribute_IntValue struct {
IntValue int64 `protobuf:"varint,3,opt,name=int_value,json=intValue,proto3,oneof"`
}
func (*Domain_Attribute_BoolValue) isDomain_Attribute_TypedValue() {}
func (*Domain_Attribute_IntValue) isDomain_Attribute_TypedValue() {}
func (m *Domain_Attribute) GetTypedValue() isDomain_Attribute_TypedValue {
if m != nil {
return m.TypedValue
}
return nil
}
func (m *Domain_Attribute) GetBoolValue() bool {
if x, ok := m.GetTypedValue().(*Domain_Attribute_BoolValue); ok {
return x.BoolValue
}
return false
}
func (m *Domain_Attribute) GetIntValue() int64 {
if x, ok := m.GetTypedValue().(*Domain_Attribute_IntValue); ok {
return x.IntValue
}
return 0
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*Domain_Attribute) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*Domain_Attribute_BoolValue)(nil),
(*Domain_Attribute_IntValue)(nil),
}
}
// IP for routing decision, in CIDR form.
type CIDR struct {
// IP address, should be either 4 or 16 bytes.
@@ -362,8 +458,10 @@ func (m *GeoSiteList) GetEntry() []*GeoSite {
}
type RoutingRule struct {
// Tag of outbound that this rule is pointing to.
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
// Types that are valid to be assigned to TargetTag:
// *RoutingRule_Tag
// *RoutingRule_BalancingTag
TargetTag isRoutingRule_TargetTag `protobuf_oneof:"target_tag"`
// List of domains for target domain matching.
Domain []*Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"`
// List of CIDRs for target IP address matching.
@@ -372,9 +470,12 @@ type RoutingRule struct {
// 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 supposed to contain exactly same content. They will be merged during runtime.
// For customized GeoIPs, please leave country code empty.
Geoip []*GeoIP `protobuf:"bytes,10,rep,name=geoip,proto3" json:"geoip,omitempty"`
PortRange *net.PortRange `protobuf:"bytes,4,opt,name=port_range,json=portRange,proto3" json:"port_range,omitempty"`
NetworkList *net.NetworkList `protobuf:"bytes,5,opt,name=network_list,json=networkList,proto3" json:"network_list,omitempty"`
Geoip []*GeoIP `protobuf:"bytes,10,rep,name=geoip,proto3" json:"geoip,omitempty"`
PortRange *net.PortRange `protobuf:"bytes,4,opt,name=port_range,json=portRange,proto3" json:"port_range,omitempty"`
// List of networks. Deprecated. Use networks.
NetworkList *net.NetworkList `protobuf:"bytes,5,opt,name=network_list,json=networkList,proto3" json:"network_list,omitempty"` // Deprecated: Do not use.
// List of networks for matching.
Networks []net.Network `protobuf:"varint,13,rep,packed,name=networks,proto3,enum=v2ray.core.common.net.Network" json:"networks,omitempty"`
// List of CIDRs for source IP address matching.
SourceCidr []*CIDR `protobuf:"bytes,6,rep,name=source_cidr,json=sourceCidr,proto3" json:"source_cidr,omitempty"` // Deprecated: Do not use.
// List of GeoIPs for source IP address matching. If this entry exists, the source_cidr above will have no effect.
@@ -412,9 +513,39 @@ func (m *RoutingRule) XXX_DiscardUnknown() {
var xxx_messageInfo_RoutingRule proto.InternalMessageInfo
func (m *RoutingRule) GetTag() string {
type isRoutingRule_TargetTag interface {
isRoutingRule_TargetTag()
}
type RoutingRule_Tag struct {
Tag string `protobuf:"bytes,1,opt,name=tag,proto3,oneof"`
}
type RoutingRule_BalancingTag struct {
BalancingTag string `protobuf:"bytes,12,opt,name=balancing_tag,json=balancingTag,proto3,oneof"`
}
func (*RoutingRule_Tag) isRoutingRule_TargetTag() {}
func (*RoutingRule_BalancingTag) isRoutingRule_TargetTag() {}
func (m *RoutingRule) GetTargetTag() isRoutingRule_TargetTag {
if m != nil {
return m.Tag
return m.TargetTag
}
return nil
}
func (m *RoutingRule) GetTag() string {
if x, ok := m.GetTargetTag().(*RoutingRule_Tag); ok {
return x.Tag
}
return ""
}
func (m *RoutingRule) GetBalancingTag() string {
if x, ok := m.GetTargetTag().(*RoutingRule_BalancingTag); ok {
return x.BalancingTag
}
return ""
}
@@ -448,6 +579,7 @@ func (m *RoutingRule) GetPortRange() *net.PortRange {
return nil
}
// Deprecated: Do not use.
func (m *RoutingRule) GetNetworkList() *net.NetworkList {
if m != nil {
return m.NetworkList
@@ -455,6 +587,13 @@ func (m *RoutingRule) GetNetworkList() *net.NetworkList {
return nil
}
func (m *RoutingRule) GetNetworks() []net.Network {
if m != nil {
return m.Networks
}
return nil
}
// Deprecated: Do not use.
func (m *RoutingRule) GetSourceCidr() []*CIDR {
if m != nil {
@@ -491,9 +630,65 @@ func (m *RoutingRule) GetProtocol() []string {
return nil
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*RoutingRule) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*RoutingRule_Tag)(nil),
(*RoutingRule_BalancingTag)(nil),
}
}
type BalancingRule struct {
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"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BalancingRule) Reset() { *m = BalancingRule{} }
func (m *BalancingRule) String() string { return proto.CompactTextString(m) }
func (*BalancingRule) ProtoMessage() {}
func (*BalancingRule) Descriptor() ([]byte, []int) {
return fileDescriptor_6b1608360690c5fc, []int{7}
}
func (m *BalancingRule) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BalancingRule.Unmarshal(m, b)
}
func (m *BalancingRule) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BalancingRule.Marshal(b, m, deterministic)
}
func (m *BalancingRule) XXX_Merge(src proto.Message) {
xxx_messageInfo_BalancingRule.Merge(m, src)
}
func (m *BalancingRule) XXX_Size() int {
return xxx_messageInfo_BalancingRule.Size(m)
}
func (m *BalancingRule) XXX_DiscardUnknown() {
xxx_messageInfo_BalancingRule.DiscardUnknown(m)
}
var xxx_messageInfo_BalancingRule proto.InternalMessageInfo
func (m *BalancingRule) GetTag() string {
if m != nil {
return m.Tag
}
return ""
}
func (m *BalancingRule) GetOutboundSelector() []string {
if m != nil {
return m.OutboundSelector
}
return nil
}
type Config struct {
DomainStrategy Config_DomainStrategy `protobuf:"varint,1,opt,name=domain_strategy,json=domainStrategy,proto3,enum=v2ray.core.app.router.Config_DomainStrategy" json:"domain_strategy,omitempty"`
Rule []*RoutingRule `protobuf:"bytes,2,rep,name=rule,proto3" json:"rule,omitempty"`
BalancingRule []*BalancingRule `protobuf:"bytes,3,rep,name=balancing_rule,json=balancingRule,proto3" json:"balancing_rule,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -503,7 +698,7 @@ func (m *Config) Reset() { *m = Config{} }
func (m *Config) String() string { return proto.CompactTextString(m) }
func (*Config) ProtoMessage() {}
func (*Config) Descriptor() ([]byte, []int) {
return fileDescriptor_6b1608360690c5fc, []int{7}
return fileDescriptor_6b1608360690c5fc, []int{8}
}
func (m *Config) XXX_Unmarshal(b []byte) error {
@@ -538,16 +733,25 @@ func (m *Config) GetRule() []*RoutingRule {
return nil
}
func (m *Config) GetBalancingRule() []*BalancingRule {
if m != nil {
return m.BalancingRule
}
return nil
}
func init() {
proto.RegisterEnum("v2ray.core.app.router.Domain_Type", Domain_Type_name, Domain_Type_value)
proto.RegisterEnum("v2ray.core.app.router.Config_DomainStrategy", Config_DomainStrategy_name, Config_DomainStrategy_value)
proto.RegisterType((*Domain)(nil), "v2ray.core.app.router.Domain")
proto.RegisterType((*Domain_Attribute)(nil), "v2ray.core.app.router.Domain.Attribute")
proto.RegisterType((*CIDR)(nil), "v2ray.core.app.router.CIDR")
proto.RegisterType((*GeoIP)(nil), "v2ray.core.app.router.GeoIP")
proto.RegisterType((*GeoIPList)(nil), "v2ray.core.app.router.GeoIPList")
proto.RegisterType((*GeoSite)(nil), "v2ray.core.app.router.GeoSite")
proto.RegisterType((*GeoSiteList)(nil), "v2ray.core.app.router.GeoSiteList")
proto.RegisterType((*RoutingRule)(nil), "v2ray.core.app.router.RoutingRule")
proto.RegisterType((*BalancingRule)(nil), "v2ray.core.app.router.BalancingRule")
proto.RegisterType((*Config)(nil), "v2ray.core.app.router.Config")
}
@@ -556,49 +760,61 @@ func init() {
}
var fileDescriptor_6b1608360690c5fc = []byte{
// 691 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0xdd, 0x6e, 0xd3, 0x4c,
0x10, 0xfd, 0xec, 0xfc, 0xb4, 0x1e, 0xe7, 0x0b, 0xd6, 0x8a, 0x22, 0x53, 0x28, 0x04, 0x0b, 0x41,
0x2e, 0x90, 0x23, 0xa5, 0xc0, 0x1d, 0x2a, 0x6d, 0x5a, 0xa2, 0x48, 0x50, 0xa2, 0x6d, 0xcb, 0x05,
0x5c, 0x44, 0xae, 0xb3, 0x35, 0x16, 0xce, 0xee, 0x6a, 0xbd, 0x2e, 0xcd, 0x2b, 0xf0, 0x20, 0x5c,
0xf0, 0x54, 0x3c, 0x0a, 0xda, 0x9f, 0x40, 0x8b, 0x1a, 0x88, 0xb8, 0xdb, 0x59, 0x9f, 0x33, 0x73,
0xe6, 0x78, 0x76, 0xe0, 0xd1, 0x79, 0x5f, 0x24, 0xf3, 0x38, 0x65, 0xb3, 0x5e, 0xca, 0x04, 0xe9,
0x25, 0x9c, 0xf7, 0x04, 0xab, 0x24, 0x11, 0xbd, 0x94, 0xd1, 0xb3, 0x3c, 0x8b, 0xb9, 0x60, 0x92,
0xa1, 0x8d, 0x05, 0x4e, 0x90, 0x38, 0xe1, 0x3c, 0x36, 0x98, 0xcd, 0x87, 0xbf, 0xd1, 0x53, 0x36,
0x9b, 0x31, 0xda, 0xa3, 0x44, 0xf6, 0x38, 0x13, 0xd2, 0x90, 0x37, 0x1f, 0x2f, 0x47, 0x51, 0x22,
0x3f, 0x33, 0xf1, 0xc9, 0x00, 0xa3, 0x2f, 0x0e, 0x34, 0xf7, 0xd9, 0x2c, 0xc9, 0x29, 0x7a, 0x0e,
0x75, 0x39, 0xe7, 0x24, 0x74, 0x3a, 0x4e, 0xb7, 0xdd, 0x8f, 0xe2, 0x6b, 0xeb, 0xc7, 0x06, 0x1c,
0x1f, 0xcf, 0x39, 0xc1, 0x1a, 0x8f, 0x6e, 0x42, 0xe3, 0x3c, 0x29, 0x2a, 0x12, 0xba, 0x1d, 0xa7,
0xeb, 0x61, 0x13, 0x44, 0x7d, 0xa8, 0x2b, 0x0c, 0xf2, 0xa0, 0x31, 0x2e, 0x92, 0x9c, 0x06, 0xff,
0xa9, 0x23, 0x26, 0x19, 0xb9, 0x08, 0x1c, 0x04, 0x8b, 0xaa, 0x81, 0x8b, 0xd6, 0xa1, 0xfe, 0xaa,
0x2a, 0x8a, 0xa0, 0x16, 0xc5, 0x50, 0x1f, 0x8c, 0xf6, 0x31, 0x6a, 0x83, 0x9b, 0x73, 0xad, 0xa3,
0x85, 0xdd, 0x9c, 0xa3, 0x5b, 0xd0, 0xe4, 0x82, 0x9c, 0xe5, 0x17, 0xba, 0xc4, 0xff, 0xd8, 0x46,
0xd1, 0x07, 0x68, 0x0c, 0x09, 0x1b, 0x8d, 0xd1, 0x03, 0x68, 0xa5, 0xac, 0xa2, 0x52, 0xcc, 0x27,
0x29, 0x9b, 0x9a, 0x16, 0x3c, 0xec, 0xdb, 0xbb, 0x01, 0x9b, 0x12, 0xd4, 0x83, 0x7a, 0x9a, 0x4f,
0x45, 0xe8, 0x76, 0x6a, 0x5d, 0xbf, 0x7f, 0x67, 0x49, 0x77, 0xaa, 0x3c, 0xd6, 0xc0, 0x68, 0x07,
0x3c, 0x9d, 0xfc, 0x75, 0x5e, 0x4a, 0xd4, 0x87, 0x06, 0x51, 0xa9, 0x42, 0x47, 0xd3, 0xef, 0x2e,
0xa1, 0x6b, 0x02, 0x36, 0xd0, 0x28, 0x85, 0xb5, 0x21, 0x61, 0x47, 0xb9, 0x24, 0xab, 0xe8, 0x7b,
0x06, 0xcd, 0xa9, 0x76, 0xc4, 0x2a, 0xdc, 0xfa, 0xa3, 0xff, 0xd8, 0x82, 0xa3, 0x01, 0xf8, 0xb6,
0x88, 0xd6, 0xf9, 0xf4, 0xaa, 0xce, 0x7b, 0xcb, 0x75, 0x2a, 0xca, 0x42, 0xe9, 0xd7, 0x3a, 0xf8,
0x98, 0x55, 0x32, 0xa7, 0x19, 0xae, 0x0a, 0x82, 0x02, 0xa8, 0xc9, 0x24, 0xb3, 0x2a, 0xd5, 0xf1,
0x1f, 0xd5, 0xa1, 0x6d, 0x6b, 0x7a, 0xed, 0xaf, 0xa6, 0xef, 0xb9, 0xa1, 0x63, 0x8c, 0x57, 0x5e,
0x67, 0x84, 0xe5, 0x3c, 0x84, 0x55, 0xbc, 0xd6, 0x50, 0xb4, 0x03, 0xa0, 0xa6, 0x7f, 0x22, 0x12,
0x9a, 0x91, 0xb0, 0xde, 0x71, 0xba, 0x7e, 0xbf, 0x73, 0x99, 0x68, 0x1e, 0x40, 0x4c, 0x89, 0x8c,
0xc7, 0x4c, 0x48, 0xac, 0x70, 0xd8, 0xe3, 0x8b, 0x23, 0x3a, 0x80, 0x96, 0x7d, 0x18, 0x93, 0x22,
0x2f, 0x65, 0xd8, 0xd0, 0x29, 0xa2, 0x25, 0x29, 0x0e, 0x0d, 0x54, 0x59, 0x8e, 0x7d, 0xfa, 0x2b,
0x40, 0x2f, 0xc1, 0x2f, 0x59, 0x25, 0x52, 0x32, 0xd1, 0x7d, 0x37, 0x57, 0xeb, 0x1b, 0x0c, 0x67,
0xa0, 0xba, 0xdf, 0x81, 0x96, 0xcd, 0x60, 0x4c, 0xf0, 0x57, 0x30, 0xc1, 0xd6, 0x1c, 0x6a, 0x2b,
0xb6, 0x00, 0xaa, 0x92, 0x88, 0x09, 0x99, 0x25, 0x79, 0x11, 0xae, 0x75, 0x6a, 0x5d, 0x0f, 0x7b,
0xea, 0xe6, 0x40, 0x5d, 0xa0, 0xfb, 0xe0, 0xe7, 0xf4, 0x94, 0x55, 0x74, 0x3a, 0x51, 0xff, 0x78,
0x5d, 0x7f, 0x07, 0x7b, 0x75, 0x9c, 0x64, 0x68, 0x13, 0xd6, 0xf5, 0x6a, 0x48, 0x59, 0x11, 0x7a,
0xfa, 0xeb, 0xcf, 0x38, 0xfa, 0xee, 0x40, 0x73, 0xa0, 0x97, 0x14, 0x3a, 0x81, 0x1b, 0xe6, 0x27,
0x4f, 0x4a, 0x29, 0x12, 0x49, 0xb2, 0xb9, 0x5d, 0x1c, 0x4f, 0x96, 0x75, 0x6b, 0x96, 0x9b, 0x99,
0x90, 0x23, 0xcb, 0xc1, 0xed, 0xe9, 0x95, 0x58, 0x2d, 0x21, 0x51, 0x15, 0xc4, 0x8e, 0xd9, 0xb2,
0x25, 0x74, 0x69, 0x58, 0xb1, 0xc6, 0x47, 0x43, 0x68, 0x5f, 0xcd, 0xac, 0xd6, 0xca, 0x6e, 0x39,
0x2a, 0xcd, 0xde, 0x39, 0x29, 0xc9, 0x88, 0x07, 0x0e, 0x0a, 0xa0, 0x35, 0xe2, 0xa3, 0xb3, 0x43,
0x46, 0xdf, 0x24, 0x32, 0xfd, 0x18, 0xb8, 0xa8, 0x0d, 0x30, 0xe2, 0x6f, 0xe9, 0x3e, 0x99, 0x25,
0x74, 0x1a, 0xd4, 0xf6, 0x5e, 0xc0, 0xed, 0x94, 0xcd, 0xae, 0xaf, 0x3b, 0x76, 0xde, 0x37, 0xcd,
0xe9, 0x9b, 0xbb, 0xf1, 0xae, 0x8f, 0x93, 0x79, 0x3c, 0x50, 0x88, 0x5d, 0xce, 0xb5, 0x24, 0x22,
0x4e, 0x9b, 0xda, 0xab, 0xed, 0x1f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x42, 0x5b, 0xf4, 0x78, 0xe6,
0x05, 0x00, 0x00,
// 881 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xef, 0x6e, 0x1b, 0x45,
0x10, 0xcf, 0xdd, 0xd9, 0xae, 0x6f, 0xce, 0x31, 0xc7, 0x8a, 0xa2, 0x23, 0x10, 0x6a, 0x4e, 0x85,
0x5a, 0x02, 0x9d, 0x25, 0x17, 0xf8, 0x80, 0x84, 0x42, 0xe2, 0x94, 0xc4, 0x02, 0x42, 0xb4, 0x69,
0xfb, 0x01, 0x3e, 0x58, 0xe7, 0xf3, 0xe6, 0x58, 0xf5, 0xbc, 0xbb, 0xda, 0xdb, 0x2b, 0xf5, 0xc3,
0xf0, 0x02, 0x48, 0x3c, 0x03, 0xaf, 0x86, 0xf6, 0x8f, 0xe3, 0x18, 0xea, 0x12, 0xf5, 0xdb, 0xee,
0xcc, 0x6f, 0x66, 0x7f, 0xfb, 0x9b, 0x99, 0x5d, 0xf8, 0xec, 0xe5, 0x58, 0xe6, 0xab, 0xac, 0xe0,
0xcb, 0x51, 0xc1, 0x25, 0x19, 0xe5, 0x42, 0x8c, 0x24, 0x6f, 0x14, 0x91, 0xa3, 0x82, 0xb3, 0x6b,
0x5a, 0x66, 0x42, 0x72, 0xc5, 0xd1, 0xfd, 0x35, 0x4e, 0x92, 0x2c, 0x17, 0x22, 0xb3, 0x98, 0x83,
0x87, 0xff, 0x0a, 0x2f, 0xf8, 0x72, 0xc9, 0xd9, 0x88, 0x11, 0x35, 0x12, 0x5c, 0x2a, 0x1b, 0x7c,
0xf0, 0x68, 0x37, 0x8a, 0x11, 0xf5, 0x3b, 0x97, 0x2f, 0x2c, 0x30, 0xfd, 0xdb, 0x87, 0xce, 0x29,
0x5f, 0xe6, 0x94, 0xa1, 0xaf, 0xa1, 0xa5, 0x56, 0x82, 0x24, 0xde, 0xc0, 0x1b, 0xf6, 0xc7, 0x69,
0xf6, 0xda, 0xf3, 0x33, 0x0b, 0xce, 0x9e, 0xae, 0x04, 0xc1, 0x06, 0x8f, 0xde, 0x83, 0xf6, 0xcb,
0xbc, 0x6a, 0x48, 0xe2, 0x0f, 0xbc, 0x61, 0x88, 0xed, 0x06, 0x3d, 0x81, 0x30, 0x57, 0x4a, 0xd2,
0x79, 0xa3, 0x48, 0x12, 0x0c, 0x82, 0x61, 0x34, 0x7e, 0xf4, 0xe6, 0x94, 0xc7, 0x6b, 0x38, 0xde,
0x44, 0x1e, 0x54, 0x10, 0xde, 0xd8, 0x51, 0x0c, 0xc1, 0x0b, 0xb2, 0x32, 0x04, 0x43, 0xac, 0x97,
0xe8, 0x01, 0xc0, 0x9c, 0xf3, 0x6a, 0xb6, 0x21, 0xd0, 0x3d, 0xdf, 0xc3, 0xa1, 0xb6, 0x3d, 0x37,
0x34, 0x0e, 0x21, 0xa4, 0x4c, 0x39, 0x7f, 0x30, 0xf0, 0x86, 0xc1, 0xf9, 0x1e, 0xee, 0x52, 0xa6,
0x8c, 0xfb, 0x64, 0x1f, 0x22, 0x7d, 0x87, 0x85, 0x05, 0xa4, 0x63, 0x68, 0xe9, 0x8b, 0xa1, 0x10,
0xda, 0x97, 0x55, 0x4e, 0x59, 0xbc, 0xa7, 0x97, 0x98, 0x94, 0xe4, 0x55, 0xec, 0x21, 0x58, 0x4b,
0x15, 0xfb, 0xa8, 0x0b, 0xad, 0xef, 0x9b, 0xaa, 0x8a, 0x83, 0x34, 0x83, 0xd6, 0x64, 0x7a, 0x8a,
0x51, 0x1f, 0x7c, 0x2a, 0x0c, 0xb7, 0x1e, 0xf6, 0xa9, 0x40, 0xef, 0x43, 0x47, 0x48, 0x72, 0x4d,
0x5f, 0x19, 0x5a, 0xfb, 0xd8, 0xed, 0xd2, 0x5f, 0xa1, 0x7d, 0x46, 0xf8, 0xf4, 0x12, 0x7d, 0x02,
0xbd, 0x82, 0x37, 0x4c, 0xc9, 0xd5, 0xac, 0xe0, 0x0b, 0xe2, 0xae, 0x15, 0x39, 0xdb, 0x84, 0x2f,
0x08, 0x1a, 0x41, 0xab, 0xa0, 0x0b, 0x99, 0xf8, 0x46, 0xbf, 0x0f, 0x77, 0xe8, 0xa7, 0x8f, 0xc7,
0x06, 0x98, 0x1e, 0x41, 0x68, 0x92, 0xff, 0x48, 0x6b, 0x85, 0xc6, 0xd0, 0x26, 0x3a, 0x55, 0xe2,
0x99, 0xf0, 0x8f, 0x76, 0x84, 0x9b, 0x00, 0x6c, 0xa1, 0x69, 0x01, 0xf7, 0xce, 0x08, 0xbf, 0xa2,
0x8a, 0xdc, 0x85, 0xdf, 0x57, 0xd0, 0x59, 0x18, 0x45, 0x1c, 0xc3, 0xc3, 0x37, 0x56, 0x18, 0x3b,
0x70, 0x3a, 0x81, 0xc8, 0x1d, 0x62, 0x78, 0x7e, 0xb9, 0xcd, 0xf3, 0xe3, 0xdd, 0x3c, 0x75, 0xc8,
0x9a, 0xe9, 0x1f, 0x6d, 0x88, 0x30, 0x6f, 0x14, 0x65, 0x25, 0x6e, 0x2a, 0x82, 0x10, 0x04, 0x2a,
0x2f, 0x2d, 0xcb, 0xf3, 0x3d, 0xac, 0x37, 0xe8, 0x53, 0xd8, 0x9f, 0xe7, 0x55, 0xce, 0x0a, 0xca,
0xca, 0x99, 0xf6, 0xf6, 0x9c, 0xb7, 0x77, 0x63, 0x7e, 0x9a, 0x97, 0x6f, 0x79, 0x0d, 0xf4, 0xd8,
0x55, 0x27, 0xf8, 0xdf, 0xea, 0x9c, 0xf8, 0x89, 0x67, 0x2b, 0xa4, 0x8b, 0x52, 0x12, 0x4e, 0x45,
0x02, 0x77, 0x29, 0x8a, 0x81, 0xa2, 0x23, 0x00, 0x3d, 0xdb, 0x33, 0x99, 0xb3, 0x92, 0x24, 0xad,
0x81, 0x37, 0x8c, 0xc6, 0x83, 0xdb, 0x81, 0x76, 0xbc, 0x33, 0x46, 0x54, 0x76, 0xc9, 0xa5, 0xc2,
0x1a, 0x87, 0x43, 0xb1, 0x5e, 0xa2, 0x29, 0xf4, 0xdc, 0xd8, 0xcf, 0x2a, 0x5a, 0xab, 0xa4, 0x6d,
0x52, 0xa4, 0x3b, 0x52, 0x5c, 0x58, 0xa8, 0xae, 0x8d, 0x21, 0x1e, 0xb1, 0x8d, 0x01, 0x7d, 0x03,
0x5d, 0xb7, 0xad, 0x93, 0xfd, 0x41, 0x30, 0xec, 0x6f, 0xd7, 0xeb, 0xbf, 0x69, 0xf0, 0x0d, 0x1e,
0x7d, 0x07, 0x51, 0xcd, 0x1b, 0x59, 0x90, 0x99, 0xd1, 0xad, 0x73, 0x37, 0xdd, 0xc0, 0xc6, 0x4c,
0xb4, 0x7a, 0x47, 0xd0, 0x73, 0x19, 0xac, 0x88, 0xd1, 0x1d, 0x44, 0x74, 0x67, 0x9e, 0x19, 0x29,
0x0f, 0x01, 0x9a, 0x9a, 0xc8, 0x19, 0x59, 0xe6, 0xb4, 0x4a, 0xee, 0x0d, 0x82, 0x61, 0x88, 0x43,
0x6d, 0x79, 0xa2, 0x0d, 0xe8, 0x01, 0x44, 0x94, 0xcd, 0x79, 0xc3, 0x16, 0xa6, 0x5d, 0xba, 0xc6,
0x0f, 0xce, 0xa4, 0x5b, 0xe5, 0x00, 0xba, 0xe6, 0xe1, 0x2c, 0x78, 0x95, 0x84, 0xc6, 0x7b, 0xb3,
0x3f, 0xe9, 0x01, 0xa8, 0x5c, 0x96, 0x44, 0xe9, 0xd8, 0xf4, 0x02, 0xf6, 0x4f, 0xd6, 0x4d, 0x66,
0x1a, 0x34, 0xbe, 0xd5, 0xa0, 0xb6, 0x3d, 0x3f, 0x87, 0x77, 0x79, 0xa3, 0xec, 0x71, 0x35, 0xa9,
0x48, 0xa1, 0xb8, 0x9d, 0xf5, 0x10, 0xc7, 0x6b, 0xc7, 0x95, 0xb3, 0xa7, 0x7f, 0xf9, 0xd0, 0x99,
0x98, 0x0f, 0x02, 0x3d, 0x83, 0x77, 0x6c, 0x0b, 0xce, 0x6a, 0x25, 0x73, 0x45, 0xca, 0x95, 0x7b,
0xb4, 0xbf, 0xd8, 0xa5, 0xa5, 0xfd, 0x58, 0x6c, 0xff, 0x5e, 0xb9, 0x18, 0xdc, 0x5f, 0x6c, 0xed,
0xf5, 0x07, 0x20, 0x9b, 0x8a, 0xb8, 0x21, 0xd8, 0xf5, 0x01, 0xdc, 0x9a, 0x39, 0x6c, 0xf0, 0xe8,
0x07, 0xe8, 0x6f, 0xa6, 0xcc, 0x64, 0xb0, 0x13, 0xf1, 0x70, 0x47, 0x86, 0x2d, 0x59, 0xf0, 0x66,
0x42, 0xf5, 0x36, 0x3d, 0x83, 0xfe, 0x36, 0x4d, 0xfd, 0xd4, 0x1e, 0xd7, 0xd3, 0xda, 0xbe, 0xc5,
0xcf, 0x6a, 0x32, 0x15, 0xb1, 0x87, 0x62, 0xe8, 0x4d, 0xc5, 0xf4, 0xfa, 0x82, 0xb3, 0x9f, 0x72,
0x55, 0xfc, 0x16, 0xfb, 0xa8, 0x0f, 0x30, 0x15, 0x3f, 0xb3, 0x53, 0xb2, 0xcc, 0xd9, 0x22, 0x0e,
0x4e, 0xbe, 0x85, 0x0f, 0x0a, 0xbe, 0x7c, 0x3d, 0x85, 0x4b, 0xef, 0x97, 0x8e, 0x5d, 0xfd, 0xe9,
0xdf, 0x7f, 0x3e, 0xc6, 0xf9, 0x2a, 0x9b, 0x68, 0xc4, 0xb1, 0x10, 0xe6, 0x7e, 0x44, 0xce, 0x3b,
0xa6, 0xac, 0x8f, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x9b, 0x8e, 0x85, 0x22, 0xaf, 0x07, 0x00,
0x00,
}

View File

@@ -28,6 +28,18 @@ message Domain {
// Domain value.
string value = 2;
message Attribute {
string key = 1;
oneof typed_value {
bool bool_value = 2;
int64 int_value = 3;
}
}
// Attributes of this domain. May be used for filtering.
repeated Attribute attribute = 3;
}
// IP for routing decision, in CIDR form.
@@ -58,8 +70,14 @@ message GeoSiteList{
}
message RoutingRule {
// Tag of outbound that this rule is pointing to.
string tag = 1;
oneof target_tag {
// Tag of outbound that this rule is pointing to.
string tag = 1;
// Tag of routing balancer.
string balancing_tag = 12;
}
// List of domains for target domain matching.
repeated Domain domain = 2;
@@ -74,7 +92,12 @@ message RoutingRule {
repeated GeoIP geoip = 10;
v2ray.core.common.net.PortRange port_range = 4;
v2ray.core.common.net.NetworkList network_list = 5;
// List of networks. Deprecated. Use networks.
v2ray.core.common.net.NetworkList network_list = 5 [deprecated = true];
// List of networks for matching.
repeated v2ray.core.common.net.Network networks = 13;
// List of CIDRs for source IP address matching.
repeated CIDR source_cidr = 6 [deprecated = true];
@@ -87,6 +110,11 @@ message RoutingRule {
repeated string protocol = 9;
}
message BalancingRule {
string tag = 1;
repeated string outbound_selector = 2;
}
message Config {
enum DomainStrategy {
// Use domain as is.
@@ -103,4 +131,5 @@ message Config {
}
DomainStrategy domain_strategy = 1;
repeated RoutingRule rule = 2;
repeated BalancingRule balancing_rule = 3;
}

View File

@@ -10,6 +10,7 @@ import (
"v2ray.com/core/common/net"
"v2ray.com/core/common/session"
"v2ray.com/core/features/dns"
"v2ray.com/core/features/outbound"
"v2ray.com/core/features/routing"
)
@@ -35,8 +36,8 @@ func ResolvedIPsFromContext(ctx context.Context) (IPResolver, bool) {
func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
r := new(Router)
if err := core.RequireFeatures(ctx, func(d dns.Client) error {
return r.Init(config.(*Config), d)
if err := core.RequireFeatures(ctx, func(d dns.Client, ohm outbound.Manager) error {
return r.Init(config.(*Config), d, ohm)
}); err != nil {
return nil, err
}
@@ -47,23 +48,44 @@ func init() {
// Router is an implementation of routing.Router.
type Router struct {
domainStrategy Config_DomainStrategy
rules []Rule
rules []*Rule
balancers map[string]*Balancer
dns dns.Client
}
// Init initializes the Router.
func (r *Router) Init(config *Config, d dns.Client) error {
func (r *Router) Init(config *Config, d dns.Client, ohm outbound.Manager) error {
r.domainStrategy = config.DomainStrategy
r.rules = make([]Rule, len(config.Rule))
r.dns = d
for idx, rule := range config.Rule {
r.rules[idx].Tag = rule.Tag
r.balancers = make(map[string]*Balancer, len(config.BalancingRule))
for _, rule := range config.BalancingRule {
balancer, err := rule.Build(ohm)
if err != nil {
return err
}
r.balancers[rule.Tag] = balancer
}
r.rules = make([]*Rule, 0, len(config.Rule))
for _, rule := range config.Rule {
cond, err := rule.BuildCondition()
if err != nil {
return err
}
r.rules[idx].Condition = cond
rr := &Rule{
Condition: cond,
Tag: rule.GetTag(),
}
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
@@ -97,8 +119,16 @@ func (r *ipResolver) Resolve() []net.Address {
return r.ip
}
// PickRoute implements routing.Router.
func (r *Router) PickRoute(ctx context.Context) (string, error) {
rule, err := r.pickRouteInternal(ctx)
if err != nil {
return "", err
}
return rule.GetTag()
}
// PickRoute implements routing.Router.
func (r *Router) pickRouteInternal(ctx context.Context) (*Rule, error) {
resolver := &ipResolver{
dns: r.dns,
}
@@ -113,12 +143,12 @@ func (r *Router) PickRoute(ctx context.Context) (string, error) {
for _, rule := range r.rules {
if rule.Apply(ctx) {
return rule.Tag, nil
return rule, nil
}
}
if outbound == nil || !outbound.Target.IsValid() {
return "", common.ErrNoClue
return nil, common.ErrNoClue
}
dest := outbound.Target
@@ -129,13 +159,13 @@ func (r *Router) PickRoute(ctx context.Context) (string, error) {
ctx = ContextWithResolveIPs(ctx, resolver)
for _, rule := range r.rules {
if rule.Apply(ctx) {
return rule.Tag, nil
return rule, nil
}
}
}
}
return "", common.ErrNoClue
return nil, common.ErrNoClue
}
// Start implements common.Runnable.

View File

@@ -8,17 +8,23 @@ import (
"v2ray.com/core/common"
"v2ray.com/core/common/net"
"v2ray.com/core/common/session"
"v2ray.com/core/features/outbound"
"v2ray.com/core/testing/mocks"
)
type mockOutboundManager struct {
outbound.Manager
outbound.HandlerSelector
}
func TestSimpleRouter(t *testing.T) {
config := &Config{
Rule: []*RoutingRule{
{
Tag: "test",
NetworkList: &net.NetworkList{
Network: []net.Network{net.Network_TCP},
TargetTag: &RoutingRule_Tag{
Tag: "test",
},
Networks: []net.Network{net.Network_TCP},
},
},
}
@@ -27,9 +33,55 @@ func TestSimpleRouter(t *testing.T) {
defer mockCtl.Finish()
mockDns := mocks.NewDNSClient(mockCtl)
mockOhm := mocks.NewOutboundManager(mockCtl)
mockHs := mocks.NewOutboundHandlerSelector(mockCtl)
r := new(Router)
common.Must(r.Init(config, mockDns))
common.Must(r.Init(config, mockDns, &mockOutboundManager{
Manager: mockOhm,
HandlerSelector: mockHs,
}))
ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)})
tag, err := r.PickRoute(ctx)
common.Must(err)
if tag != "test" {
t.Error("expect tag 'test', bug actually ", tag)
}
}
func TestSimpleBalancer(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-"},
},
},
}
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{"test"})
r := new(Router)
common.Must(r.Init(config, mockDns, &mockOutboundManager{
Manager: mockOhm,
HandlerSelector: mockHs,
}))
ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)})
tag, err := r.PickRoute(ctx)
@@ -44,7 +96,9 @@ func TestIPOnDemand(t *testing.T) {
DomainStrategy: Config_IpOnDemand,
Rule: []*RoutingRule{
{
Tag: "test",
TargetTag: &RoutingRule_Tag{
Tag: "test",
},
Cidr: []*CIDR{
{
Ip: []byte{192, 168, 0, 0},
@@ -62,7 +116,7 @@ func TestIPOnDemand(t *testing.T) {
mockDns.EXPECT().LookupIP(gomock.Eq("v2ray.com")).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
r := new(Router)
common.Must(r.Init(config, mockDns))
common.Must(r.Init(config, mockDns, nil))
ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)})
tag, err := r.PickRoute(ctx)

View File

@@ -17,7 +17,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type GetStatsRequest struct {
// Name of the stat counter.

View File

@@ -15,7 +15,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Config struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`

View File

@@ -1,34 +0,0 @@
image: Visual Studio 2017
platform:
- x64
- x86
branches:
only:
- master
version: "1.0.{build}"
environment:
global:
GOPATH: C:\gopath
GOVERSION: "1.11.2"
install:
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
- rmdir c:\go /s /q
- appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.zip
- 7z x go%GOVERSION%.windows-amd64.zip -y -oC:\ > NUL
- go version
- go get -t v2ray.com/core/...
- go get -t v2ray.com/ext/...
build_script:
- go build v2ray.com/core/main
- go build v2ray.com/ext/tools/control/main
build: off
test_script:
- go test -p 1 -v v2ray.com/core/...

View File

@@ -0,0 +1,25 @@
jobs:
- job: ${{ parameters.name }}
timeoutInMinutes: 30
pool:
vmImage: ${{ parameters.vmImage }}
variables:
GOPATH: '$(system.defaultWorkingDirectory)'
steps:
- checkout: none
- task: GoTool@0
inputs:
version: '1.11.2'
- script: |
go version
go get -v -t -d v2ray.com/core/...
go get -v -t -d v2ray.com/ext/...
workingDirectory: '$(system.defaultWorkingDirectory)'
displayName: 'Fetch sources'
- script: |
go test -p 1 -timeout 30m -v v2ray.com/core/...
workingDirectory: '$(system.defaultWorkingDirectory)'
displayName: 'Test'

41
azure-pipelines.yml Normal file
View File

@@ -0,0 +1,41 @@
jobs:
- template: azure-pipelines.template.yml
parameters:
name: linux
vmImage: 'ubuntu-16.04'
- template: azure-pipelines.template.yml
parameters:
name: windows
vmImage: 'vs2017-win2016'
- template: azure-pipelines.template.yml
parameters:
name: macos
vmImage: 'macOS-10.13'
- job: linux_coverage
dependsOn: linux
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
timeoutInMinutes: 30
pool:
vmImage: 'Ubuntu 16.04'
variables:
GOPATH: '$(system.defaultWorkingDirectory)'
CODECOV_TOKEN: '$(coverage.token)'
steps:
- checkout: none
- script: |
go version
go get -v -t -d v2ray.com/core/...
go get -v -t -d v2ray.com/ext/...
workingDirectory: '$(system.defaultWorkingDirectory)'
displayName: 'Fetch sources'
- script: |
cd ./src/v2ray.com/core
bash ./testing/coverage/coverall
workingDirectory: '$(system.defaultWorkingDirectory)'
displayName: 'Coverage'

View File

@@ -25,9 +25,11 @@ func (b *Buffer) Release() {
if b == nil || b.v == nil {
return
}
pool.Put(b.v)
p := b.v
b.v = nil
b.Clear()
pool.Put(p)
}
// Clear clears the content of the buffer, results an empty buffer with
@@ -57,7 +59,7 @@ func (b *Buffer) Bytes() []byte {
func (b *Buffer) Extend(n int32) []byte {
end := b.end + n
if end > int32(len(b.v)) {
panic(newError("out of bound: ", end))
panic("extending out of bound")
}
ext := b.v[b.end:end]
b.end = end
@@ -139,9 +141,14 @@ func (b *Buffer) Write(data []byte) (int, error) {
return nBytes, nil
}
// WriteBytes appends one or more bytes to the end of the buffer.
func (b *Buffer) WriteBytes(bytes ...byte) (int, error) {
return b.Write(bytes)
// WriteByte writes a single byte into the buffer.
func (b *Buffer) WriteByte(v byte) error {
if b.IsFull() {
return newError("buffer full")
}
b.v[b.end] = v
b.end++
return nil
}
// WriteString implements io.StringWriter.
@@ -174,7 +181,8 @@ func (b *Buffer) ReadFrom(reader io.Reader) (int64, error) {
func (b *Buffer) ReadFullFrom(reader io.Reader, size int32) (int64, error) {
end := b.end + size
if end > int32(len(b.v)) {
return 0, newError("out of bound: ", end)
v := end
return 0, newError("out of bound: ", v)
}
n, err := io.ReadFull(reader, b.v[b.end:end])
b.end += int32(n)
@@ -194,3 +202,11 @@ func New() *Buffer {
v: pool.Get().([]byte),
}
}
// StackNew creates a new Buffer object on stack.
// This method is for buffers that is released in the same function.
func StackNew() Buffer {
return Buffer{
v: pool.Get().([]byte),
}
}

View File

@@ -5,43 +5,45 @@ import (
"crypto/rand"
"testing"
"github.com/google/go-cmp/cmp"
"v2ray.com/core/common"
. "v2ray.com/core/common/buf"
"v2ray.com/core/common/compare"
. "v2ray.com/ext/assert"
)
func TestBufferClear(t *testing.T) {
assert := With(t)
buffer := New()
defer buffer.Release()
payload := "Bytes"
buffer.Write([]byte(payload))
assert(buffer.Len(), Equals, int32(len(payload)))
if diff := cmp.Diff(buffer.Bytes(), []byte(payload)); diff != "" {
t.Error(diff)
}
buffer.Clear()
assert(buffer.Len(), Equals, int32(0))
if buffer.Len() != 0 {
t.Error("expect 0 lenght, but got ", buffer.Len())
}
}
func TestBufferIsEmpty(t *testing.T) {
assert := With(t)
buffer := New()
defer buffer.Release()
assert(buffer.IsEmpty(), IsTrue)
if buffer.IsEmpty() != true {
t.Error("expect empty buffer, but not")
}
}
func TestBufferString(t *testing.T) {
assert := With(t)
buffer := New()
defer buffer.Release()
common.Must2(buffer.WriteString("Test String"))
assert(buffer.String(), Equals, "Test String")
const payload = "Test String"
common.Must2(buffer.WriteString(payload))
if buffer.String() != payload {
t.Error("expect buffer content as ", payload, " but actually ", buffer.String())
}
}
func TestBufferSlice(t *testing.T) {
@@ -49,8 +51,8 @@ func TestBufferSlice(t *testing.T) {
b := New()
common.Must2(b.Write([]byte("abcd")))
bytes := b.BytesFrom(-2)
if err := compare.BytesEqualWithDetail(bytes, []byte{'c', 'd'}); err != nil {
t.Error(err)
if diff := cmp.Diff(bytes, []byte{'c', 'd'}); diff != "" {
t.Error(diff)
}
}
@@ -58,8 +60,8 @@ func TestBufferSlice(t *testing.T) {
b := New()
common.Must2(b.Write([]byte("abcd")))
bytes := b.BytesTo(-2)
if err := compare.BytesEqualWithDetail(bytes, []byte{'a', 'b'}); err != nil {
t.Error(err)
if diff := cmp.Diff(bytes, []byte{'a', 'b'}); diff != "" {
t.Error(diff)
}
}
@@ -67,8 +69,8 @@ func TestBufferSlice(t *testing.T) {
b := New()
common.Must2(b.Write([]byte("abcd")))
bytes := b.BytesRange(-3, -1)
if err := compare.BytesEqualWithDetail(bytes, []byte{'b', 'c'}); err != nil {
t.Error(err)
if diff := cmp.Diff(bytes, []byte{'b', 'c'}); diff != "" {
t.Error(diff)
}
}
}
@@ -85,8 +87,8 @@ func TestBufferReadFullFrom(t *testing.T) {
t.Error("expect reading 1024 bytes, but actually ", n)
}
if err := compare.BytesEqualWithDetail(payload, b.Bytes()); err != nil {
t.Error(err)
if diff := cmp.Diff(payload, b.Bytes()); diff != "" {
t.Error(diff)
}
}
@@ -96,3 +98,70 @@ func BenchmarkNewBuffer(b *testing.B) {
buffer.Release()
}
}
func BenchmarkNewBufferStack(b *testing.B) {
for i := 0; i < b.N; i++ {
buffer := StackNew()
buffer.Release()
}
}
func BenchmarkWrite2(b *testing.B) {
buffer := New()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = buffer.Write([]byte{'a', 'b'})
buffer.Clear()
}
}
func BenchmarkWrite8(b *testing.B) {
buffer := New()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = buffer.Write([]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'})
buffer.Clear()
}
}
func BenchmarkWrite32(b *testing.B) {
buffer := New()
payload := make([]byte, 32)
rand.Read(payload)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = buffer.Write(payload)
buffer.Clear()
}
}
func BenchmarkWriteByte2(b *testing.B) {
buffer := New()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = buffer.WriteByte('a')
_ = buffer.WriteByte('b')
buffer.Clear()
}
}
func BenchmarkWriteByte8(b *testing.B) {
buffer := New()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = buffer.WriteByte('a')
_ = buffer.WriteByte('b')
_ = buffer.WriteByte('c')
_ = buffer.WriteByte('d')
_ = buffer.WriteByte('e')
_ = buffer.WriteByte('f')
_ = buffer.WriteByte('g')
_ = buffer.WriteByte('h')
buffer.Clear()
}
}

View File

@@ -8,33 +8,10 @@ import (
"v2ray.com/core/common/signal"
)
type errorHandler func(error) error
type dataHandler func(MultiBuffer)
type copyHandler struct {
onReadError []errorHandler
onData []dataHandler
onWriteError []errorHandler
}
func (h *copyHandler) readFrom(reader Reader) (MultiBuffer, error) {
mb, err := reader.ReadMultiBuffer()
if err != nil {
for _, handler := range h.onReadError {
err = handler(err)
}
}
return mb, err
}
func (h *copyHandler) writeTo(writer Writer, mb MultiBuffer) error {
err := writer.WriteMultiBuffer(mb)
if err != nil {
for _, handler := range h.onWriteError {
err = handler(err)
}
}
return err
onData []dataHandler
}
// SizeCounter is for counting bytes copied by Copy().
@@ -99,13 +76,13 @@ func IsWriteError(err error) bool {
func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
for {
buffer, err := handler.readFrom(reader)
buffer, err := reader.ReadMultiBuffer()
if !buffer.IsEmpty() {
for _, handler := range handler.onData {
handler(buffer)
}
if werr := handler.writeTo(writer, buffer); werr != nil {
if werr := writer.WriteMultiBuffer(buffer); werr != nil {
return writeError{werr}
}
}

View File

@@ -2,6 +2,7 @@ package buf_test
import (
"crypto/rand"
"io"
"testing"
"github.com/golang/mock/gomock"
@@ -52,3 +53,19 @@ func TestWriteError(t *testing.T) {
t.Fatal("unexpected error message: ", err.Error())
}
}
type TestReader struct{}
func (TestReader) Read(b []byte) (int, error) {
return len(b), nil
}
func BenchmarkCopy(b *testing.B) {
reader := buf.NewReader(io.LimitReader(TestReader{}, 10240))
writer := buf.Discard
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = buf.Copy(reader, writer)
}
}

View File

@@ -2,6 +2,7 @@ package buf
import (
"io"
"net"
"syscall"
"time"
)
@@ -38,6 +39,11 @@ func WriteAllBytes(writer io.Writer, payload []byte) error {
return nil
}
func isPacketReader(reader io.Reader) bool {
_, ok := reader.(net.PacketConn)
return ok
}
// NewReader creates a new Reader.
// The Reader instance doesn't take the ownership of reader.
func NewReader(reader io.Reader) Reader {
@@ -45,6 +51,12 @@ func NewReader(reader io.Reader) Reader {
return mr
}
if isPacketReader(reader) {
return &PacketReader{
Reader: reader,
}
}
if useReadv {
if sc, ok := reader.(syscall.Conn); ok {
rawConn, err := sc.SyscallConn()
@@ -61,14 +73,25 @@ func NewReader(reader io.Reader) Reader {
}
}
func isPacketWriter(writer io.Writer) bool {
if _, ok := writer.(net.PacketConn); ok {
return true
}
// If the writer doesn't implement syscall.Conn, it is probably not a TCP connection.
if _, ok := writer.(syscall.Conn); !ok {
return true
}
return false
}
// NewWriter creates a new Writer.
func NewWriter(writer io.Writer) Writer {
if mw, ok := writer.(Writer); ok {
return mw
}
if _, ok := writer.(syscall.Conn); !ok {
// If the writer doesn't implement syscall.Conn, it is probably not a TCP connection.
if isPacketWriter(writer) {
return &SequentialWriter{
Writer: writer,
}

View File

@@ -3,26 +3,13 @@ package buf
import (
"io"
"v2ray.com/core/common"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/serial"
)
// ReadAllToMultiBuffer reads all content from the reader into a MultiBuffer, until EOF.
func ReadAllToMultiBuffer(reader io.Reader) (MultiBuffer, error) {
mb := NewMultiBufferCap(128)
if _, err := mb.ReadFrom(reader); err != nil {
mb.Release()
return nil, err
}
return mb, nil
}
// ReadAllToBytes reads all content from the reader into a byte array, until EOF.
func ReadAllToBytes(reader io.Reader) ([]byte, error) {
mb, err := ReadAllToMultiBuffer(reader)
mb, err := ReadFrom(reader)
if err != nil {
return nil, err
}
@@ -30,34 +17,48 @@ func ReadAllToBytes(reader io.Reader) ([]byte, error) {
return nil, nil
}
b := make([]byte, mb.Len())
common.Must2(mb.Read(b))
mb.Release()
mb, _ = SplitBytes(mb, b)
ReleaseMulti(mb)
return b, nil
}
// MultiBuffer is a list of Buffers. The order of Buffer matters.
type MultiBuffer []*Buffer
// NewMultiBufferCap creates a new MultiBuffer instance.
func NewMultiBufferCap(capacity int32) MultiBuffer {
return MultiBuffer(make([]*Buffer, 0, capacity))
}
// NewMultiBufferValue wraps a list of Buffers into MultiBuffer.
func NewMultiBufferValue(b ...*Buffer) MultiBuffer {
return MultiBuffer(b)
}
// Append appends buffer to the end of this MultiBuffer
func (mb *MultiBuffer) Append(buf *Buffer) {
if buf != nil {
*mb = append(*mb, buf)
// MergeMulti merges content from src to dest, and returns the new address of dest and src
func MergeMulti(dest MultiBuffer, src MultiBuffer) (MultiBuffer, MultiBuffer) {
dest = append(dest, src...)
for idx := range src {
src[idx] = nil
}
return dest, src[:0]
}
// AppendMulti appends a MultiBuffer to the end of this one.
func (mb *MultiBuffer) AppendMulti(buf MultiBuffer) {
*mb = append(*mb, buf...)
// MergeBytes merges the given bytes into MultiBuffer and return the new address of the merged MultiBuffer.
func MergeBytes(dest MultiBuffer, src []byte) MultiBuffer {
n := len(dest)
if n > 0 && !(dest)[n-1].IsFull() {
nBytes, _ := (dest)[n-1].Write(src)
src = src[nBytes:]
}
for len(src) > 0 {
b := New()
nBytes, _ := b.Write(src)
src = src[nBytes:]
dest = append(dest, b)
}
return dest
}
// ReleaseMulti release all content of the MultiBuffer, and returns an empty MultiBuffer.
func ReleaseMulti(mb MultiBuffer) MultiBuffer {
for i := range mb {
mb[i].Release()
mb[i] = nil
}
return mb[:0]
}
// Copy copied the beginning part of the MultiBuffer into the given byte array.
@@ -73,104 +74,107 @@ func (mb MultiBuffer) Copy(b []byte) int {
return total
}
// ReadFrom implements io.ReaderFrom.
func (mb *MultiBuffer) ReadFrom(reader io.Reader) (int64, error) {
totalBytes := int64(0)
// ReadFrom reads all content from reader until EOF.
func ReadFrom(reader io.Reader) (MultiBuffer, error) {
mb := make(MultiBuffer, 0, 16)
for {
b := New()
_, err := b.ReadFullFrom(reader, Size)
if b.IsEmpty() {
b.Release()
} else {
mb.Append(b)
mb = append(mb, b)
}
totalBytes += int64(b.Len())
if err != nil {
if errors.Cause(err) == io.EOF || errors.Cause(err) == io.ErrUnexpectedEOF {
return totalBytes, nil
return mb, nil
}
return totalBytes, err
return mb, err
}
}
}
// Read implements io.Reader.
func (mb *MultiBuffer) Read(b []byte) (int, error) {
if mb.IsEmpty() {
return 0, io.EOF
}
endIndex := len(*mb)
// SplitBytes splits the given amount of bytes from the beginning of the MultiBuffer.
// It returns the new address of MultiBuffer leftover, and number of bytes written into the input byte slice.
func SplitBytes(mb MultiBuffer, b []byte) (MultiBuffer, int) {
totalBytes := 0
for i, bb := range *mb {
nBytes, _ := bb.Read(b)
endIndex := -1
for i := range mb {
pBuffer := mb[i]
nBytes, _ := pBuffer.Read(b)
totalBytes += nBytes
b = b[nBytes:]
if bb.IsEmpty() {
bb.Release()
(*mb)[i] = nil
} else {
if !pBuffer.IsEmpty() {
endIndex = i
break
}
pBuffer.Release()
mb[i] = nil
}
*mb = (*mb)[endIndex:]
return totalBytes, nil
if endIndex == -1 {
mb = mb[:0]
} else {
mb = mb[endIndex:]
}
return mb, totalBytes
}
// WriteTo implements io.WriterTo.
func (mb *MultiBuffer) WriteTo(writer io.Writer) (int64, error) {
defer mb.Release()
// SplitFirst splits the first Buffer from the beginning of the MultiBuffer.
func SplitFirst(mb MultiBuffer) (MultiBuffer, *Buffer) {
if len(mb) == 0 {
return mb, nil
}
totalBytes := int64(0)
for _, b := range *mb {
nBytes, err := writer.Write(b.Bytes())
totalBytes += int64(nBytes)
if err != nil {
return totalBytes, err
b := mb[0]
mb[0] = nil
mb = mb[1:]
return mb, b
}
// SplitSize splits the beginning of the MultiBuffer into another one, for at most size bytes.
func SplitSize(mb MultiBuffer, size int32) (MultiBuffer, MultiBuffer) {
if len(mb) == 0 {
return mb, nil
}
if mb[0].Len() > size {
b := New()
copy(b.Extend(size), mb[0].BytesTo(size))
mb[0].Advance(size)
return mb, MultiBuffer{b}
}
totalBytes := int32(0)
var r MultiBuffer
endIndex := -1
for i := range mb {
if totalBytes+mb[i].Len() > size {
endIndex = i
break
}
totalBytes += mb[i].Len()
r = append(r, mb[i])
mb[i] = nil
}
return totalBytes, nil
}
// Write implements io.Writer.
func (mb *MultiBuffer) Write(b []byte) (int, error) {
totalBytes := len(b)
n := len(*mb)
if n > 0 && !(*mb)[n-1].IsFull() {
nBytes, _ := (*mb)[n-1].Write(b)
b = b[nBytes:]
if endIndex == -1 {
// To reuse mb array
mb = mb[:0]
} else {
mb = mb[endIndex:]
}
for len(b) > 0 {
bb := New()
nBytes, _ := bb.Write(b)
b = b[nBytes:]
mb.Append(bb)
}
return totalBytes, nil
}
// WriteMultiBuffer implements Writer.
func (mb *MultiBuffer) WriteMultiBuffer(b MultiBuffer) error {
*mb = append(*mb, b...)
for i := range b {
b[i] = nil
}
return nil
return mb, r
}
// Len returns the total number of bytes in the MultiBuffer.
func (mb *MultiBuffer) Len() int32 {
func (mb MultiBuffer) Len() int32 {
if mb == nil {
return 0
}
size := int32(0)
for _, b := range *mb {
for _, b := range mb {
size += b.Len()
}
return size
@@ -186,15 +190,7 @@ func (mb MultiBuffer) IsEmpty() bool {
return true
}
// Release releases all Buffers in the MultiBuffer.
func (mb *MultiBuffer) Release() {
for i, b := range *mb {
b.Release()
(*mb)[i] = nil
}
*mb = nil
}
// String returns the content of the MultiBuffer in string.
func (mb MultiBuffer) String() string {
v := make([]interface{}, len(mb))
for i, b := range mb {
@@ -203,36 +199,44 @@ func (mb MultiBuffer) String() string {
return serial.Concat(v...)
}
// SliceBySize splits the beginning of this MultiBuffer into another one, for at most size bytes.
func (mb *MultiBuffer) SliceBySize(size int32) MultiBuffer {
slice := NewMultiBufferCap(10)
sliceSize := int32(0)
endIndex := len(*mb)
for i, b := range *mb {
if b.Len()+sliceSize > size {
endIndex = i
break
}
sliceSize += b.Len()
slice.Append(b)
(*mb)[i] = nil
}
*mb = (*mb)[endIndex:]
if endIndex == 0 && len(*mb) > 0 {
b := New()
common.Must2(b.ReadFullFrom((*mb)[0], size))
return NewMultiBufferValue(b)
}
return slice
// MultiBufferContainer is a ReadWriteCloser wrapper over MultiBuffer.
type MultiBufferContainer struct {
MultiBuffer
}
// SplitFirst splits out the first Buffer in this MultiBuffer.
func (mb *MultiBuffer) SplitFirst() *Buffer {
if len(*mb) == 0 {
return nil
// Read implements io.Reader.
func (c *MultiBufferContainer) Read(b []byte) (int, error) {
if c.MultiBuffer.IsEmpty() {
return 0, io.EOF
}
b := (*mb)[0]
(*mb)[0] = nil
*mb = (*mb)[1:]
return b
mb, nBytes := SplitBytes(c.MultiBuffer, b)
c.MultiBuffer = mb
return nBytes, nil
}
// ReadMultiBuffer implements Reader.
func (c *MultiBufferContainer) ReadMultiBuffer() (MultiBuffer, error) {
mb := c.MultiBuffer
c.MultiBuffer = nil
return mb, nil
}
// Write implements io.Writer.
func (c *MultiBufferContainer) Write(b []byte) (int, error) {
c.MultiBuffer = MergeBytes(c.MultiBuffer, b)
return len(b), nil
}
// WriteMultiBuffer implement Writer.
func (c *MultiBufferContainer) WriteMultiBuffer(b MultiBuffer) error {
mb, _ := MergeMulti(c.MultiBuffer, b)
c.MultiBuffer = mb
return nil
}
// Close implement io.Closer.
func (c *MultiBufferContainer) Close() error {
c.MultiBuffer = ReleaseMulti(c.MultiBuffer)
return nil
}

View File

@@ -5,6 +5,8 @@ import (
"io"
"testing"
"github.com/google/go-cmp/cmp"
"v2ray.com/core/common"
. "v2ray.com/core/common/buf"
. "v2ray.com/ext/assert"
@@ -14,15 +16,14 @@ func TestMultiBufferRead(t *testing.T) {
assert := With(t)
b1 := New()
b1.WriteBytes('a', 'b')
b1.WriteString("ab")
b2 := New()
b2.WriteBytes('c', 'd')
mb := NewMultiBufferValue(b1, b2)
b2.WriteString("cd")
mb := MultiBuffer{b1, b2}
bs := make([]byte, 32)
nBytes, err := mb.Read(bs)
assert(err, IsNil)
_, nBytes := SplitBytes(mb, bs)
assert(nBytes, Equals, 4)
assert(bs[:nBytes], Equals, []byte("abcd"))
}
@@ -32,27 +33,77 @@ func TestMultiBufferAppend(t *testing.T) {
var mb MultiBuffer
b := New()
b.WriteBytes('a', 'b')
mb.Append(b)
b.WriteString("ab")
mb = append(mb, b)
assert(mb.Len(), Equals, int32(2))
}
func TestMultiBufferSliceBySizeLarge(t *testing.T) {
assert := With(t)
lb := make([]byte, 8*1024)
common.Must2(io.ReadFull(rand.Reader, lb))
mb := MergeBytes(nil, lb)
mb, mb2 := SplitSize(mb, 1024)
if mb2.Len() != 1024 {
t.Error("expect length 1024, but got ", mb2.Len())
}
if mb.Len() != 7*1024 {
t.Error("expect length 7*1024, but got ", mb.Len())
}
mb, mb3 := SplitSize(mb, 7*1024)
if mb3.Len() != 7*1024 {
t.Error("expect length 7*1024, but got", mb.Len())
}
if !mb.IsEmpty() {
t.Error("expect empty buffer, but got ", mb.Len())
}
}
func TestMultiBufferSplitFirst(t *testing.T) {
b1 := New()
b1.WriteString("b1")
b2 := New()
b2.WriteString("b2")
b3 := New()
b3.WriteString("b3")
var mb MultiBuffer
common.Must2(mb.Write(lb))
mb = append(mb, b1, b2, b3)
mb2 := mb.SliceBySize(1024)
assert(mb2.Len(), Equals, int32(1024))
mb, c1 := SplitFirst(mb)
if diff := cmp.Diff(b1.String(), c1.String()); diff != "" {
t.Error(diff)
}
mb, c2 := SplitFirst(mb)
if diff := cmp.Diff(b2.String(), c2.String()); diff != "" {
t.Error(diff)
}
mb, c3 := SplitFirst(mb)
if diff := cmp.Diff(b3.String(), c3.String()); diff != "" {
t.Error(diff)
}
if !mb.IsEmpty() {
t.Error("expect empty buffer, but got ", mb.String())
}
}
func TestInterface(t *testing.T) {
assert := With(t)
func BenchmarkSplitBytes(b *testing.B) {
var mb MultiBuffer
raw := make([]byte, Size)
assert((*MultiBuffer)(nil), Implements, (*io.WriterTo)(nil))
assert((*MultiBuffer)(nil), Implements, (*io.ReaderFrom)(nil))
b.ResetTimer()
for i := 0; i < b.N; i++ {
buffer := StackNew()
buffer.Extend(Size)
mb = append(mb, &buffer)
mb, _ = SplitBytes(mb, raw)
}
}

View File

@@ -7,7 +7,7 @@ import (
"v2ray.com/core/common/errors"
)
func readOne(r io.Reader) (*Buffer, error) {
func readOneUDP(r io.Reader) (*Buffer, error) {
b := New()
for i := 0; i < 64; i++ {
_, err := b.ReadFrom(r)
@@ -20,6 +20,35 @@ func readOne(r io.Reader) (*Buffer, error) {
}
}
b.Release()
return nil, newError("Reader returns too many empty payloads.")
}
// ReadBuffer reads a Buffer from the given reader, without allocating large buffer in advance.
func ReadBuffer(r io.Reader) (*Buffer, error) {
// Use an one-byte buffer to wait for incoming payload.
var firstByte [1]byte
nBytes, err := r.Read(firstByte[:])
if err != nil {
return nil, err
}
b := New()
if nBytes > 0 {
common.Must(b.WriteByte(firstByte[0]))
}
for i := 0; i < 64; i++ {
_, err := b.ReadFrom(r)
if !b.IsEmpty() {
return b, nil
}
if err != nil {
b.Release()
return nil, err
}
}
b.Release()
return nil, newError("Reader returns too many empty payloads.")
}
@@ -46,10 +75,9 @@ func (r *BufferedReader) ReadByte() (byte, error) {
// Read implements io.Reader. It reads from internal buffer first (if available) and then reads from the underlying reader.
func (r *BufferedReader) Read(b []byte) (int, error) {
if !r.Buffer.IsEmpty() {
nBytes, err := r.Buffer.Read(b)
common.Must(err)
buffer, nBytes := SplitBytes(r.Buffer, b)
r.Buffer = buffer
if r.Buffer.IsEmpty() {
r.Buffer.Release()
r.Buffer = nil
}
return nBytes, nil
@@ -60,12 +88,11 @@ func (r *BufferedReader) Read(b []byte) (int, error) {
return 0, err
}
nBytes, err := mb.Read(b)
common.Must(err)
mb, nBytes := SplitBytes(mb, b)
if !mb.IsEmpty() {
r.Buffer = mb
}
return nBytes, err
return nBytes, nil
}
// ReadMultiBuffer implements Reader.
@@ -89,7 +116,8 @@ func (r *BufferedReader) ReadAtMost(size int32) (MultiBuffer, error) {
r.Buffer = mb
}
mb := r.Buffer.SliceBySize(size)
rb, mb := SplitSize(r.Buffer, size)
r.Buffer = rb
if r.Buffer.IsEmpty() {
r.Buffer = nil
}
@@ -123,7 +151,8 @@ func (r *BufferedReader) WriteTo(writer io.Writer) (int64, error) {
// Close implements io.Closer.
func (r *BufferedReader) Close() error {
if !r.Buffer.IsEmpty() {
r.Buffer.Release()
ReleaseMulti(r.Buffer)
r.Buffer = nil
}
return common.Close(r.Reader)
}
@@ -135,9 +164,23 @@ type SingleReader struct {
// ReadMultiBuffer implements Reader.
func (r *SingleReader) ReadMultiBuffer() (MultiBuffer, error) {
b, err := readOne(r.Reader)
b, err := ReadBuffer(r.Reader)
if err != nil {
return nil, err
}
return NewMultiBufferValue(b), nil
return MultiBuffer{b}, nil
}
// PacketReader is a Reader that read one Buffer every time.
type PacketReader struct {
io.Reader
}
// ReadMultiBuffer implements Reader.
func (r *PacketReader) ReadMultiBuffer() (MultiBuffer, error) {
b, err := readOneUDP(r.Reader)
if err != nil {
return nil, err
}
return MultiBuffer{b}, nil
}

View File

@@ -17,10 +17,10 @@ func TestBytesReaderWriteTo(t *testing.T) {
pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024))
reader := &BufferedReader{Reader: pReader}
b1 := New()
b1.WriteBytes('a', 'b', 'c')
b1.WriteString("abc")
b2 := New()
b2.WriteBytes('e', 'f', 'g')
assert(pWriter.WriteMultiBuffer(NewMultiBufferValue(b1, b2)), IsNil)
b2.WriteString("efg")
assert(pWriter.WriteMultiBuffer(MultiBuffer{b1, b2}), IsNil)
pWriter.Close()
pReader2, pWriter2 := pipe.New(pipe.WithSizeLimit(1024))
@@ -44,10 +44,10 @@ func TestBytesReaderMultiBuffer(t *testing.T) {
pReader, pWriter := pipe.New(pipe.WithSizeLimit(1024))
reader := &BufferedReader{Reader: pReader}
b1 := New()
b1.WriteBytes('a', 'b', 'c')
b1.WriteString("abc")
b2 := New()
b2.WriteBytes('e', 'f', 'g')
assert(pWriter.WriteMultiBuffer(NewMultiBufferValue(b1, b2)), IsNil)
b2.WriteString("efg")
assert(pWriter.WriteMultiBuffer(MultiBuffer{b1, b2}), IsNil)
pWriter.Close()
mbReader := NewReader(reader)
@@ -69,8 +69,7 @@ func TestReadByte(t *testing.T) {
t.Error("unexpected byte: ", b, " want a")
}
var mb MultiBuffer
nBytes, err := reader.WriteTo(&mb)
nBytes, err := reader.WriteTo(DiscardBytes)
common.Must(err)
if nBytes != 3 {
t.Error("unexpect bytes written: ", nBytes)

View File

@@ -48,6 +48,7 @@ type multiReader interface {
Clear()
}
// ReadVReader is a Reader that uses readv(2) syscall to read data.
type ReadVReader struct {
io.Reader
rawConn syscall.RawConn
@@ -55,6 +56,7 @@ type ReadVReader struct {
alloc allocStrategy
}
// NewReadVReader creates a new ReadVReader.
func NewReadVReader(reader io.Reader, rawConn syscall.RawConn) *ReadVReader {
return &ReadVReader{
Reader: reader,
@@ -83,14 +85,12 @@ func (r *ReadVReader) readMulti() (MultiBuffer, error) {
r.mr.Clear()
if err != nil {
mb := MultiBuffer(bs)
mb.Release()
ReleaseMulti(MultiBuffer(bs))
return nil, err
}
if nBytes == 0 {
mb := MultiBuffer(bs)
mb.Release()
ReleaseMulti(MultiBuffer(bs))
return nil, io.EOF
}
@@ -99,7 +99,7 @@ func (r *ReadVReader) readMulti() (MultiBuffer, error) {
if nBytes <= 0 {
break
}
end := int32(nBytes)
end := nBytes
if end > Size {
end = Size
}
@@ -119,14 +119,14 @@ func (r *ReadVReader) readMulti() (MultiBuffer, error) {
// ReadMultiBuffer implements Reader.
func (r *ReadVReader) ReadMultiBuffer() (MultiBuffer, error) {
if r.alloc.Current() == 1 {
b, err := readOne(r.Reader)
b, err := ReadBuffer(r.Reader)
if err != nil {
return nil, err
}
if b.IsFull() {
r.alloc.Adjust(1)
}
return NewMultiBufferValue(b), nil
return MultiBuffer{b}, nil
}
mb, err := r.readMulti()

View File

@@ -33,8 +33,7 @@ func TestReadvReader(t *testing.T) {
go func() {
writer := NewWriter(conn)
var mb MultiBuffer
common.Must2(mb.Write(data))
mb := MergeBytes(nil, data)
if err := writer.WriteMultiBuffer(mb); err != nil {
t.Fatal("failed to write data: ", err)
@@ -51,14 +50,14 @@ func TestReadvReader(t *testing.T) {
if err != nil {
t.Fatal("unexpected error: ", err)
}
rmb.AppendMulti(mb)
rmb, _ = MergeMulti(rmb, mb)
if rmb.Len() == size {
break
}
}
rdata := make([]byte, size)
common.Must2(rmb.Read(rdata))
SplitBytes(rmb, rdata)
if err := compare.BytesEqualWithDetail(data, rdata); err != nil {
t.Fatal(err)

View File

@@ -18,7 +18,7 @@ type BufferToBytesWriter struct {
// WriteMultiBuffer implements Writer. This method takes ownership of the given buffer.
func (w *BufferToBytesWriter) WriteMultiBuffer(mb MultiBuffer) error {
defer mb.Release()
defer ReleaseMulti(mb)
size := mb.Len()
if size == 0 {
@@ -29,17 +29,19 @@ func (w *BufferToBytesWriter) WriteMultiBuffer(mb MultiBuffer) error {
return WriteAllBytes(w.Writer, mb[0].Bytes())
}
if cap(w.cache) < len(mb) {
w.cache = make([][]byte, 0, len(mb))
}
bs := w.cache
for _, b := range mb {
bs = append(bs, b.Bytes())
}
w.cache = bs
defer func() {
for idx := range w.cache {
w.cache[idx] = nil
for idx := range bs {
bs[idx] = nil
}
w.cache = w.cache[:0]
}()
nb := net.Buffers(bs)
@@ -134,15 +136,16 @@ func (w *BufferedWriter) WriteMultiBuffer(b MultiBuffer) error {
return w.writer.WriteMultiBuffer(b)
}
defer b.Release()
reader := MultiBufferContainer{
MultiBuffer: b,
}
defer reader.Close()
for !b.IsEmpty() {
for !reader.MultiBuffer.IsEmpty() {
if w.buffer == nil {
w.buffer = New()
}
if _, err := w.buffer.ReadFrom(&b); err != nil {
return err
}
common.Must2(w.buffer.ReadFrom(&reader))
if w.buffer.IsFull() {
if err := w.flushInternal(); err != nil {
return err
@@ -175,7 +178,7 @@ func (w *BufferedWriter) flushInternal() error {
return err
}
return w.writer.WriteMultiBuffer(NewMultiBufferValue(b))
return w.writer.WriteMultiBuffer(MultiBuffer{b})
}
// SetBuffered sets whether the internal buffer is used. If set to false, Flush() will be called to clear the buffer.
@@ -216,7 +219,7 @@ type SequentialWriter struct {
// WriteMultiBuffer implements Writer.
func (w *SequentialWriter) WriteMultiBuffer(mb MultiBuffer) error {
defer mb.Release()
defer ReleaseMulti(mb)
for _, b := range mb {
if b.IsEmpty() {
@@ -234,7 +237,7 @@ func (w *SequentialWriter) WriteMultiBuffer(mb MultiBuffer) error {
type noOpWriter byte
func (noOpWriter) WriteMultiBuffer(b MultiBuffer) error {
b.Release()
ReleaseMulti(b)
return nil
}

View File

@@ -25,7 +25,7 @@ func TestWriter(t *testing.T) {
writer := NewBufferedWriter(NewWriter(writeBuffer))
writer.SetBuffered(false)
err := writer.WriteMultiBuffer(NewMultiBufferValue(lb))
err := writer.WriteMultiBuffer(MultiBuffer{lb})
assert(err, IsNil)
assert(writer.Flush(), IsNil)
assert(expectedBytes, Equals, writeBuffer.Bytes())

View File

@@ -35,6 +35,8 @@ func init() {
// GetPool returns a sync.Pool that generates bytes array with at least the given size.
// It may return nil if no such pool exists.
//
// v2ray:api:stable
func GetPool(size int32) *sync.Pool {
for idx, ps := range poolSize {
if size <= ps {
@@ -45,6 +47,8 @@ func GetPool(size int32) *sync.Pool {
}
// Alloc returns a byte slice with at least the given size. Minimum size of returned slice is 2048.
//
// v2ray:api:stable
func Alloc(size int32) []byte {
pool := GetPool(size)
if pool != nil {
@@ -54,6 +58,8 @@ func Alloc(size int32) []byte {
}
// Free puts a byte slice into the internal pool.
//
// v2ray:api:stable
func Free(b []byte) {
size := int32(cap(b))
b = b[0:cap(b)]

View File

@@ -1,10 +0,0 @@
package compare
import "v2ray.com/core/common/errors"
func StringEqualWithDetail(a string, b string) error {
if a != b {
return errors.New("Got ", b, " but want ", a)
}
return nil
}

View File

@@ -4,7 +4,6 @@ import (
"crypto/cipher"
"io"
"math/rand"
"time"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
@@ -177,7 +176,7 @@ func (r *AuthenticationReader) readInternal(soft bool, mb *buf.MultiBuffer) erro
if err != nil {
return nil
}
mb.Append(b)
*mb = append(*mb, b)
return nil
}
@@ -195,15 +194,15 @@ func (r *AuthenticationReader) readInternal(soft bool, mb *buf.MultiBuffer) erro
return err
}
common.Must2(mb.Write(rb))
*mb = buf.MergeBytes(*mb, rb)
return nil
}
func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
const readSize = 16
mb := buf.NewMultiBufferCap(readSize)
mb := make(buf.MultiBuffer, 0, readSize)
if err := r.readInternal(false, &mb); err != nil {
mb.Release()
buf.ReleaseMulti(mb)
return nil, err
}
@@ -213,7 +212,7 @@ func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
break
}
if err != nil {
mb.Release()
buf.ReleaseMulti(mb)
return nil, err
}
}
@@ -227,7 +226,6 @@ type AuthenticationWriter struct {
sizeParser ChunkSizeEncoder
transferType protocol.TransferType
padding PaddingLengthGenerator
randReader *rand.Rand
}
func NewAuthenticationWriter(auth Authenticator, sizeParser ChunkSizeEncoder, writer io.Writer, transferType protocol.TransferType, padding PaddingLengthGenerator) *AuthenticationWriter {
@@ -239,13 +237,12 @@ func NewAuthenticationWriter(auth Authenticator, sizeParser ChunkSizeEncoder, wr
}
if padding != nil {
w.padding = padding
w.randReader = rand.New(rand.NewSource(time.Now().Unix()))
}
return w
}
func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) {
encryptedSize := b.Len() + int32(w.auth.Overhead())
func (w *AuthenticationWriter) seal(b []byte) (*buf.Buffer, error) {
encryptedSize := int32(len(b) + w.auth.Overhead())
var paddingSize int32
if w.padding != nil {
paddingSize = int32(w.padding.NextPaddingLen())
@@ -258,20 +255,21 @@ func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) {
eb := buf.New()
w.sizeParser.Encode(uint16(encryptedSize+paddingSize), eb.Extend(w.sizeParser.SizeBytes()))
if _, err := w.auth.Seal(eb.Extend(encryptedSize)[:0], b.Bytes()); err != nil {
if _, err := w.auth.Seal(eb.Extend(encryptedSize)[:0], b); err != nil {
eb.Release()
return nil, err
}
if paddingSize > 0 {
// With size of the chunk and padding length encrypted, the content of padding doesn't matter much.
common.Must2(eb.ReadFullFrom(w.randReader, int32(paddingSize)))
paddingBytes := eb.Extend(paddingSize)
common.Must2(rand.Read(paddingBytes))
}
return eb, nil
}
func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error {
defer mb.Release()
defer buf.ReleaseMulti(mb)
var maxPadding int32
if w.padding != nil {
@@ -279,19 +277,24 @@ func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error {
}
payloadSize := buf.Size - int32(w.auth.Overhead()) - w.sizeParser.SizeBytes() - maxPadding
mb2Write := buf.NewMultiBufferCap(int32(len(mb) + 10))
mb2Write := make(buf.MultiBuffer, 0, len(mb)+10)
temp := buf.New()
defer temp.Release()
rawBytes := temp.Extend(payloadSize)
for {
b := buf.New()
common.Must2(b.ReadFrom(io.LimitReader(&mb, int64(payloadSize))))
eb, err := w.seal(b)
b.Release()
nb, nBytes := buf.SplitBytes(mb, rawBytes)
mb = nb
eb, err := w.seal(rawBytes[:nBytes])
if err != nil {
mb2Write.Release()
buf.ReleaseMulti(mb2Write)
return err
}
mb2Write.Append(eb)
mb2Write = append(mb2Write, eb)
if mb.IsEmpty() {
break
}
@@ -301,21 +304,21 @@ func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error {
}
func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error {
defer mb.Release()
defer buf.ReleaseMulti(mb)
mb2Write := buf.NewMultiBufferCap(int32(len(mb)) + 1)
mb2Write := make(buf.MultiBuffer, 0, len(mb)+1)
for _, b := range mb {
if b.IsEmpty() {
continue
}
eb, err := w.seal(b)
eb, err := w.seal(b.Bytes())
if err != nil {
continue
}
mb2Write.Append(eb)
mb2Write = append(mb2Write, eb)
}
if mb2Write.IsEmpty() {
@@ -328,12 +331,9 @@ func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error {
// WriteMultiBuffer implements buf.Writer.
func (w *AuthenticationWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
if mb.IsEmpty() {
b := buf.New()
defer b.Release()
eb, err := w.seal(b)
eb, err := w.seal([]byte{})
common.Must(err)
return w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(eb))
return w.writer.WriteMultiBuffer(buf.MultiBuffer{eb})
}
if w.transferType == protocol.TransferTypeStream {

View File

@@ -30,8 +30,7 @@ func TestAuthenticationReaderWriter(t *testing.T) {
rawPayload := make([]byte, payloadSize)
rand.Read(rawPayload)
var payload buf.MultiBuffer
payload.Write(rawPayload)
payload := buf.MergeBytes(nil, rawPayload)
assert(payload.Len(), Equals, int32(payloadSize))
cache := bytes.NewBuffer(nil)
@@ -60,13 +59,13 @@ func TestAuthenticationReaderWriter(t *testing.T) {
mb2, err := reader.ReadMultiBuffer()
assert(err, IsNil)
mb.AppendMulti(mb2)
mb, _ = buf.MergeMulti(mb, mb2)
}
assert(mb.Len(), Equals, int32(payloadSize))
mbContent := make([]byte, payloadSize)
mb.Read(mbContent)
buf.SplitBytes(mb, mbContent)
assert(mbContent, Equals, rawPayload)
_, err = reader.ReadMultiBuffer()
@@ -97,11 +96,11 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) {
var payload buf.MultiBuffer
pb1 := buf.New()
pb1.Write([]byte("abcd"))
payload.Append(pb1)
payload = append(payload, pb1)
pb2 := buf.New()
pb2.Write([]byte("efgh"))
payload.Append(pb2)
payload = append(payload, pb2)
assert(writer.WriteMultiBuffer(payload), IsNil)
assert(cache.Len(), GreaterThan, int32(0))
@@ -117,10 +116,10 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) {
mb, err := reader.ReadMultiBuffer()
assert(err, IsNil)
b1 := mb.SplitFirst()
mb, b1 := buf.SplitFirst(mb)
assert(b1.String(), Equals, "abcd")
b2 := mb.SplitFirst()
mb, b2 := buf.SplitFirst(mb)
assert(b2.String(), Equals, "efgh")
assert(mb.IsEmpty(), IsTrue)

View File

@@ -140,15 +140,16 @@ func NewChunkStreamWriter(sizeEncoder ChunkSizeEncoder, writer io.Writer) *Chunk
func (w *ChunkStreamWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
const sliceSize = 8192
mbLen := mb.Len()
mb2Write := buf.NewMultiBufferCap(mbLen/buf.Size + mbLen/sliceSize + 2)
mb2Write := make(buf.MultiBuffer, 0, mbLen/buf.Size+mbLen/sliceSize+2)
for {
slice := mb.SliceBySize(sliceSize)
mb2, slice := buf.SplitSize(mb, sliceSize)
mb = mb2
b := buf.New()
w.sizeEncoder.Encode(uint16(slice.Len()), b.Extend(w.sizeEncoder.SizeBytes()))
mb2Write.Append(b)
mb2Write.AppendMulti(slice)
mb2Write = append(mb2Write, b)
mb2Write = append(mb2Write, slice...)
if mb.IsEmpty() {
break

View File

@@ -20,12 +20,12 @@ func TestChunkStreamIO(t *testing.T) {
reader := NewChunkStreamReader(PlainChunkSizeParser{}, cache)
b := buf.New()
b.WriteBytes('a', 'b', 'c', 'd')
common.Must(writer.WriteMultiBuffer(buf.NewMultiBufferValue(b)))
b.WriteString("abcd")
common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{b}))
b = buf.New()
b.WriteBytes('e', 'f', 'g')
common.Must(writer.WriteMultiBuffer(buf.NewMultiBufferValue(b)))
b.WriteString("efg")
common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{b}))
common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{}))

View File

@@ -4,7 +4,8 @@ import (
"io"
"testing"
"v2ray.com/core/common/compare"
"github.com/google/go-cmp/cmp"
. "v2ray.com/core/common/errors"
"v2ray.com/core/common/log"
. "v2ray.com/ext/assert"
@@ -46,8 +47,8 @@ func TestErrorMessage(t *testing.T) {
}
for _, d := range data {
if err := compare.StringEqualWithDetail(d.msg, d.err.Error()); err != nil {
t.Fatal(err)
if diff := cmp.Diff(d.msg, d.err.Error()); diff != "" {
t.Error(diff)
}
}
}

View File

@@ -0,0 +1,30 @@
package errors
import (
"strings"
)
type multiError []error
func (e multiError) Error() string {
var r strings.Builder
r.WriteString("multierr: ")
for _, err := range e {
r.WriteString(err.Error())
r.WriteString(" | ")
}
return r.String()
}
func Combine(maybeError ...error) error {
var errs multiError
for _, err := range maybeError {
if err != nil {
errs = append(errs, err)
}
}
if len(errs) == 0 {
return nil
}
return errs
}

View File

@@ -15,7 +15,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Severity int32

View File

@@ -3,7 +3,8 @@ package log_test
import (
"testing"
"v2ray.com/core/common/compare"
"github.com/google/go-cmp/cmp"
"v2ray.com/core/common/log"
"v2ray.com/core/common/net"
)
@@ -26,7 +27,7 @@ func TestLogRecord(t *testing.T) {
Content: net.ParseAddress(ip),
})
if err := compare.StringEqualWithDetail("[Error] "+ip, logger.value); err != nil {
t.Fatal(err)
if diff := cmp.Diff("[Error] "+ip, logger.value); diff != "" {
t.Error(diff)
}
}

View File

@@ -35,7 +35,7 @@ func (m *ClientManager) Dispatch(ctx context.Context, link *transport.Link) erro
}
}
return newError("unable to find an available mux client")
return newError("unable to find an available mux client").AtWarning()
}
type WorkerPicker interface {
@@ -274,7 +274,7 @@ func (m *ClientWorker) IsClosing() bool {
}
func (m *ClientWorker) IsFull() bool {
if m.IsClosing() {
if m.IsClosing() || m.Closed() {
return true
}
@@ -322,6 +322,10 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
s, found := m.sessionManager.Get(meta.SessionID)
if !found {
// Notify remote peer to close this session.
closingWriter := NewResponseWriter(meta.SessionID, m.link.Writer, protocol.TransferTypeStream)
closingWriter.Close()
return buf.Copy(NewStreamReader(reader), buf.Discard)
}
@@ -330,6 +334,10 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
if err != nil && buf.IsWriteError(err) {
newError("failed to write to downstream. closing session ", s.ID).Base(err).WriteToLog()
// Notify remote peer to close this session.
closingWriter := NewResponseWriter(meta.SessionID, m.link.Writer, protocol.TransferTypeStream)
closingWriter.Close()
drainErr := buf.Copy(rr, buf.Discard)
pipe.CloseError(s.input)
s.Close()

View File

@@ -9,6 +9,8 @@ import (
"v2ray.com/core/common"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/mux"
"v2ray.com/core/common/net"
"v2ray.com/core/common/session"
"v2ray.com/core/testing/mocks"
"v2ray.com/core/transport"
"v2ray.com/core/transport/pipe"
@@ -45,3 +47,70 @@ func TestClientWorkerEOF(t *testing.T) {
t.Error("expected failed dispatching, but actually not")
}
}
func TestClientWorkerClose(t *testing.T) {
mockCtl := gomock.NewController(t)
defer mockCtl.Finish()
r1, w1 := pipe.New(pipe.WithoutSizeLimit())
worker1, err := mux.NewClientWorker(transport.Link{
Reader: r1,
Writer: w1,
}, mux.ClientStrategy{
MaxConcurrency: 4,
MaxConnection: 4,
})
common.Must(err)
r2, w2 := pipe.New(pipe.WithoutSizeLimit())
worker2, err := mux.NewClientWorker(transport.Link{
Reader: r2,
Writer: w2,
}, mux.ClientStrategy{
MaxConcurrency: 4,
MaxConnection: 4,
})
common.Must(err)
factory := mocks.NewMuxClientWorkerFactory(mockCtl)
gomock.InOrder(
factory.EXPECT().Create().Return(worker1, nil),
factory.EXPECT().Create().Return(worker2, nil),
)
picker := &mux.IncrementalWorkerPicker{
Factory: factory,
}
manager := &mux.ClientManager{
Picker: picker,
}
tr1, tw1 := pipe.New(pipe.WithoutSizeLimit())
ctx1 := session.ContextWithOutbound(context.Background(), &session.Outbound{
Target: net.TCPDestination(net.DomainAddress("www.v2ray.com"), 80),
})
common.Must(manager.Dispatch(ctx1, &transport.Link{
Reader: tr1,
Writer: tw1,
}))
defer tw1.Close()
common.Must(w1.Close())
time.Sleep(time.Millisecond * 500)
if !worker1.Closed() {
t.Error("worker1 is not finished")
}
tr2, tw2 := pipe.New(pipe.WithoutSizeLimit())
ctx2 := session.ContextWithOutbound(context.Background(), &session.Outbound{
Target: net.TCPDestination(net.DomainAddress("www.v2ray.com"), 80),
})
common.Must(manager.Dispatch(ctx2, &transport.Link{
Reader: tr2,
Writer: tw2,
}))
defer tw2.Close()
common.Must(w2.Close())
}

View File

@@ -61,22 +61,21 @@ type FrameMetadata struct {
}
func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
common.Must2(b.WriteBytes(0x00, 0x00))
lenBytes := b.Bytes()
lenBytes := b.Extend(2)
len0 := b.Len()
if _, err := serial.WriteUint16(b, f.SessionID); err != nil {
return err
}
sessionBytes := b.Extend(2)
binary.BigEndian.PutUint16(sessionBytes, f.SessionID)
common.Must2(b.WriteBytes(byte(f.SessionStatus), byte(f.Option)))
common.Must(b.WriteByte(byte(f.SessionStatus)))
common.Must(b.WriteByte(byte(f.Option)))
if f.SessionStatus == SessionStatusNew {
switch f.Target.Network {
case net.Network_TCP:
common.Must2(b.WriteBytes(byte(TargetNetworkTCP)))
common.Must(b.WriteByte(byte(TargetNetworkTCP)))
case net.Network_UDP:
common.Must2(b.WriteBytes(byte(TargetNetworkUDP)))
common.Must(b.WriteByte(byte(TargetNetworkUDP)))
}
if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
@@ -121,6 +120,9 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
f.Target.Network = net.Network_Unknown
if f.SessionStatus == SessionStatusNew {
if b.Len() < 8 {
return newError("insufficient buffer: ", b.Len())
}
network := TargetNetwork(b.Byte(4))
b.Advance(5)

25
common/mux/frame_test.go Normal file
View File

@@ -0,0 +1,25 @@
package mux_test
import (
"testing"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/mux"
"v2ray.com/core/common/net"
)
func BenchmarkFrameWrite(b *testing.B) {
frame := mux.FrameMetadata{
Target: net.TCPDestination(net.DomainAddress("www.v2ray.com"), net.Port(80)),
SessionID: 1,
SessionStatus: mux.SessionStatusNew,
}
writer := buf.New()
defer writer.Release()
for i := 0; i < b.N; i++ {
common.Must(frame.WriteTo(writer))
writer.Clear()
}
}

View File

@@ -22,7 +22,7 @@ func readAll(reader buf.Reader) (buf.MultiBuffer, error) {
if err != nil {
return nil, err
}
mb.AppendMulti(b)
mb = append(mb, b...)
}
return mb, nil
}
@@ -44,7 +44,7 @@ func TestReaderWriter(t *testing.T) {
writePayload := func(writer *Writer, payload ...byte) error {
b := buf.New()
b.Write(payload)
return writer.WriteMultiBuffer(buf.NewMultiBufferValue(b))
return writer.WriteMultiBuffer(buf.MultiBuffer{b})
}
assert(writePayload(writer, 'a', 'b', 'c', 'd'), IsNil)

View File

@@ -43,7 +43,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
return nil, err
}
r.eof = true
return buf.NewMultiBufferValue(b), nil
return buf.MultiBuffer{b}, nil
}
// NewStreamReader creates a new StreamReader.

View File

@@ -159,6 +159,10 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
s, found := w.sessionManager.Get(meta.SessionID)
if !found {
// Notify remote peer to close this session.
closingWriter := NewResponseWriter(meta.SessionID, w.link.Writer, protocol.TransferTypeStream)
closingWriter.Close()
return buf.Copy(NewStreamReader(reader), buf.Discard)
}
@@ -166,7 +170,11 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
err := buf.Copy(rr, s.output)
if err != nil && buf.IsWriteError(err) {
newError("failed to write to downstream writer. closing session ", s.ID).Base(err)
newError("failed to write to downstream writer. closing session ", s.ID).Base(err).WriteToLog()
// Notify remote peer to close this session.
closingWriter := NewResponseWriter(meta.SessionID, w.link.Writer, protocol.TransferTypeStream)
closingWriter.Close()
drainErr := buf.Copy(rr, buf.Discard)
pipe.CloseError(s.input)

View File

@@ -58,7 +58,7 @@ func (w *Writer) writeMetaOnly() error {
if err := meta.WriteTo(b); err != nil {
return err
}
return w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(b))
return w.writer.WriteMultiBuffer(buf.MultiBuffer{b})
}
func writeMetaWithFrame(writer buf.Writer, meta FrameMetadata, data buf.MultiBuffer) error {
@@ -70,9 +70,9 @@ func writeMetaWithFrame(writer buf.Writer, meta FrameMetadata, data buf.MultiBuf
return err
}
mb2 := buf.NewMultiBufferCap(int32(len(data)) + 1)
mb2.Append(frame)
mb2.AppendMulti(data)
mb2 := make(buf.MultiBuffer, 0, len(data)+1)
mb2 = append(mb2, frame)
mb2 = append(mb2, data...)
return writer.WriteMultiBuffer(mb2)
}
@@ -85,7 +85,7 @@ func (w *Writer) writeData(mb buf.MultiBuffer) error {
// WriteMultiBuffer implements buf.Writer.
func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error {
defer mb.Release()
defer buf.ReleaseMulti(mb)
if mb.IsEmpty() {
return w.writeMetaOnly()
@@ -94,9 +94,11 @@ func (w *Writer) WriteMultiBuffer(mb buf.MultiBuffer) error {
for !mb.IsEmpty() {
var chunk buf.MultiBuffer
if w.transferType == protocol.TransferTypeStream {
chunk = mb.SliceBySize(8 * 1024)
mb, chunk = buf.SplitSize(mb, 8*1024)
} else {
chunk = buf.NewMultiBufferValue(mb.SplitFirst())
mb2, b := buf.SplitFirst(mb)
mb = mb2
chunk = buf.MultiBuffer{b}
}
if err := w.writeData(chunk); err != nil {
return err
@@ -119,6 +121,6 @@ func (w *Writer) Close() error {
frame := buf.New()
common.Must(meta.WriteTo(frame))
w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(frame)) // nolint: errcheck
w.writer.WriteMultiBuffer(buf.MultiBuffer{frame}) // nolint: errcheck
return nil
}

View File

@@ -35,16 +35,6 @@ const (
AddressFamilyDomain = AddressFamily(2)
)
// Either returns true if current AddressFamily matches any of the AddressFamilies provided.
func (af AddressFamily) Either(fs ...AddressFamily) bool {
for _, f := range fs {
if af == f {
return true
}
}
return false
}
// IsIPv4 returns true if current AddressFamily is IPv4.
func (af AddressFamily) IsIPv4() bool {
return af == AddressFamilyIPv4
@@ -55,6 +45,11 @@ func (af AddressFamily) IsIPv6() bool {
return af == AddressFamilyIPv6
}
// IsIP returns true if current AddressFamily is IPv6 or IPv4.
func (af AddressFamily) IsIP() bool {
return af == AddressFamilyIPv4 || af == AddressFamilyIPv6
}
// IsDomain returns true if current AddressFamily is Domain.
func (af AddressFamily) IsDomain() bool {
return af == AddressFamilyDomain

View File

@@ -15,7 +15,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Address of a network host. It may be either an IP address or a domain address.
type IPOrDomain struct {
@@ -90,72 +90,14 @@ func (m *IPOrDomain) GetDomain() string {
return ""
}
// XXX_OneofFuncs is for the internal use of the proto package.
func (*IPOrDomain) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
return _IPOrDomain_OneofMarshaler, _IPOrDomain_OneofUnmarshaler, _IPOrDomain_OneofSizer, []interface{}{
// XXX_OneofWrappers is for the internal use of the proto package.
func (*IPOrDomain) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*IPOrDomain_Ip)(nil),
(*IPOrDomain_Domain)(nil),
}
}
func _IPOrDomain_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
m := msg.(*IPOrDomain)
// address
switch x := m.Address.(type) {
case *IPOrDomain_Ip:
b.EncodeVarint(1<<3 | proto.WireBytes)
b.EncodeRawBytes(x.Ip)
case *IPOrDomain_Domain:
b.EncodeVarint(2<<3 | proto.WireBytes)
b.EncodeStringBytes(x.Domain)
case nil:
default:
return fmt.Errorf("IPOrDomain.Address has unexpected type %T", x)
}
return nil
}
func _IPOrDomain_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
m := msg.(*IPOrDomain)
switch tag {
case 1: // address.ip
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeRawBytes(true)
m.Address = &IPOrDomain_Ip{x}
return true, err
case 2: // address.domain
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeStringBytes()
m.Address = &IPOrDomain_Domain{x}
return true, err
default:
return false, nil
}
}
func _IPOrDomain_OneofSizer(msg proto.Message) (n int) {
m := msg.(*IPOrDomain)
// address
switch x := m.Address.(type) {
case *IPOrDomain_Ip:
n += 1 // tag and wire
n += proto.SizeVarint(uint64(len(x.Ip)))
n += len(x.Ip)
case *IPOrDomain_Domain:
n += 1 // tag and wire
n += proto.SizeVarint(uint64(len(x.Domain)))
n += len(x.Domain)
case nil:
default:
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
}
return n
}
func init() {
proto.RegisterType((*IPOrDomain)(nil), "v2ray.core.common.net.IPOrDomain")
}

View File

@@ -104,18 +104,27 @@ func TestIPOrDomain(t *testing.T) {
func BenchmarkParseAddressIPv4(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = ParseAddress("8.8.8.8")
addr := ParseAddress("8.8.8.8")
if addr.Family() != AddressFamilyIPv4 {
panic("not ipv4")
}
}
}
func BenchmarkParseAddressIPv6(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = ParseAddress("2001:4860:0:2001::68")
addr := ParseAddress("2001:4860:0:2001::68")
if addr.Family() != AddressFamilyIPv6 {
panic("not ipv6")
}
}
}
func BenchmarkParseAddressDomain(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = ParseAddress("v2ray.com")
addr := ParseAddress("v2ray.com")
if addr.Family() != AddressFamilyDomain {
panic("not domain")
}
}
}

View File

@@ -99,8 +99,8 @@ func (c *connection) Write(b []byte) (int, error) {
}
l := len(b)
mb := buf.NewMultiBufferCap(int32(l)/buf.Size + 1)
common.Must2(mb.Write(b))
mb := make(buf.MultiBuffer, 0, l/buf.Size+1)
mb = buf.MergeBytes(mb, b)
return l, c.writer.WriteMultiBuffer(mb)
}

View File

@@ -83,7 +83,14 @@ func (d Destination) NetAddr() string {
// String returns the strings form of this Destination.
func (d Destination) String() string {
return d.Network.URLPrefix() + ":" + d.NetAddr()
prefix := "unknown:"
switch d.Network {
case Network_TCP:
prefix = "tcp:"
case Network_UDP:
prefix = "udp:"
}
return prefix + d.NetAddr()
}
// IsValid returns true if this Destination is valid.

View File

@@ -15,7 +15,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Endpoint of a network connection.
type Endpoint struct {

View File

@@ -1,29 +1,5 @@
package net
import (
"strings"
)
func ParseNetwork(nwStr string) Network {
if network, found := Network_value[nwStr]; found {
return Network(network)
}
switch strings.ToLower(nwStr) {
case "tcp":
return Network_TCP
case "udp":
return Network_UDP
default:
return Network_Unknown
}
}
func (n Network) AsList() *NetworkList {
return &NetworkList{
Network: []Network{n},
}
}
func (n Network) SystemString() string {
switch n {
case Network_TCP:
@@ -35,41 +11,12 @@ func (n Network) SystemString() string {
}
}
func (n Network) URLPrefix() string {
switch n {
case Network_TCP:
return "tcp"
case Network_UDP:
return "udp"
default:
return "unknown"
}
}
// HasNetwork returns true if the network list has a certain network.
func HasNetwork(list []Network, network Network) bool {
for _, value := range list {
if string(value) == string(network) {
if value == network {
return true
}
}
return false
}
// HasNetwork returns true if the given network is in v NetworkList.
func (l NetworkList) HasNetwork(network Network) bool {
for _, value := range l.Network {
if string(value) == string(network) {
return true
}
}
return false
}
func (l NetworkList) Get(idx int) Network {
return l.Network[idx]
}
// Size returns the number of networks in this network list.
func (l NetworkList) Size() int {
return len(l.Network)
}

View File

@@ -15,7 +15,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Network int32

View File

@@ -15,7 +15,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// PortRange represents a range of ports.
type PortRange struct {

View File

@@ -56,5 +56,6 @@ type TCPListener = net.TCPListener
type UnixListener = net.UnixListener
var ResolveUnixAddr = net.ResolveUnixAddr
var ResolveUDPAddr = net.ResolveUDPAddr
type Resolver = net.Resolver

View File

@@ -17,8 +17,8 @@ func Run(args []string, input io.Reader) (buf.MultiBuffer, error) {
return nil, newError("v2ctl doesn't exist").Base(err)
}
errBuffer := buf.MultiBuffer{}
outBuffer := buf.MultiBuffer{}
var errBuffer buf.MultiBufferContainer
var outBuffer buf.MultiBufferContainer
cmd := exec.Command(v2ctl, args...)
cmd.Stderr = &errBuffer
@@ -35,12 +35,10 @@ func Run(args []string, input io.Reader) (buf.MultiBuffer, error) {
if err := cmd.Wait(); err != nil {
msg := "failed to execute v2ctl"
if errBuffer.Len() > 0 {
msg += ": " + errBuffer.String()
msg += ": " + errBuffer.MultiBuffer.String()
}
errBuffer.Release()
outBuffer.Release()
return nil, newError(msg).Base(err)
}
return outBuffer, nil
return outBuffer.MultiBuffer, nil
}

View File

@@ -2,24 +2,28 @@ package protocol
import (
"io"
"unsafe"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/net"
"v2ray.com/core/common/serial"
"v2ray.com/core/common/task"
"v2ray.com/core/common/stack"
)
type AddressOption func(*AddressParser)
type AddressOption func(*option)
func PortThenAddress() AddressOption {
return func(p *AddressParser) {
return func(p *option) {
p.portFirst = true
}
}
func AddressFamilyByte(b byte, f net.AddressFamily) AddressOption {
return func(p *AddressParser) {
if b >= 16 {
panic("address family byte too big")
}
return func(p *option) {
p.addrTypeMap[b] = f
p.addrByteMap[f] = b
}
@@ -28,38 +32,127 @@ func AddressFamilyByte(b byte, f net.AddressFamily) AddressOption {
type AddressTypeParser func(byte) byte
func WithAddressTypeParser(atp AddressTypeParser) AddressOption {
return func(p *AddressParser) {
return func(p *option) {
p.typeParser = atp
}
}
// AddressParser is a utility for reading and writer addresses.
type AddressParser struct {
addrTypeMap map[byte]net.AddressFamily
addrByteMap map[net.AddressFamily]byte
type AddressSerializer interface {
ReadAddressPort(buffer *buf.Buffer, input io.Reader) (net.Address, net.Port, error)
WriteAddressPort(writer io.Writer, addr net.Address, port net.Port) error
}
const afInvalid = 255
type option struct {
addrTypeMap [16]net.AddressFamily
addrByteMap [16]byte
portFirst bool
typeParser AddressTypeParser
}
// NewAddressParser creates a new AddressParser
func NewAddressParser(options ...AddressOption) *AddressParser {
p := &AddressParser{
addrTypeMap: make(map[byte]net.AddressFamily, 8),
addrByteMap: make(map[net.AddressFamily]byte, 8),
func NewAddressParser(options ...AddressOption) AddressSerializer {
var o option
for i := range o.addrByteMap {
o.addrByteMap[i] = afInvalid
}
for i := range o.addrTypeMap {
o.addrTypeMap[i] = net.AddressFamily(afInvalid)
}
for _, opt := range options {
opt(p)
opt(&o)
}
return p
ap := &addressParser{
addrByteMap: o.addrByteMap,
addrTypeMap: o.addrTypeMap,
}
if o.typeParser != nil {
ap.typeParser = o.typeParser
}
if o.portFirst {
return portFirstAddressParser{ap: ap}
}
return portLastAddressParser{ap: ap}
}
func (p *AddressParser) readPort(b *buf.Buffer, reader io.Reader) (net.Port, error) {
type portFirstAddressParser struct {
ap *addressParser
}
func (p portFirstAddressParser) ReadAddressPort(buffer *buf.Buffer, input io.Reader) (net.Address, net.Port, error) {
if buffer == nil {
buffer = buf.New()
defer buffer.Release()
}
port, err := readPort(buffer, input)
if err != nil {
return nil, 0, err
}
addr, err := p.ap.readAddress(buffer, input)
if err != nil {
return nil, 0, err
}
return addr, port, nil
}
func (p portFirstAddressParser) WriteAddressPort(writer io.Writer, addr net.Address, port net.Port) error {
if err := writePort(writer, port); err != nil {
return err
}
return p.ap.writeAddress(writer, addr)
}
type portLastAddressParser struct {
ap *addressParser
}
func (p portLastAddressParser) ReadAddressPort(buffer *buf.Buffer, input io.Reader) (net.Address, net.Port, error) {
if buffer == nil {
buffer = buf.New()
defer buffer.Release()
}
addr, err := p.ap.readAddress(buffer, input)
if err != nil {
return nil, 0, err
}
port, err := readPort(buffer, input)
if err != nil {
return nil, 0, err
}
return addr, port, nil
}
func (p portLastAddressParser) WriteAddressPort(writer io.Writer, addr net.Address, port net.Port) error {
if err := p.ap.writeAddress(writer, addr); err != nil {
return err
}
return writePort(writer, port)
}
func readPort(b *buf.Buffer, reader io.Reader) (net.Port, error) {
if _, err := b.ReadFullFrom(reader, 2); err != nil {
return 0, err
}
return net.PortFromBytes(b.BytesFrom(-2)), nil
}
func writePort(writer io.Writer, port net.Port) error {
return common.Error2(serial.WriteUint16(writer, port.Value()))
}
func maybeIPPrefix(b byte) bool {
return b == '[' || (b >= '0' && b <= '9')
}
@@ -73,7 +166,13 @@ func isValidDomain(d string) bool {
return true
}
func (p *AddressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Address, error) {
type addressParser struct {
addrTypeMap [16]net.AddressFamily
addrByteMap [16]byte
typeParser AddressTypeParser
}
func (p *addressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Address, error) {
if _, err := b.ReadFullFrom(reader, 1); err != nil {
return nil, err
}
@@ -83,8 +182,12 @@ func (p *AddressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Addres
addrType = p.typeParser(addrType)
}
addrFamily, valid := p.addrTypeMap[addrType]
if !valid {
if addrType >= 16 {
return nil, newError("unknown address type: ", addrType)
}
addrFamily := p.addrTypeMap[addrType]
if addrFamily == net.AddressFamily(afInvalid) {
return nil, newError("unknown address type: ", addrType)
}
@@ -110,7 +213,7 @@ func (p *AddressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Addres
domain := string(b.BytesFrom(-domainLength))
if maybeIPPrefix(domain[0]) {
addr := net.ParseAddress(domain)
if addr.Family().IsIPv4() || addr.Family().IsIPv6() {
if addr.Family().IsIP() {
return addr, nil
}
}
@@ -123,93 +226,49 @@ func (p *AddressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Addres
}
}
// ReadAddressPort reads address and port from the given input.
func (p *AddressParser) ReadAddressPort(buffer *buf.Buffer, input io.Reader) (net.Address, net.Port, error) {
if buffer == nil {
buffer = buf.New()
defer buffer.Release()
}
var addr net.Address
var port net.Port
pTask := func() error {
lp, err := p.readPort(buffer, input)
if err != nil {
return err
}
port = lp
return nil
}
aTask := func() error {
a, err := p.readAddress(buffer, input)
if err != nil {
return err
}
addr = a
return nil
}
var err error
if p.portFirst {
err = task.Run(task.Sequential(pTask, aTask))()
} else {
err = task.Run(task.Sequential(aTask, pTask))()
}
if err != nil {
return nil, 0, err
}
return addr, port, nil
}
func (p *AddressParser) writePort(writer io.Writer, port net.Port) error {
return common.Error2(serial.WriteUint16(writer, port.Value()))
}
func (p *AddressParser) writeAddress(writer io.Writer, address net.Address) error {
tb, valid := p.addrByteMap[address.Family()]
if !valid {
//go:nosplit
func (p *addressParser) writeAddress(writer io.Writer, address net.Address) error {
tb := p.addrByteMap[address.Family()]
if tb == afInvalid {
return newError("unknown address family", address.Family())
}
switch address.Family() {
case net.AddressFamilyIPv4, net.AddressFamilyIPv6:
return task.Run(task.Sequential(func() error {
return common.Error2(writer.Write([]byte{tb}))
}, func() error {
return common.Error2(writer.Write(address.IP()))
}))()
var bytes stack.TwoBytes
s := bytes[:1]
s[0] = tb
p := uintptr(unsafe.Pointer(&s))
v := (*[]byte)(unsafe.Pointer(p))
if _, err := writer.Write(*v); err != nil {
return err
}
if _, err := writer.Write(address.IP()); err != nil {
return err
}
case net.AddressFamilyDomain:
domain := address.Domain()
if isDomainTooLong(domain) {
return newError("Super long domain is not supported: ", domain)
}
return task.Run(task.Sequential(func() error {
return common.Error2(writer.Write([]byte{tb, byte(len(domain))}))
}, func() error {
return common.Error2(writer.Write([]byte(domain)))
}))()
var bytes stack.TwoBytes
s := bytes[:]
s[0] = tb
s[1] = byte(len(domain))
p := uintptr(unsafe.Pointer(&s))
v := (*[]byte)(unsafe.Pointer(p))
if _, err := writer.Write(*v); err != nil {
return err
}
if _, err := io.WriteString(writer, domain); err != nil {
return err
}
default:
panic("Unknown family type.")
}
}
// WriteAddressPort writes address and port into the given writer.
func (p *AddressParser) WriteAddressPort(writer io.Writer, addr net.Address, port net.Port) error {
pTask := func() error {
return p.writePort(writer, port)
}
aTask := func() error {
return p.writeAddress(writer, addr)
}
if p.portFirst {
return task.Run(task.Sequential(pTask, aTask))()
}
return task.Run(task.Sequential(aTask, pTask))()
return nil
}

View File

@@ -4,9 +4,10 @@ import (
"bytes"
"testing"
"github.com/google/go-cmp/cmp"
"v2ray.com/core/common"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/compare"
"v2ray.com/core/common/net"
. "v2ray.com/core/common/protocol"
)
@@ -35,6 +36,12 @@ func TestAddressReading(t *testing.T) {
Address: net.IPAddress([]byte{0, 0, 0, 0}),
Port: net.Port(53),
},
{
Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4), PortThenAddress()},
Input: []byte{0, 53, 1, 0, 0, 0, 0},
Address: net.IPAddress([]byte{0, 0, 0, 0}),
Port: net.Port(53),
},
{
Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)},
Input: []byte{1, 0, 0, 0, 0},
@@ -128,9 +135,108 @@ func TestAddressWriting(t *testing.T) {
}
} else {
common.Must(err)
if err := compare.BytesEqualWithDetail(tc.Bytes, b.Bytes()); err != nil {
if diff := cmp.Diff(tc.Bytes, b.Bytes()); diff != "" {
t.Error(err)
}
}
}
}
func BenchmarkAddressReadingIPv4(b *testing.B) {
parser := NewAddressParser(AddressFamilyByte(0x01, net.AddressFamilyIPv4))
cache := buf.New()
defer cache.Release()
payload := buf.New()
defer payload.Release()
raw := []byte{1, 0, 0, 0, 0, 0, 53}
payload.Write(raw)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, err := parser.ReadAddressPort(cache, payload)
common.Must(err)
cache.Clear()
payload.Clear()
payload.Extend(int32(len(raw)))
}
}
func BenchmarkAddressReadingIPv6(b *testing.B) {
parser := NewAddressParser(AddressFamilyByte(0x04, net.AddressFamilyIPv6))
cache := buf.New()
defer cache.Release()
payload := buf.New()
defer payload.Release()
raw := []byte{4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 80}
payload.Write(raw)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, err := parser.ReadAddressPort(cache, payload)
common.Must(err)
cache.Clear()
payload.Clear()
payload.Extend(int32(len(raw)))
}
}
func BenchmarkAddressReadingDomain(b *testing.B) {
parser := NewAddressParser(AddressFamilyByte(0x03, net.AddressFamilyDomain))
cache := buf.New()
defer cache.Release()
payload := buf.New()
defer payload.Release()
raw := []byte{3, 9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 80}
payload.Write(raw)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, err := parser.ReadAddressPort(cache, payload)
common.Must(err)
cache.Clear()
payload.Clear()
payload.Extend(int32(len(raw)))
}
}
func BenchmarkAddressWritingIPv4(b *testing.B) {
parser := NewAddressParser(AddressFamilyByte(0x01, net.AddressFamilyIPv4))
writer := buf.New()
defer writer.Release()
b.ResetTimer()
for i := 0; i < b.N; i++ {
common.Must(parser.WriteAddressPort(writer, net.LocalHostIP, net.Port(80)))
writer.Clear()
}
}
func BenchmarkAddressWritingIPv6(b *testing.B) {
parser := NewAddressParser(AddressFamilyByte(0x04, net.AddressFamilyIPv6))
writer := buf.New()
defer writer.Release()
b.ResetTimer()
for i := 0; i < b.N; i++ {
common.Must(parser.WriteAddressPort(writer, net.LocalHostIPv6, net.Port(80)))
writer.Clear()
}
}
func BenchmarkAddressWritingDomain(b *testing.B) {
parser := NewAddressParser(AddressFamilyByte(0x02, net.AddressFamilyDomain))
writer := buf.New()
defer writer.Release()
b.ResetTimer()
for i := 0; i < b.N; i++ {
common.Must(parser.WriteAddressPort(writer, net.DomainAddress("www.v2ray.com"), net.Port(80)))
writer.Clear()
}
}

View File

@@ -15,7 +15,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type SecurityType int32

View File

@@ -3,7 +3,6 @@ package http_test
import (
"testing"
"v2ray.com/core/common/compare"
. "v2ray.com/core/common/protocol/http"
)
@@ -98,8 +97,8 @@ first_name=John&last_name=Doe&action=Submit`,
if err != nil {
t.Errorf("Expect no error but actually %s in test %v", err.Error(), test)
}
if err := compare.StringEqualWithDetail(header.Domain(), test.domain); err != nil {
t.Error(err)
if header.Domain() != test.domain {
t.Error("expected domain ", test.domain, " but got ", header.Domain())
}
}
}

View File

@@ -16,7 +16,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type ServerEndpoint struct {
Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`

View File

@@ -3,7 +3,6 @@ package tls_test
import (
"testing"
"v2ray.com/core/common/compare"
. "v2ray.com/core/common/protocol/tls"
)
@@ -94,8 +93,8 @@ func TestTLSHeaders(t *testing.T) {
if err != nil {
t.Errorf("Expect no error but actually %s in test %v", err.Error(), test)
}
if err := compare.StringEqualWithDetail(header.Domain(), test.domain); err != nil {
t.Error(err)
if header.Domain() != test.domain {
t.Error("expect domain ", test.domain, " but got ", header.Domain())
}
}
}

View File

@@ -16,7 +16,7 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// User is a generic user for all procotols.
type User struct {

View File

@@ -3,30 +3,45 @@ package serial
import (
"encoding/binary"
"io"
"unsafe"
"v2ray.com/core/common/stack"
)
// ReadUint16 reads first two bytes from the reader, and then coverts them to an uint16 value.
//go:nosplit
func ReadUint16(reader io.Reader) (uint16, error) {
var b [2]byte
if _, err := io.ReadFull(reader, b[:]); err != nil {
var b stack.TwoBytes
s := b[:]
p := uintptr(unsafe.Pointer(&s))
v := (*[]byte)(unsafe.Pointer(p))
if _, err := io.ReadFull(reader, *v); err != nil {
return 0, err
}
return binary.BigEndian.Uint16(b[:]), nil
return binary.BigEndian.Uint16(*v), nil
}
// WriteUint16 writes an uint16 value into writer.
//go:nosplit
func WriteUint16(writer io.Writer, value uint16) (int, error) {
var b [2]byte
binary.BigEndian.PutUint16(b[:], value)
return writer.Write(b[:])
}
func WriteUint32(writer io.Writer, value uint32) (int, error) {
var b [4]byte
binary.BigEndian.PutUint32(b[:], value)
return writer.Write(b[:])
var b stack.TwoBytes
s := b[:]
p := uintptr(unsafe.Pointer(&s))
v := (*[]byte)(unsafe.Pointer(p))
binary.BigEndian.PutUint16(*v, value)
return writer.Write(*v)
}
// WriteUint64 writes an uint64 value into writer.
//go:nosplit
func WriteUint64(writer io.Writer, value uint64) (int, error) {
var b [8]byte
binary.BigEndian.PutUint64(b[:], value)
return writer.Write(b[:])
var b stack.EightBytes
s := b[:]
p := uintptr(unsafe.Pointer(&s))
v := (*[]byte)(unsafe.Pointer(p))
binary.BigEndian.PutUint64(*v, value)
return writer.Write(*v)
}

Some files were not shown because too many files have changed in this diff Show More