You've already forked v2ray-core
Compare commits
169 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72f5e9de16 | ||
|
|
8567c66842 | ||
|
|
bea521537e | ||
|
|
d4613f156b | ||
|
|
0bc22154e5 | ||
|
|
c49b93b39e | ||
|
|
a6d81cc56d | ||
|
|
28fa84ce69 | ||
|
|
05f8de1b8f | ||
|
|
0f63be6340 | ||
|
|
3340f81d03 | ||
|
|
e392f8ed3d | ||
|
|
844f6937a4 | ||
|
|
c5a384195a | ||
|
|
e5314cfb56 | ||
|
|
057e271588 | ||
|
|
9379b2f934 | ||
|
|
7244dc928e | ||
|
|
afba7a45c9 | ||
|
|
61ad81c326 | ||
|
|
d649de172e | ||
|
|
fd060a0880 | ||
|
|
3b1aaa9a3d | ||
|
|
1d54fd7433 | ||
|
|
828cfac630 | ||
|
|
135bf169c0 | ||
|
|
90ab42b1cb | ||
|
|
fe0f32e0f1 | ||
|
|
f70712a1a3 | ||
|
|
45fbf6f059 | ||
|
|
e0093206e7 | ||
|
|
5ecec6afd8 | ||
|
|
4ff26a36ad | ||
|
|
ad4908be9e | ||
|
|
a1801b5d6f | ||
|
|
275ab2ae8c | ||
|
|
19926b8e4f | ||
|
|
6870ead73e | ||
|
|
18e7d107ba | ||
|
|
71a5cf09da | ||
|
|
7dee0a71c1 | ||
|
|
480175691c | ||
|
|
db83f23f00 | ||
|
|
39a092b178 | ||
|
|
9d17221266 | ||
|
|
39c84c8dcd | ||
|
|
9cad27e9bd | ||
|
|
4b6e12815d | ||
|
|
1cc3a4832d | ||
|
|
fa7ce36aa8 | ||
|
|
3a1cf06dc1 | ||
|
|
010964f272 | ||
|
|
e26ae20065 | ||
|
|
9d4a1df40b | ||
|
|
5bd47c89c4 | ||
|
|
3335f77c70 | ||
|
|
92a6699706 | ||
|
|
cfa7ee88ce | ||
|
|
096bbd2c51 | ||
|
|
3eac22c27d | ||
|
|
5279296f03 | ||
|
|
d2d0c69f17 | ||
|
|
e99dd29946 | ||
|
|
649f4da92a | ||
|
|
b2cbc369ac | ||
|
|
14c2d415d2 | ||
|
|
786290a31d | ||
|
|
84f8bca01c | ||
|
|
bb8cab9cc7 | ||
|
|
234c8081f4 | ||
|
|
769f770cf7 | ||
|
|
7b80322b60 | ||
|
|
d1f318c82a | ||
|
|
6543f5825b | ||
|
|
b9c8506c23 | ||
|
|
120058310a | ||
|
|
4b885f5775 | ||
|
|
8a82a3664c | ||
|
|
9a83561504 | ||
|
|
bb1efdebd1 | ||
|
|
8d8eb0f35a | ||
|
|
afc613f8f3 | ||
|
|
d013e8069d | ||
|
|
d675bb92df | ||
|
|
91227581e5 | ||
|
|
97b7c303b0 | ||
|
|
83c4b20b6e | ||
|
|
b1b8cb5ef5 | ||
|
|
5c4e33f759 | ||
|
|
bcd5d026fe | ||
|
|
cb7646f682 | ||
|
|
842a089dad | ||
|
|
0f324a613e | ||
|
|
b41513f644 | ||
|
|
27772a75a7 | ||
|
|
650f5e6350 | ||
|
|
09d3aaf2ab | ||
|
|
14646940a0 | ||
|
|
248099eca5 | ||
|
|
1089a887e0 | ||
|
|
48cac1733f | ||
|
|
2364f5f280 | ||
|
|
a01fdc29a6 | ||
|
|
db5259e75b | ||
|
|
f2f67132a7 | ||
|
|
deebb68597 | ||
|
|
a20262ef20 | ||
|
|
a5ed9e00ab | ||
|
|
bd7bc63fac | ||
|
|
151f316c32 | ||
|
|
6d770d6f30 | ||
|
|
9bc6a5813e | ||
|
|
4de776265b | ||
|
|
0fd7e9216a | ||
|
|
10b4bbf7c6 | ||
|
|
770a20d266 | ||
|
|
24288a74a2 | ||
|
|
ac4f868078 | ||
|
|
7560a99d7b | ||
|
|
6c7dcc35ab | ||
|
|
ff7e5a7cdb | ||
|
|
61b1013571 | ||
|
|
5c5816072e | ||
|
|
585608a796 | ||
|
|
16102271dd | ||
|
|
e8faa7d4e3 | ||
|
|
8c12fb6ff1 | ||
|
|
a14fae4b35 | ||
|
|
c5ccbe6b63 | ||
|
|
eeb588ffa0 | ||
|
|
29d978d157 | ||
|
|
953bfac572 | ||
|
|
6f7c30ad51 | ||
|
|
97f8727ad5 | ||
|
|
cd73486e93 | ||
|
|
d63e576a4c | ||
|
|
265f10e6c3 | ||
|
|
2de8a0be2e | ||
|
|
b40ae39d56 | ||
|
|
d1deebb6a9 | ||
|
|
0cfc13b029 | ||
|
|
9343b8879e | ||
|
|
4f167bb4d0 | ||
|
|
6dd5365b84 | ||
|
|
ab7930c09b | ||
|
|
c9fd7c272c | ||
|
|
9c0da3bab2 | ||
|
|
a8b3c74945 | ||
|
|
ee844a2b75 | ||
|
|
891bdd14fe | ||
|
|
956868ef78 | ||
|
|
3f3d00298a | ||
|
|
c26a4b407b | ||
|
|
90291730f5 | ||
|
|
cf21cb13dd | ||
|
|
df2d5b5d75 | ||
|
|
58221ebae1 | ||
|
|
2cc92920fa | ||
|
|
73d3be424b | ||
|
|
874fc87498 | ||
|
|
440cf090d6 | ||
|
|
9f56d48297 | ||
|
|
c98019a9c3 | ||
|
|
557330808d | ||
|
|
90b132b434 | ||
|
|
9d91a97926 | ||
|
|
4b2ee1185d | ||
|
|
03386f958d | ||
|
|
425ac88133 |
24
.github/ISSUE_TEMPLATE/crash_cn.md
vendored
24
.github/ISSUE_TEMPLATE/crash_cn.md
vendored
@@ -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
|
||||
// 在这里附上配置文件
|
||||
```
|
||||
|
||||
请预览一下你填的内容再提交。
|
||||
3
.github/ISSUE_TEMPLATE/crash_en.md
vendored
3
.github/ISSUE_TEMPLATE/crash_en.md
vendored
@@ -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
6
.github/ISSUE_TEMPLATE/feature_en.md
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: "Open a feature request to V2Ray"
|
||||
---
|
||||
|
||||
Please describe the new feature you want in detail.
|
||||
24
.travis.yml
24
.travis.yml
@@ -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
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -10,7 +10,8 @@
|
||||
"--disable=gas",
|
||||
"--disable=gocyclo",
|
||||
"--disable=gosec",
|
||||
"--disable=interfacer"
|
||||
"--disable=interfacer",
|
||||
"--deadline=5m"
|
||||
],
|
||||
"go.formatTool": "goimports",
|
||||
|
||||
|
||||
18
.vscode/tasks.json
vendored
18
.vscode/tasks.json
vendored
@@ -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}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
31
README.md
31
README.md
@@ -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
14
annotations.go
Normal 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
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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:"-"`
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
|
||||
202
app/dns/udpns.go
202
app/dns/udpns.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{}))
|
||||
}
|
||||
|
||||
@@ -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:"-"`
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
44
app/router/balancing.go
Normal 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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:"-"`
|
||||
|
||||
34
appveyor.yml
34
appveyor.yml
@@ -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/...
|
||||
25
azure-pipelines.template.yml
Normal file
25
azure-pipelines.template.yml
Normal 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
41
azure-pipelines.yml
Normal 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'
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{}))
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
30
common/errors/multi_error.go
Normal file
30
common/errors/multi_error.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
25
common/mux/frame_test.go
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -56,5 +56,6 @@ type TCPListener = net.TCPListener
|
||||
type UnixListener = net.UnixListener
|
||||
|
||||
var ResolveUnixAddr = net.ResolveUnixAddr
|
||||
var ResolveUDPAddr = net.ResolveUDPAddr
|
||||
|
||||
type Resolver = net.Resolver
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user