You've already forked v2ray-core
Compare commits
361 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
443e9d4725 | ||
|
|
c5e5a7265d | ||
|
|
6fe7463bf4 | ||
|
|
0040881b84 | ||
|
|
347ae8fd75 | ||
|
|
ca0ae50e62 | ||
|
|
27a29da7bf | ||
|
|
d1b44619b0 | ||
|
|
b72afc7c35 | ||
|
|
6a620d1c6d | ||
|
|
a509252c24 | ||
|
|
a29a6a586d | ||
|
|
c96533f278 | ||
|
|
f4aa50a160 | ||
|
|
2034d54bab | ||
|
|
2049759640 | ||
|
|
b02bd5b1d8 | ||
|
|
7dfa852677 | ||
|
|
03af6e6856 | ||
|
|
dbee8c620a | ||
|
|
ea39a6fde6 | ||
|
|
0857fdb0f8 | ||
|
|
f871ca63e2 | ||
|
|
723a693c78 | ||
|
|
edd9885d0e | ||
|
|
96ec5cfc58 | ||
|
|
6f5b54747e | ||
|
|
e304e2761d | ||
|
|
128e866021 | ||
|
|
08526a32ff | ||
|
|
3be5045dfc | ||
|
|
84a7a2ad61 | ||
|
|
0cd0033245 | ||
|
|
85d6e1ad13 | ||
|
|
1931820c4c | ||
|
|
7e499ee0b6 | ||
|
|
089de290ad | ||
|
|
a39132f4c2 | ||
|
|
bf2d45a83d | ||
|
|
00a747afe3 | ||
|
|
f072d26fe4 | ||
|
|
187a89484c | ||
|
|
b2cd65d6bc | ||
|
|
acb01e6177 | ||
|
|
45423add40 | ||
|
|
3f396078ab | ||
|
|
4aadaa7513 | ||
|
|
f45fa6e918 | ||
|
|
8d3da0ed8e | ||
|
|
e586f3c127 | ||
|
|
17ea740e7e | ||
|
|
066e5e216d | ||
|
|
72ee6436eb | ||
|
|
d5cd919cec | ||
|
|
f6d4e599bd | ||
|
|
b6a6c154a3 | ||
|
|
33e0cfe233 | ||
|
|
9e8ed30052 | ||
|
|
9523cb3ec3 | ||
|
|
631db6e69a | ||
|
|
ef2d49302d | ||
|
|
ffdce592ec | ||
|
|
906d846c2e | ||
|
|
c32f1a0152 | ||
|
|
991b2703dc | ||
|
|
0acd3b6d62 | ||
|
|
6f998fa18b | ||
|
|
476b3c68d2 | ||
|
|
05ed05b9a0 | ||
|
|
73afe9a001 | ||
|
|
c7b0264f9a | ||
|
|
fc69c77369 | ||
|
|
d602025908 | ||
|
|
c0a1198514 | ||
|
|
9a44ff3145 | ||
|
|
9434f5156c | ||
|
|
b9bf419f57 | ||
|
|
205f8b382d | ||
|
|
94d4b02191 | ||
|
|
b68075a99d | ||
|
|
4c63e9e168 | ||
|
|
f7d54d57c4 | ||
|
|
b50671f041 | ||
|
|
63390786ed | ||
|
|
a88a120938 | ||
|
|
e13ea7a54f | ||
|
|
12d13f1560 | ||
|
|
f5fd76ab85 | ||
|
|
566adec285 | ||
|
|
96c6012013 | ||
|
|
636effbe38 | ||
|
|
30041041d3 | ||
|
|
5e7d413404 | ||
|
|
be81d246f2 | ||
|
|
754e8b6e78 | ||
|
|
1e3ebeef59 | ||
|
|
56ce062154 | ||
|
|
a615afc906 | ||
|
|
5448ec1fe4 | ||
|
|
78ef65e17b | ||
|
|
e44b374e66 | ||
|
|
344e48e1bb | ||
|
|
16d1b42f59 | ||
|
|
6efff7654c | ||
|
|
34fb72e7c6 | ||
|
|
13e83c17a5 | ||
|
|
e170750d7e | ||
|
|
6ea8691a07 | ||
|
|
d1d153acb9 | ||
|
|
8ea3c13236 | ||
|
|
03ff683686 | ||
|
|
165e323fab | ||
|
|
6a1054a0f3 | ||
|
|
d5cb2e88c9 | ||
|
|
9c3ebc0453 | ||
|
|
968fffcffb | ||
|
|
2c259165ff | ||
|
|
e4acfe7f7e | ||
|
|
54b791821e | ||
|
|
0eaa3ba27a | ||
|
|
f8a614714c | ||
|
|
ab2cef1c09 | ||
|
|
84889117e9 | ||
|
|
e5c4fd3e1c | ||
|
|
490f77b4a0 | ||
|
|
7bc6d8af88 | ||
|
|
951b278ac7 | ||
|
|
6416c42bee | ||
|
|
a1af9ea839 | ||
|
|
3ad83da7cb | ||
|
|
32da784549 | ||
|
|
080f0abee9 | ||
|
|
3e84e4cb44 | ||
|
|
e1ce179b8e | ||
|
|
94fb16fdfa | ||
|
|
a2abdc3d2f | ||
|
|
8e98ede75d | ||
|
|
2169dcd7da | ||
|
|
6ca4eaf3b6 | ||
|
|
829355e6bf | ||
|
|
5714ae3935 | ||
|
|
c61db151df | ||
|
|
dc53575540 | ||
|
|
4c74e25319 | ||
|
|
48d4f5b7bb | ||
|
|
aa46e4ea05 | ||
|
|
d63330d15e | ||
|
|
5680c69146 | ||
|
|
a1f5839461 | ||
|
|
a6649fa0ff | ||
|
|
9d83fbcb7c | ||
|
|
7aa848dc02 | ||
|
|
7056b414e2 | ||
|
|
f050113925 | ||
|
|
98cc28a4c4 | ||
|
|
71c9bd678c | ||
|
|
313b3c1db0 | ||
|
|
812ac54290 | ||
|
|
af5a2ac0bc | ||
|
|
6e615c5863 | ||
|
|
c5170a36f0 | ||
|
|
0047910a81 | ||
|
|
d958b9884e | ||
|
|
aa6726a6d0 | ||
|
|
41b230994e | ||
|
|
beea2528ce | ||
|
|
6915095a0a | ||
|
|
27b8715b63 | ||
|
|
52c6b9b60b | ||
|
|
32d9984e28 | ||
|
|
5a5c9b891d | ||
|
|
2e63b29191 | ||
|
|
231f5fc140 | ||
|
|
7f661f5215 | ||
|
|
5ee87404fd | ||
|
|
e9fdc35c3c | ||
|
|
cf6d36ecd5 | ||
|
|
3925b62751 | ||
|
|
67ac925ee7 | ||
|
|
d12d5b0593 | ||
|
|
d37c01905c | ||
|
|
0812d7f9be | ||
|
|
5b70c33c77 | ||
|
|
882746380e | ||
|
|
25ef62434a | ||
|
|
87e1d66ff0 | ||
|
|
15f190d268 | ||
|
|
6257dfa247 | ||
|
|
0613d0cf22 | ||
|
|
ab710f2714 | ||
|
|
4beeba933c | ||
|
|
576b16c2f7 | ||
|
|
db3ca752ff | ||
|
|
0327113fb8 | ||
|
|
6847139ae3 | ||
|
|
3729ad2330 | ||
|
|
41225f8c12 | ||
|
|
6ec9032a54 | ||
|
|
1238304b1d | ||
|
|
b131f64f7b | ||
|
|
1cd5372a5a | ||
|
|
143829e065 | ||
|
|
299f866997 | ||
|
|
5fb9f10fc2 | ||
|
|
fb41233051 | ||
|
|
560b429902 | ||
|
|
aa38488712 | ||
|
|
8cd32a8267 | ||
|
|
ee0c25b0f3 | ||
|
|
7bf74761df | ||
|
|
a0d08e4961 | ||
|
|
16f5f81fe9 | ||
|
|
20f046af0b | ||
|
|
bac9304e05 | ||
|
|
5334d25e16 | ||
|
|
cd4416e7e5 | ||
|
|
b5ab714b52 | ||
|
|
f54da94d03 | ||
|
|
f7396aad8c | ||
|
|
2b966b039f | ||
|
|
7a84c930d4 | ||
|
|
87cb0d5dc9 | ||
|
|
233b73d1db | ||
|
|
f5d613f10a | ||
|
|
42ae2d804f | ||
|
|
e5394cdaa0 | ||
|
|
103c721626 | ||
|
|
9d21c78a01 | ||
|
|
b8160bbdae | ||
|
|
c4536d99d4 | ||
|
|
99516bc511 | ||
|
|
71ce8f4416 | ||
|
|
9b6dc6bcea | ||
|
|
22ce652a25 | ||
|
|
de4b4516ee | ||
|
|
bcf87d1592 | ||
|
|
14623e6fa4 | ||
|
|
18841209ee | ||
|
|
c8da174a7a | ||
|
|
0f368f8139 | ||
|
|
29e9473026 | ||
|
|
9144b66217 | ||
|
|
4df5d42011 | ||
|
|
f601962391 | ||
|
|
8a5f37dad7 | ||
|
|
f4ff2e9366 | ||
|
|
d0d63ee5b3 | ||
|
|
9315bb29fb | ||
|
|
579786166c | ||
|
|
7fdf0de06d | ||
|
|
cbb183ec58 | ||
|
|
e72a7cb96f | ||
|
|
29cb680a4b | ||
|
|
6d6158b089 | ||
|
|
0912574166 | ||
|
|
4f9a9d1f0c | ||
|
|
c07d478ab5 | ||
|
|
35b30e5bb0 | ||
|
|
70201d8879 | ||
|
|
a9ef8d27bd | ||
|
|
e51710d144 | ||
|
|
2fd963801c | ||
|
|
e84d52263d | ||
|
|
0f28c577ce | ||
|
|
6f0b4aaf31 | ||
|
|
a8792b2b60 | ||
|
|
77eee3ed37 | ||
|
|
12c40b7195 | ||
|
|
63c1f2106d | ||
|
|
8666d122f0 | ||
|
|
679d646571 | ||
|
|
1c06fc216a | ||
|
|
666dff02b0 | ||
|
|
098982d79a | ||
|
|
09f7e8b61e | ||
|
|
ffce784dce | ||
|
|
b17edf907e | ||
|
|
d9b59133cc | ||
|
|
8a83a0b30e | ||
|
|
9c3f2ada47 | ||
|
|
8b3875050d | ||
|
|
e732ba62a6 | ||
|
|
3f7b70a465 | ||
|
|
3b1f8b553b | ||
|
|
774c8f5d5f | ||
|
|
83c7e8491c | ||
|
|
e878973def | ||
|
|
3376ea078d | ||
|
|
08261e4f13 | ||
|
|
306ffabef0 | ||
|
|
045e1dcc53 | ||
|
|
aad10a75d3 | ||
|
|
5eae540e5c | ||
|
|
309b64e376 | ||
|
|
ea8f7e7506 | ||
|
|
798c870069 | ||
|
|
372a32a66f | ||
|
|
50ca869929 | ||
|
|
030fca10ba | ||
|
|
243e3ced89 | ||
|
|
a4d76dc394 | ||
|
|
6c31ff91e6 | ||
|
|
4b92e6b25b | ||
|
|
e0c22860f1 | ||
|
|
a2d8d55ba1 | ||
|
|
0a7deabd46 | ||
|
|
47338fba1e | ||
|
|
2f47074c98 | ||
|
|
f3975aafbf | ||
|
|
7a082ce63a | ||
|
|
40ebd1e9ae | ||
|
|
72fb5a256c | ||
|
|
b9c88b673b | ||
|
|
ff0cb89efa | ||
|
|
9a8bc3fc3c | ||
|
|
dfe1ac1f2b | ||
|
|
a86cd36ad2 | ||
|
|
6ce7b1d532 | ||
|
|
9713bf9adf | ||
|
|
957eaf38db | ||
|
|
43f6998d31 | ||
|
|
4ec96efe84 | ||
|
|
2c82f65189 | ||
|
|
202ac9bb56 | ||
|
|
0fac2084c7 | ||
|
|
3a6bf38686 | ||
|
|
21ab26f41f | ||
|
|
9457c4b349 | ||
|
|
da24d00367 | ||
|
|
9b07ffd68f | ||
|
|
b47c1ca609 | ||
|
|
c3aa839227 | ||
|
|
06aaaf970b | ||
|
|
8f4bd95bc3 | ||
|
|
3f9cb1136a | ||
|
|
87f664048b | ||
|
|
07c3f2d7cd | ||
|
|
3c4c14c89c | ||
|
|
46f76e55e5 | ||
|
|
e610faaff6 | ||
|
|
81cdaa0e4e | ||
|
|
a2268d2a7b | ||
|
|
e9f0eea0d1 | ||
|
|
f48f51c6b4 | ||
|
|
a4296f22df | ||
|
|
3156c4586c | ||
|
|
c75d840706 | ||
|
|
444808a51a | ||
|
|
ab39750ceb | ||
|
|
fc63f0432c | ||
|
|
3582b9d869 | ||
|
|
bbdc692a93 | ||
|
|
50957b9973 | ||
|
|
47b2fafb32 | ||
|
|
cfdda19834 | ||
|
|
5b23d25e35 | ||
|
|
28cf3aa0c7 | ||
|
|
ac58eed310 | ||
|
|
54cf3f75b1 | ||
|
|
0ea2678e72 | ||
|
|
bb503c6954 |
17
.github/ISSUE_TEMPLATE
vendored
Normal file
17
.github/ISSUE_TEMPLATE
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
提交 Issue 之前请先阅读 [Issue 指引](https://www.v2ray.com/zh_cn/chapter_01/issue.html),然后回答下面的问题,谢谢。
|
||||
Please answer the following questions before submitting your issue. Thank you.
|
||||
|
||||
1) 你正在使用哪个版本的 V2Ray?(如果服务器和客户端使用了不同版本,请注明)
|
||||
1) What version of V2Ray are you using?
|
||||
|
||||
2) 你的使用场景是什么?比如使用 Chrome 通过 Socks/VMess 代理观看 YouTube 视频。
|
||||
2) What your scenario of using V2Ray? E.g., Watching YouTube videos in Chrome via Socks/VMess proxy.
|
||||
|
||||
3) 你看到的不正常的现象是什么?
|
||||
3) What did you see?
|
||||
|
||||
4) 你期待看到的正确表现是怎样的?
|
||||
4) What do you expected to see instead?
|
||||
|
||||
5) 请附上你的配置文件。
|
||||
5) Please attach your configuration file.
|
||||
@@ -1,7 +1,7 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.6.2
|
||||
- 1.6.3
|
||||
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -1,6 +1,6 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"go.buildFlags": ["-tags", "json"],
|
||||
"go.lintFlags": ["-tags", "json"],
|
||||
"go.vetFlags": ["-tags", "json"]
|
||||
"editor.tabSize": 2,
|
||||
|
||||
"go.buildTags": "json"
|
||||
}
|
||||
30
.vscode/tasks.json
vendored
Normal file
30
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"command": "go",
|
||||
"isShellCommand": true,
|
||||
"showOutput": "always",
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "build",
|
||||
"args": ["-tags", "json", "github.com/v2ray/v2ray-core/..."],
|
||||
"isBuildCommand": true,
|
||||
"problemMatcher": {
|
||||
"owner": "go",
|
||||
"fileLocation": ["relative", "${workspaceRoot}"],
|
||||
"pattern": {
|
||||
"regexp": "^([^:]+\\.go):(\\d+):(.*)",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"message": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"taskName": "test",
|
||||
"args": ["-tags", "json", "github.com/v2ray/v2ray-core/..."],
|
||||
"isBuildCommand": false
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
}
|
||||
19
README.md
19
README.md
@@ -1,11 +1,18 @@
|
||||
# Project V2Ray
|
||||
|
||||
[](https://gitter.im/v2ray/v2ray-core?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://travis-ci.org/v2ray/v2ray-core)
|
||||
[](https://codecov.io/github/v2ray/v2ray-core?branch=master)
|
||||
[](https://goreportcard.com/report/v2ray/v2ray-core)
|
||||
[](https://godoc.org/github.com/v2ray/v2ray-core)
|
||||
[](https://codebeat.co/projects/github-com-v2ray-v2ray-core)
|
||||
[![Build Status][1]][2] [![codecov.io][3]][4] [![Go Report][5]][6] [![GoDoc][7]][8] [![codebeat][9]][10] [![Downloads][11]][12]
|
||||
[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://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://goreportcard.com/badge/github.com/v2ray/v2ray-core "Go Report badge"
|
||||
[6]: https://goreportcard.com/report/github.com/v2ray/v2ray-core "Go Report"
|
||||
[7]: https://godoc.org/github.com/v2ray/v2ray-core?status.svg "GoDoc badge"
|
||||
[8]: https://godoc.org/github.com/v2ray/v2ray-core "GoDoc"
|
||||
[9]: https://codebeat.co/badges/f2354ca8-3e24-463d-a2e3-159af73b2477 "Codebeat badge"
|
||||
[10]: https://codebeat.co/projects/github-com-v2ray-v2ray-core "Codebeat"
|
||||
[11]: https://img.shields.io/github/downloads/v2ray/v2ray-core/total.svg?maxAge=2592000
|
||||
[12]: https://github.com/v2ray/v2ray-core/releases
|
||||
|
||||
V2Ray 是一个模块化的代理软件包,它的目标是提供常用的代理软件模块,简化网络代理软件的开发。
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ func NewDefaultDispatcher(space app.Space) *DefaultDispatcher {
|
||||
func (this *DefaultDispatcher) Initialize(space app.Space) error {
|
||||
if !space.HasApp(proxyman.APP_ID_OUTBOUND_MANAGER) {
|
||||
log.Error("DefaultDispatcher: OutboundHandlerManager is not found in the space.")
|
||||
return app.ErrorMissingApplication
|
||||
return app.ErrMissingApplication
|
||||
}
|
||||
this.ohm = space.GetApp(proxyman.APP_ID_OUTBOUND_MANAGER).(proxyman.OutboundHandlerManager)
|
||||
|
||||
@@ -68,7 +68,7 @@ func (this *DefaultDispatcher) DispatchToOutbound(destination v2net.Destination)
|
||||
func (this *DefaultDispatcher) FilterPacketAndDispatch(destination v2net.Destination, link ray.OutboundRay, dispatcher proxy.OutboundHandler) {
|
||||
payload, err := link.OutboundInput().Read()
|
||||
if err != nil {
|
||||
log.Info("DefaultDispatcher: No payload to dispatch, stopping now.")
|
||||
log.Info("DefaultDispatcher: No payload towards ", destination, ", stopping now.")
|
||||
link.OutboundInput().Release()
|
||||
link.OutboundOutput().Release()
|
||||
return
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Hosts map[string]net.IP
|
||||
NameServers []v2net.Destination
|
||||
}
|
||||
|
||||
@@ -4,13 +4,16 @@ package dns
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
func (this *Config) UnmarshalJSON(data []byte) error {
|
||||
type JsonConfig struct {
|
||||
Servers []v2net.AddressJson `json:"servers"`
|
||||
Servers []v2net.AddressJson `json:"servers"`
|
||||
Hosts map[string]v2net.AddressJson `json:"hosts"`
|
||||
}
|
||||
jsonConfig := new(JsonConfig)
|
||||
if err := json.Unmarshal(data, jsonConfig); err != nil {
|
||||
@@ -21,5 +24,15 @@ func (this *Config) UnmarshalJSON(data []byte) error {
|
||||
this.NameServers[idx] = v2net.UDPDestination(server.Address, v2net.Port(53))
|
||||
}
|
||||
|
||||
if jsonConfig.Hosts != nil {
|
||||
this.Hosts = make(map[string]net.IP)
|
||||
for domain, ip := range jsonConfig.Hosts {
|
||||
if ip.Address.IsDomain() {
|
||||
return errors.New(ip.Address.String() + " is not an IP.")
|
||||
}
|
||||
this.Hosts[domain] = ip.Address.IP()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,13 +8,11 @@ import (
|
||||
|
||||
. "github.com/v2ray/v2ray-core/app/dns"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
netassert "github.com/v2ray/v2ray-core/common/net/testing/assert"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestConfigParsing(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rawJson := `{
|
||||
"servers": ["8.8.8.8"]
|
||||
@@ -24,7 +22,7 @@ func TestConfigParsing(t *testing.T) {
|
||||
err := json.Unmarshal([]byte(rawJson), config)
|
||||
assert.Error(err).IsNil()
|
||||
assert.Int(len(config.NameServers)).Equals(1)
|
||||
netassert.Destination(config.NameServers[0]).IsUDP()
|
||||
netassert.Address(config.NameServers[0].Address()).Equals(v2net.IPAddress([]byte{8, 8, 8, 8}))
|
||||
netassert.Port(config.NameServers[0].Port()).Equals(v2net.Port(53))
|
||||
assert.Destination(config.NameServers[0]).IsUDP()
|
||||
assert.Address(config.NameServers[0].Address()).Equals(v2net.IPAddress([]byte{8, 8, 8, 8}))
|
||||
assert.Port(config.NameServers[0].Port()).Equals(v2net.Port(53))
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/app/dispatcher"
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/transport/hub"
|
||||
"github.com/v2ray/v2ray-core/transport/internet/udp"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
@@ -21,6 +21,10 @@ const (
|
||||
CleanupThreshold = 512
|
||||
)
|
||||
|
||||
var (
|
||||
pseudoDestination = v2net.UDPDestination(v2net.LocalHostIP, v2net.Port(53))
|
||||
)
|
||||
|
||||
type ARecord struct {
|
||||
IPs []net.IP
|
||||
Expire time.Time
|
||||
@@ -39,7 +43,7 @@ type UDPNameServer struct {
|
||||
sync.Mutex
|
||||
address v2net.Destination
|
||||
requests map[uint16]*PendingRequest
|
||||
udpServer *hub.UDPServer
|
||||
udpServer *udp.UDPServer
|
||||
nextCleanup time.Time
|
||||
}
|
||||
|
||||
@@ -47,7 +51,7 @@ func NewUDPNameServer(address v2net.Destination, dispatcher dispatcher.PacketDis
|
||||
s := &UDPNameServer{
|
||||
address: address,
|
||||
requests: make(map[uint16]*PendingRequest),
|
||||
udpServer: hub.NewUDPServer(dispatcher),
|
||||
udpServer: udp.NewUDPServer(dispatcher),
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -80,13 +84,13 @@ func (this *UDPNameServer) AssignUnusedID(response chan<- *ARecord) uint16 {
|
||||
}
|
||||
|
||||
for {
|
||||
id = uint16(rand.Intn(65536))
|
||||
id = uint16(dice.Roll(65536))
|
||||
if _, found := this.requests[id]; found {
|
||||
continue
|
||||
}
|
||||
log.Debug("DNS: Add pending request id ", id)
|
||||
this.requests[id] = &PendingRequest{
|
||||
expire: time.Now().Add(time.Second * 16),
|
||||
expire: time.Now().Add(time.Second * 8),
|
||||
response: response,
|
||||
}
|
||||
break
|
||||
@@ -139,15 +143,13 @@ func (this *UDPNameServer) HandleResponse(dest v2net.Destination, payload *alloc
|
||||
close(request.response)
|
||||
}
|
||||
|
||||
func (this *UDPNameServer) QueryA(domain string) <-chan *ARecord {
|
||||
response := make(chan *ARecord, 1)
|
||||
|
||||
func (this *UDPNameServer) BuildQueryA(domain string, id uint16) *alloc.Buffer {
|
||||
buffer := alloc.NewBuffer()
|
||||
msg := new(dns.Msg)
|
||||
msg.Id = this.AssignUnusedID(response)
|
||||
msg.Id = id
|
||||
msg.RecursionDesired = true
|
||||
msg.Question = []dns.Question{
|
||||
dns.Question{
|
||||
{
|
||||
Name: dns.Fqdn(domain),
|
||||
Qtype: dns.TypeA,
|
||||
Qclass: dns.ClassINET,
|
||||
@@ -156,8 +158,32 @@ func (this *UDPNameServer) QueryA(domain string) <-chan *ARecord {
|
||||
writtenBuffer, _ := msg.PackBuffer(buffer.Value)
|
||||
buffer.Slice(0, len(writtenBuffer))
|
||||
|
||||
fakeDestination := v2net.UDPDestination(v2net.LocalHostIP, v2net.Port(53))
|
||||
this.udpServer.Dispatch(fakeDestination, this.address, buffer, this.HandleResponse)
|
||||
return buffer
|
||||
}
|
||||
|
||||
func (this *UDPNameServer) DispatchQuery(payload *alloc.Buffer) {
|
||||
this.udpServer.Dispatch(pseudoDestination, this.address, payload, this.HandleResponse)
|
||||
}
|
||||
|
||||
func (this *UDPNameServer) QueryA(domain string) <-chan *ARecord {
|
||||
response := make(chan *ARecord, 1)
|
||||
id := this.AssignUnusedID(response)
|
||||
|
||||
this.DispatchQuery(this.BuildQueryA(domain, id))
|
||||
|
||||
go func() {
|
||||
for i := 0; i < 2; i++ {
|
||||
time.Sleep(time.Second)
|
||||
this.Lock()
|
||||
_, found := this.requests[id]
|
||||
this.Unlock()
|
||||
if found {
|
||||
this.DispatchQuery(this.BuildQueryA(domain, id))
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ type DomainRecord struct {
|
||||
type CacheServer struct {
|
||||
sync.RWMutex
|
||||
space app.Space
|
||||
hosts map[string]net.IP
|
||||
records map[string]*DomainRecord
|
||||
servers []NameServer
|
||||
}
|
||||
@@ -31,11 +32,12 @@ func NewCacheServer(space app.Space, config *Config) *CacheServer {
|
||||
server := &CacheServer{
|
||||
records: make(map[string]*DomainRecord),
|
||||
servers: make([]NameServer, len(config.NameServers)),
|
||||
hosts: config.Hosts,
|
||||
}
|
||||
space.InitializeApplication(func() error {
|
||||
if !space.HasApp(dispatcher.APP_ID) {
|
||||
log.Error("DNS: Dispatcher is not found in the space.")
|
||||
return app.ErrorMissingApplication
|
||||
return app.ErrMissingApplication
|
||||
}
|
||||
|
||||
dispatcher := space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher)
|
||||
@@ -46,6 +48,9 @@ func NewCacheServer(space app.Space, config *Config) *CacheServer {
|
||||
server.servers[idx] = NewUDPNameServer(ns, dispatcher)
|
||||
}
|
||||
}
|
||||
if len(config.NameServers) == 0 {
|
||||
server.servers = append(server.servers, &LocalNameServer{})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return server
|
||||
@@ -67,6 +72,10 @@ func (this *CacheServer) GetCached(domain string) []net.IP {
|
||||
}
|
||||
|
||||
func (this *CacheServer) Get(domain string) []net.IP {
|
||||
if ip, found := this.hosts[domain]; found {
|
||||
return []net.IP{ip}
|
||||
}
|
||||
|
||||
domain = dns.Fqdn(domain)
|
||||
ips := this.GetCached(domain)
|
||||
if ips != nil {
|
||||
|
||||
@@ -10,19 +10,28 @@ import (
|
||||
. "github.com/v2ray/v2ray-core/app/dns"
|
||||
"github.com/v2ray/v2ray-core/app/proxyman"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
netassert "github.com/v2ray/v2ray-core/common/net/testing/assert"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/proxy/freedom"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
"github.com/v2ray/v2ray-core/transport/internet"
|
||||
)
|
||||
|
||||
func TestDnsAdd(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
space := app.NewSpace()
|
||||
|
||||
outboundHandlerManager := proxyman.NewDefaultOutboundHandlerManager()
|
||||
outboundHandlerManager.SetDefaultHandler(&freedom.FreedomConnection{})
|
||||
outboundHandlerManager.SetDefaultHandler(
|
||||
freedom.NewFreedomConnection(
|
||||
&freedom.Config{},
|
||||
space,
|
||||
&proxy.OutboundHandlerMeta{
|
||||
Address: v2net.AnyIP,
|
||||
StreamSettings: &internet.StreamSettings{
|
||||
Type: internet.StreamConnectionTypeRawTCP,
|
||||
},
|
||||
}))
|
||||
space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, outboundHandlerManager)
|
||||
space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space))
|
||||
|
||||
@@ -37,5 +46,5 @@ func TestDnsAdd(t *testing.T) {
|
||||
|
||||
ips := server.Get(domain)
|
||||
assert.Int(len(ips)).Equals(1)
|
||||
netassert.IP(ips[0].To4()).Equals(net.IP([]byte{127, 0, 0, 1}))
|
||||
assert.IP(ips[0].To4()).Equals(net.IP([]byte{127, 0, 0, 1}))
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ type ConfigObjectCreator func([]byte) (interface{}, error)
|
||||
var (
|
||||
configCache map[string]ConfigObjectCreator
|
||||
|
||||
ErrorRouterNotFound = errors.New("Router not found.")
|
||||
ErrRouterNotFound = errors.New("Router not found.")
|
||||
)
|
||||
|
||||
func RegisterRouterConfig(strategy string, creator ConfigObjectCreator) error {
|
||||
@@ -21,7 +21,7 @@ func RegisterRouterConfig(strategy string, creator ConfigObjectCreator) error {
|
||||
func CreateRouterConfig(strategy string, data []byte) (interface{}, error) {
|
||||
creator, found := configCache[strategy]
|
||||
if !found {
|
||||
return nil, ErrorRouterNotFound
|
||||
return nil, ErrRouterNotFound
|
||||
}
|
||||
return creator(data)
|
||||
}
|
||||
|
||||
@@ -33,5 +33,5 @@ func CreateRouter(name string, rawConfig interface{}, space app.Space) (Router,
|
||||
if factory, found := routerCache[name]; found {
|
||||
return factory.Create(rawConfig, space)
|
||||
}
|
||||
return nil, ErrorRouterNotFound
|
||||
return nil, ErrRouterNotFound
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,18 +6,17 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/app/router/rules"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestChinaIPJson(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rule := ParseRule([]byte(`{
|
||||
"type": "chinaip",
|
||||
"outboundTag": "x"
|
||||
}`))
|
||||
assert.StringLiteral(rule.Tag).Equals("x")
|
||||
assert.String(rule.Tag).Equals("x")
|
||||
assert.Bool(rule.Apply(makeDestination("121.14.1.189"))).IsTrue() // sina.com.cn
|
||||
assert.Bool(rule.Apply(makeDestination("101.226.103.106"))).IsTrue() // qq.com
|
||||
assert.Bool(rule.Apply(makeDestination("115.239.210.36"))).IsTrue() // image.baidu.com
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
. "github.com/v2ray/v2ray-core/app/router/rules"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
@@ -15,7 +14,7 @@ func makeDestination(ip string) v2net.Destination {
|
||||
}
|
||||
|
||||
func TestChinaIP(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rule := NewChinaIPRule("tag")
|
||||
assert.Bool(rule.Apply(makeDestination("121.14.1.189"))).IsTrue() // sina.com.cn
|
||||
|
||||
@@ -66,6 +66,7 @@ func init() {
|
||||
anySubDomain + "51" + dotLa,
|
||||
anySubDomain + "51credit" + dotCom,
|
||||
anySubDomain + "51cto" + dotCom,
|
||||
anySubDomain + "51jingying" + dotCom,
|
||||
anySubDomain + "51job" + dotCom,
|
||||
anySubDomain + "51jobcdn" + dotCom,
|
||||
anySubDomain + "51wendang" + dotCom,
|
||||
@@ -120,6 +121,7 @@ func init() {
|
||||
anySubDomain + "bitauto" + dotCom,
|
||||
anySubDomain + "bitautoimg" + dotCom,
|
||||
anySubDomain + "bobo" + dotCom,
|
||||
anySubDomain + "bootcss" + dotCom,
|
||||
anySubDomain + "btcfans" + dotCom,
|
||||
anySubDomain + "caiyunapp" + dotCom,
|
||||
anySubDomain + "ccb" + dotCom,
|
||||
@@ -135,8 +137,10 @@ func init() {
|
||||
anySubDomain + "chinacache" + dotNet,
|
||||
anySubDomain + "chinahr" + dotCom,
|
||||
anySubDomain + "chinamobile" + dotCom,
|
||||
anySubDomain + "chinapay" + dotCom,
|
||||
anySubDomain + "chinatranslation" + dotNet,
|
||||
anySubDomain + "chinaz" + dotCom,
|
||||
anySubDomain + "chiphell" + dotCom,
|
||||
anySubDomain + "chouti" + dotCom,
|
||||
anySubDomain + "chuangxin" + dotCom,
|
||||
anySubDomain + "chuansong" + dotMe,
|
||||
@@ -149,6 +153,7 @@ func init() {
|
||||
anySubDomain + "cnepub" + dotCom,
|
||||
anySubDomain + "cnzz" + dotCom,
|
||||
anySubDomain + "coding" + dotNet,
|
||||
anySubDomain + "coolapk" + dotCom,
|
||||
anySubDomain + "cqvip" + dotCom,
|
||||
anySubDomain + "csbew" + dotCom,
|
||||
anySubDomain + "csdn" + dotNet,
|
||||
@@ -180,7 +185,9 @@ func init() {
|
||||
anySubDomain + "emarbox" + dotCom,
|
||||
anySubDomain + "eoeandroid" + dotCom,
|
||||
anySubDomain + "etao" + dotCom,
|
||||
anySubDomain + "excelhome" + dotNet,
|
||||
anySubDomain + "fanli" + dotCom,
|
||||
anySubDomain + "feng" + dotCom,
|
||||
anySubDomain + "fengniao" + dotCom,
|
||||
anySubDomain + "fhldns" + dotCom,
|
||||
anySubDomain + "foxmail" + dotCom,
|
||||
@@ -188,6 +195,7 @@ func init() {
|
||||
anySubDomain + "geetest" + dotCom,
|
||||
anySubDomain + "geilicdn" + dotCom,
|
||||
anySubDomain + "getui" + dotCom,
|
||||
anySubDomain + "google-analytics" + dotCom,
|
||||
anySubDomain + "growingio" + dotCom,
|
||||
anySubDomain + "gtags" + dotNet,
|
||||
anySubDomain + "gwdang" + dotCom,
|
||||
@@ -196,6 +204,7 @@ func init() {
|
||||
anySubDomain + "haosou" + dotCom,
|
||||
anySubDomain + "hdslb" + dotCom,
|
||||
anySubDomain + "henha" + dotCom,
|
||||
anySubDomain + "henkuai" + dotCom,
|
||||
anySubDomain + "hexun" + dotCom,
|
||||
anySubDomain + "hichina" + dotCom,
|
||||
anySubDomain + "huanqiu" + dotCom,
|
||||
@@ -213,6 +222,7 @@ func init() {
|
||||
anySubDomain + "ifeng" + dotCom,
|
||||
anySubDomain + "ifengimg" + dotCom,
|
||||
anySubDomain + "ijinshan" + dotCom,
|
||||
anySubDomain + "ikafan" + dotCom,
|
||||
anySubDomain + "imedao" + dotCom,
|
||||
anySubDomain + "imgo" + dotTv,
|
||||
anySubDomain + "imooc" + dotCom,
|
||||
@@ -227,6 +237,7 @@ func init() {
|
||||
anySubDomain + "it168" + dotCom,
|
||||
anySubDomain + "it610" + dotCom,
|
||||
anySubDomain + "iteye" + dotCom,
|
||||
anySubDomain + "ithome" + dotCom,
|
||||
anySubDomain + "itjuzi" + dotCom,
|
||||
anySubDomain + "jandan" + dotNet,
|
||||
anySubDomain + "jd" + dotCom,
|
||||
@@ -240,6 +251,7 @@ func init() {
|
||||
anySubDomain + "jikexueyuan" + dotCom,
|
||||
anySubDomain + "jisuanke" + dotCom,
|
||||
anySubDomain + "jmstatic" + dotCom,
|
||||
anySubDomain + "jsdelivr" + dotNet,
|
||||
anySubDomain + "jstv" + dotCom,
|
||||
anySubDomain + "jumei" + dotCom,
|
||||
anySubDomain + "jyimg" + dotCom,
|
||||
@@ -303,6 +315,7 @@ func init() {
|
||||
anySubDomain + "mogucdn" + dotCom,
|
||||
anySubDomain + "mogujie" + dotCom,
|
||||
anySubDomain + "mop" + dotCom,
|
||||
anySubDomain + "mscbsc" + dotCom,
|
||||
anySubDomain + "mukewang" + dotCom,
|
||||
anySubDomain + "mydrivers" + dotCom,
|
||||
anySubDomain + "myshow360" + dotNet,
|
||||
@@ -319,6 +332,7 @@ func init() {
|
||||
anySubDomain + "onlinesjtu" + dotCom,
|
||||
anySubDomain + "oschina" + dotNet,
|
||||
anySubDomain + "paipai" + dotCom,
|
||||
anySubDomain + "pcbeta" + dotCom,
|
||||
anySubDomain + "pchome" + dotNet,
|
||||
anySubDomain + "pingan" + dotCom,
|
||||
anySubDomain + "pingplusplus" + dotCom,
|
||||
@@ -345,6 +359,7 @@ func init() {
|
||||
anySubDomain + "qunarzz" + dotCom,
|
||||
anySubDomain + "qzone" + dotCom,
|
||||
anySubDomain + "renren" + dotCom,
|
||||
anySubDomain + "ruanmei" + dotCom,
|
||||
anySubDomain + "ruby-china" + dotOrg,
|
||||
anySubDomain + "sandai" + dotNet,
|
||||
anySubDomain + "sanguosha" + dotCom,
|
||||
@@ -352,6 +367,7 @@ func init() {
|
||||
anySubDomain + "segmentfault" + dotCom,
|
||||
anySubDomain + "sf-express" + dotCom,
|
||||
anySubDomain + "sharejs" + dotCom,
|
||||
anySubDomain + "shmetro" + dotCom,
|
||||
anySubDomain + "shutcm" + dotCom,
|
||||
anySubDomain + "simei8" + dotCom,
|
||||
anySubDomain + "sina" + dotCom,
|
||||
@@ -391,6 +407,7 @@ func init() {
|
||||
anySubDomain + "tudouui" + dotCom,
|
||||
anySubDomain + "tuicool" + dotCom,
|
||||
anySubDomain + "tuniu" + dotCom,
|
||||
anySubDomain + "tutuapp" + dotCom,
|
||||
anySubDomain + "u17" + dotCom,
|
||||
anySubDomain + "useso" + dotCom,
|
||||
anySubDomain + "unionpay" + dotCom,
|
||||
@@ -401,14 +418,19 @@ func init() {
|
||||
anySubDomain + "v5875" + dotCom,
|
||||
anySubDomain + "vamaker" + dotCom,
|
||||
anySubDomain + "vancl" + dotCom,
|
||||
anySubDomain + "vcimg" + dotCom,
|
||||
anySubDomain + "vip" + dotCom,
|
||||
anySubDomain + "wallstreetcn" + dotCom,
|
||||
anySubDomain + "wandoujia" + dotCom,
|
||||
anySubDomain + "wdjimg" + dotCom,
|
||||
anySubDomain + "weand" + dotCom,
|
||||
anySubDomain + "webterren" + dotCom,
|
||||
anySubDomain + "weibo" + dotCom,
|
||||
anySubDomain + "weicaifu" + dotCom,
|
||||
anySubDomain + "weidian" + dotCom,
|
||||
anySubDomain + "weiphone" + dotCom,
|
||||
anySubDomain + "weiphone" + dotNet,
|
||||
anySubDomain + "weixing" + dotCom,
|
||||
anySubDomain + "weiyun" + dotCom,
|
||||
anySubDomain + "wonnder" + dotCom,
|
||||
anySubDomain + "worktile" + dotCom,
|
||||
@@ -451,6 +473,7 @@ func init() {
|
||||
anySubDomain + "zampda" + dotNet,
|
||||
anySubDomain + "zastatic" + dotCom,
|
||||
anySubDomain + "zbjimg" + dotCom,
|
||||
anySubDomain + "zdfans" + dotCom,
|
||||
anySubDomain + "zhenai" + dotCom,
|
||||
anySubDomain + "zhanqi" + dotTv,
|
||||
anySubDomain + "zhaopin" + dotCom,
|
||||
|
||||
@@ -6,18 +6,17 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/app/router/rules"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestChinaSitesJson(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rule := ParseRule([]byte(`{
|
||||
"type": "chinasites",
|
||||
"outboundTag": "y"
|
||||
}`))
|
||||
assert.StringLiteral(rule.Tag).Equals("y")
|
||||
assert.String(rule.Tag).Equals("y")
|
||||
assert.Bool(rule.Apply(makeDomainDestination("v.qq.com"))).IsTrue()
|
||||
assert.Bool(rule.Apply(makeDomainDestination("www.163.com"))).IsTrue()
|
||||
assert.Bool(rule.Apply(makeDomainDestination("ngacn.cc"))).IsTrue()
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
. "github.com/v2ray/v2ray-core/app/router/rules"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
@@ -14,7 +13,7 @@ func makeDomainDestination(domain string) v2net.Destination {
|
||||
}
|
||||
|
||||
func TestChinaSites(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rule := NewChinaSitesRule("tag")
|
||||
assert.Bool(rule.Apply(makeDomainDestination("v.qq.com"))).IsTrue()
|
||||
|
||||
@@ -3,9 +3,9 @@ package rules
|
||||
import (
|
||||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
)
|
||||
|
||||
type Condition interface {
|
||||
@@ -63,12 +63,12 @@ func (this *AnyCondition) Len() int {
|
||||
}
|
||||
|
||||
type PlainDomainMatcher struct {
|
||||
pattern serial.StringLiteral
|
||||
pattern string
|
||||
}
|
||||
|
||||
func NewPlainDomainMatcher(pattern string) *PlainDomainMatcher {
|
||||
return &PlainDomainMatcher{
|
||||
pattern: serial.StringLiteral(pattern),
|
||||
pattern: pattern,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ func (this *PlainDomainMatcher) Apply(dest v2net.Destination) bool {
|
||||
if !dest.Address().IsDomain() {
|
||||
return false
|
||||
}
|
||||
domain := serial.StringLiteral(dest.Address().Domain())
|
||||
return domain.Contains(this.pattern)
|
||||
domain := dest.Address().Domain()
|
||||
return strings.Contains(domain, this.pattern)
|
||||
}
|
||||
|
||||
type RegexpDomainMatcher struct {
|
||||
@@ -98,8 +98,8 @@ func (this *RegexpDomainMatcher) Apply(dest v2net.Destination) bool {
|
||||
if !dest.Address().IsDomain() {
|
||||
return false
|
||||
}
|
||||
domain := serial.StringLiteral(dest.Address().Domain())
|
||||
return this.pattern.MatchString(domain.ToLower().String())
|
||||
domain := dest.Address().Domain()
|
||||
return this.pattern.MatchString(strings.ToLower(domain))
|
||||
}
|
||||
|
||||
type CIDRMatcher struct {
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"strings"
|
||||
|
||||
router "github.com/v2ray/v2ray-core/app/router"
|
||||
"github.com/v2ray/v2ray-core/common/collect"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
)
|
||||
|
||||
type JsonRule struct {
|
||||
@@ -21,10 +21,10 @@ type JsonRule struct {
|
||||
func parseFieldRule(msg json.RawMessage) (*Rule, error) {
|
||||
type RawFieldRule struct {
|
||||
JsonRule
|
||||
Domain *serial.StringLiteralList `json:"domain"`
|
||||
IP *serial.StringLiteralList `json:"ip"`
|
||||
Port *v2net.PortRange `json:"port"`
|
||||
Network *v2net.NetworkList `json:"network"`
|
||||
Domain *collect.StringList `json:"domain"`
|
||||
IP *collect.StringList `json:"ip"`
|
||||
Port *v2net.PortRange `json:"port"`
|
||||
Network *v2net.NetworkList `json:"network"`
|
||||
}
|
||||
rawFieldRule := new(RawFieldRule)
|
||||
err := json.Unmarshal(msg, rawFieldRule)
|
||||
@@ -37,14 +37,14 @@ func parseFieldRule(msg json.RawMessage) (*Rule, error) {
|
||||
anyCond := NewAnyCondition()
|
||||
for _, rawDomain := range *(rawFieldRule.Domain) {
|
||||
var matcher Condition
|
||||
if strings.HasPrefix(rawDomain.String(), "regexp:") {
|
||||
rawMatcher, err := NewRegexpDomainMatcher(rawDomain.String()[7:])
|
||||
if strings.HasPrefix(rawDomain, "regexp:") {
|
||||
rawMatcher, err := NewRegexpDomainMatcher(rawDomain[7:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matcher = rawMatcher
|
||||
} else {
|
||||
matcher = NewPlainDomainMatcher(rawDomain.String())
|
||||
matcher = NewPlainDomainMatcher(rawDomain)
|
||||
}
|
||||
anyCond.Add(matcher)
|
||||
}
|
||||
@@ -54,7 +54,7 @@ func parseFieldRule(msg json.RawMessage) (*Rule, error) {
|
||||
if rawFieldRule.IP != nil && rawFieldRule.IP.Len() > 0 {
|
||||
anyCond := NewAnyCondition()
|
||||
for _, ipStr := range *(rawFieldRule.IP) {
|
||||
cidrMatcher, err := NewCIDRMatcher(ipStr.String())
|
||||
cidrMatcher, err := NewCIDRMatcher(ipStr)
|
||||
if err != nil {
|
||||
log.Error("Router: Invalid IP range in router rule: ", err)
|
||||
return nil, err
|
||||
@@ -128,10 +128,10 @@ func init() {
|
||||
Rules: make([]*Rule, len(jsonConfig.RuleList)),
|
||||
DomainStrategy: DomainAsIs,
|
||||
}
|
||||
domainStrategy := serial.StringLiteral(jsonConfig.DomainStrategy).ToLower()
|
||||
if domainStrategy.String() == "alwaysip" {
|
||||
domainStrategy := strings.ToLower(jsonConfig.DomainStrategy)
|
||||
if domainStrategy == "alwaysip" {
|
||||
config.DomainStrategy = AlwaysUseIP
|
||||
} else if domainStrategy.String() == "ipifnonmatch" {
|
||||
} else if domainStrategy == "ipifnonmatch" {
|
||||
config.DomainStrategy = UseIPIfNonMatch
|
||||
}
|
||||
for idx, rawRule := range jsonConfig.RuleList {
|
||||
|
||||
@@ -7,12 +7,11 @@ import (
|
||||
|
||||
. "github.com/v2ray/v2ray-core/app/router/rules"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestDomainRule(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rule := ParseRule([]byte(`{
|
||||
"type": "field",
|
||||
@@ -33,7 +32,7 @@ func TestDomainRule(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPRule(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rule := ParseRule([]byte(`{
|
||||
"type": "field",
|
||||
|
||||
@@ -2,59 +2,34 @@ package rules
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
"github.com/v2ray/v2ray-core/app/dns"
|
||||
"github.com/v2ray/v2ray-core/app/router"
|
||||
"github.com/v2ray/v2ray-core/common/collect"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorInvalidRule = errors.New("Invalid Rule")
|
||||
ErrorNoRuleApplicable = errors.New("No rule applicable")
|
||||
ErrInvalidRule = errors.New("Invalid Rule")
|
||||
ErrNoRuleApplicable = errors.New("No rule applicable")
|
||||
)
|
||||
|
||||
type cacheEntry struct {
|
||||
tag string
|
||||
err error
|
||||
validUntil time.Time
|
||||
}
|
||||
|
||||
func newCacheEntry(tag string, err error) *cacheEntry {
|
||||
this := &cacheEntry{
|
||||
tag: tag,
|
||||
err: err,
|
||||
}
|
||||
this.Extend()
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *cacheEntry) IsValid() bool {
|
||||
return this.validUntil.Before(time.Now())
|
||||
}
|
||||
|
||||
func (this *cacheEntry) Extend() {
|
||||
this.validUntil = time.Now().Add(time.Hour)
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
config *RouterRuleConfig
|
||||
cache *collect.ValidityMap
|
||||
cache *RoutingTable
|
||||
dnsServer dns.Server
|
||||
}
|
||||
|
||||
func NewRouter(config *RouterRuleConfig, space app.Space) *Router {
|
||||
r := &Router{
|
||||
config: config,
|
||||
cache: collect.NewValidityMap(3600),
|
||||
cache: NewRoutingTable(),
|
||||
}
|
||||
space.InitializeApplication(func() error {
|
||||
if !space.HasApp(dns.APP_ID) {
|
||||
log.Error("DNS: Router is not found in the space.")
|
||||
return app.ErrorMissingApplication
|
||||
return app.ErrMissingApplication
|
||||
}
|
||||
r.dnsServer = space.GetApp(dns.APP_ID).(dns.Server)
|
||||
return nil
|
||||
@@ -104,18 +79,18 @@ func (this *Router) takeDetourWithoutCache(dest v2net.Destination) (string, erro
|
||||
}
|
||||
}
|
||||
|
||||
return "", ErrorNoRuleApplicable
|
||||
return "", ErrNoRuleApplicable
|
||||
}
|
||||
|
||||
func (this *Router) TakeDetour(dest v2net.Destination) (string, error) {
|
||||
rawEntry := this.cache.Get(dest)
|
||||
if rawEntry == nil {
|
||||
destStr := dest.String()
|
||||
found, tag, err := this.cache.Get(destStr)
|
||||
if !found {
|
||||
tag, err := this.takeDetourWithoutCache(dest)
|
||||
this.cache.Set(dest, newCacheEntry(tag, err))
|
||||
this.cache.Set(destStr, tag, err)
|
||||
return tag, err
|
||||
}
|
||||
entry := rawEntry.(*cacheEntry)
|
||||
return entry.tag, entry.err
|
||||
return tag, err
|
||||
}
|
||||
|
||||
type RouterFactory struct {
|
||||
|
||||
@@ -11,12 +11,11 @@ import (
|
||||
"github.com/v2ray/v2ray-core/app/router"
|
||||
. "github.com/v2ray/v2ray-core/app/router/rules"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestSimpleRouter(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
config := &RouterRuleConfig{
|
||||
Rules: []*Rule{
|
||||
@@ -37,5 +36,5 @@ func TestSimpleRouter(t *testing.T) {
|
||||
|
||||
tag, err := r.TakeDetour(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80))
|
||||
assert.Error(err).IsNil()
|
||||
assert.StringLiteral(tag).Equals("test")
|
||||
assert.String(tag).Equals("test")
|
||||
}
|
||||
|
||||
70
app/router/rules/routing_table.go
Normal file
70
app/router/rules/routing_table.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RoutingEntry struct {
|
||||
tag string
|
||||
err error
|
||||
expire time.Time
|
||||
}
|
||||
|
||||
func (this *RoutingEntry) Extend() {
|
||||
this.expire = time.Now().Add(time.Hour)
|
||||
}
|
||||
|
||||
func (this *RoutingEntry) Expired() bool {
|
||||
return this.expire.Before(time.Now())
|
||||
}
|
||||
|
||||
type RoutingTable struct {
|
||||
sync.RWMutex
|
||||
table map[string]*RoutingEntry
|
||||
}
|
||||
|
||||
func NewRoutingTable() *RoutingTable {
|
||||
return &RoutingTable{
|
||||
table: make(map[string]*RoutingEntry),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *RoutingTable) Cleanup() {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
for key, value := range this.table {
|
||||
if value.Expired() {
|
||||
delete(this.table, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *RoutingTable) Set(destination string, tag string, err error) {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
entry := &RoutingEntry{
|
||||
tag: tag,
|
||||
err: err,
|
||||
}
|
||||
entry.Extend()
|
||||
this.table[destination] = entry
|
||||
|
||||
if len(this.table) > 1000 {
|
||||
go this.Cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *RoutingTable) Get(destination string) (bool, string, error) {
|
||||
this.RLock()
|
||||
defer this.RUnlock()
|
||||
|
||||
entry, found := this.table[destination]
|
||||
if !found {
|
||||
return false, "", nil
|
||||
}
|
||||
entry.Extend()
|
||||
return true, entry.tag, entry.err
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorMissingApplication = errors.New("App: Failed to found one or more applications.")
|
||||
ErrMissingApplication = errors.New("App: Failed to found one or more applications.")
|
||||
)
|
||||
|
||||
type ID int
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
// Package alloc provides a light-weight memory allocation mechanism.
|
||||
package alloc
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"io"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -18,12 +22,23 @@ type Buffer struct {
|
||||
offset int
|
||||
}
|
||||
|
||||
func CreateBuffer(container []byte, parent *BufferPool) *Buffer {
|
||||
b := new(Buffer)
|
||||
b.head = container
|
||||
b.pool = parent
|
||||
b.Value = b.head[defaultOffset:]
|
||||
b.offset = defaultOffset
|
||||
return b
|
||||
}
|
||||
|
||||
// Release recycles the buffer into an internal buffer pool.
|
||||
func (b *Buffer) Release() {
|
||||
if b == nil {
|
||||
if b == nil || b.head == nil {
|
||||
return
|
||||
}
|
||||
b.pool.Free(b)
|
||||
if b.pool != nil {
|
||||
b.pool.Free(b)
|
||||
}
|
||||
b.head = nil
|
||||
b.Value = nil
|
||||
b.pool = nil
|
||||
@@ -37,6 +52,12 @@ func (b *Buffer) Clear() *Buffer {
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Buffer) Reset() *Buffer {
|
||||
b.offset = defaultOffset
|
||||
b.Value = b.head
|
||||
return b
|
||||
}
|
||||
|
||||
// AppendBytes appends one or more bytes to the end of the buffer.
|
||||
func (b *Buffer) AppendBytes(bytes ...byte) *Buffer {
|
||||
b.Value = append(b.Value, bytes...)
|
||||
@@ -49,11 +70,22 @@ func (b *Buffer) Append(data []byte) *Buffer {
|
||||
return b
|
||||
}
|
||||
|
||||
// AppendString appends a given string to the end of the buffer.
|
||||
func (b *Buffer) AppendString(s string) *Buffer {
|
||||
b.Value = append(b.Value, s...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Buffer) AppendUint16(v uint16) *Buffer {
|
||||
b.Value = serial.Uint16ToBytes(v, b.Value)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Buffer) AppendUint32(v uint32) *Buffer {
|
||||
b.Value = serial.Uint32ToBytes(v, b.Value)
|
||||
return b
|
||||
}
|
||||
|
||||
// Prepend prepends bytes in front of the buffer. Caller must ensure total bytes prepended is
|
||||
// no more than 16 bytes.
|
||||
func (b *Buffer) Prepend(data []byte) *Buffer {
|
||||
@@ -62,6 +94,28 @@ func (b *Buffer) Prepend(data []byte) *Buffer {
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Buffer) PrependBytes(data ...byte) *Buffer {
|
||||
return b.Prepend(data)
|
||||
}
|
||||
|
||||
func (b *Buffer) PrependUint16(v uint16) *Buffer {
|
||||
b.SliceBack(2)
|
||||
serial.Uint16ToBytes(v, b.Value[:0])
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Buffer) PrependUint32(v uint32) *Buffer {
|
||||
b.SliceBack(4)
|
||||
serial.Uint32ToBytes(v, b.Value[:0])
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Buffer) PrependHash(h hash.Hash) *Buffer {
|
||||
b.SliceBack(h.Size())
|
||||
h.Sum(b.Value[:0])
|
||||
return b
|
||||
}
|
||||
|
||||
// Bytes returns the content bytes of this Buffer.
|
||||
func (b *Buffer) Bytes() []byte {
|
||||
return b.Value
|
||||
@@ -86,7 +140,7 @@ func (b *Buffer) SliceFrom(from int) *Buffer {
|
||||
func (b *Buffer) SliceBack(offset int) *Buffer {
|
||||
newoffset := b.offset - offset
|
||||
if newoffset < 0 {
|
||||
newoffset = 0
|
||||
panic("Negative buffer offset.")
|
||||
}
|
||||
b.Value = b.head[newoffset : b.offset+len(b.Value)]
|
||||
b.offset = newoffset
|
||||
@@ -159,3 +213,19 @@ func NewBuffer() *Buffer {
|
||||
func NewLargeBuffer() *Buffer {
|
||||
return largePool.Allocate()
|
||||
}
|
||||
|
||||
func NewBufferWithSize(size int) *Buffer {
|
||||
if size <= SmallBufferSize {
|
||||
return NewSmallBuffer()
|
||||
}
|
||||
|
||||
if size <= BufferSize {
|
||||
return NewBuffer()
|
||||
}
|
||||
|
||||
return NewLargeBuffer()
|
||||
}
|
||||
|
||||
func NewLocalBuffer(size int) *Buffer {
|
||||
return CreateBuffer(make([]byte, size), nil)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func NewBufferPool(bufferSize, poolSize int) *BufferPool {
|
||||
New: func() interface{} { return make([]byte, bufferSize) },
|
||||
},
|
||||
}
|
||||
for i := 0; i < poolSize/2; i++ {
|
||||
for i := 0; i < poolSize; i++ {
|
||||
pool.chain <- make([]byte, bufferSize)
|
||||
}
|
||||
return pool
|
||||
@@ -29,12 +29,7 @@ func (p *BufferPool) Allocate() *Buffer {
|
||||
default:
|
||||
b = p.allocator.Get().([]byte)
|
||||
}
|
||||
return &Buffer{
|
||||
head: b,
|
||||
pool: p,
|
||||
Value: b[defaultOffset:],
|
||||
offset: defaultOffset,
|
||||
}
|
||||
return CreateBuffer(b, p)
|
||||
}
|
||||
|
||||
func (p *BufferPool) Free(buffer *Buffer) {
|
||||
@@ -50,10 +45,11 @@ func (p *BufferPool) Free(buffer *Buffer) {
|
||||
}
|
||||
|
||||
const (
|
||||
SmallBufferSize = 1600 - defaultOffset
|
||||
BufferSize = 8*1024 - defaultOffset
|
||||
LargeBufferSize = 64*1024 - defaultOffset
|
||||
)
|
||||
|
||||
var smallPool = NewBufferPool(1024, 64)
|
||||
var mediumPool = NewBufferPool(8*1024, 128)
|
||||
var largePool = NewBufferPool(64*1024, 64)
|
||||
var smallPool = NewBufferPool(1600, 1024)
|
||||
var mediumPool = NewBufferPool(8*1024, 256)
|
||||
var largePool = NewBufferPool(64*1024, 32)
|
||||
|
||||
@@ -4,12 +4,11 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/alloc"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestBufferClear(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := NewBuffer().Clear()
|
||||
defer buffer.Release()
|
||||
@@ -23,7 +22,7 @@ func TestBufferClear(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBufferIsFull(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := NewBuffer()
|
||||
defer buffer.Release()
|
||||
@@ -35,7 +34,7 @@ func TestBufferIsFull(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBufferPrepend(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := NewBuffer().Clear()
|
||||
defer buffer.Release()
|
||||
@@ -51,11 +50,11 @@ func TestBufferPrepend(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBufferString(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := NewBuffer().Clear()
|
||||
defer buffer.Release()
|
||||
|
||||
buffer.AppendString("Test String")
|
||||
assert.String(buffer).Equals("Test String")
|
||||
assert.String(buffer.String()).Equals("Test String")
|
||||
}
|
||||
|
||||
12
common/collect/string_list.go
Normal file
12
common/collect/string_list.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package collect
|
||||
|
||||
type StringList []string
|
||||
|
||||
func NewStringList(raw []string) *StringList {
|
||||
list := StringList(raw)
|
||||
return &list
|
||||
}
|
||||
|
||||
func (this *StringList) Len() int {
|
||||
return len(*this)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build json
|
||||
|
||||
package serial
|
||||
package collect
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -8,17 +8,17 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (this *StringLiteralList) UnmarshalJSON(data []byte) error {
|
||||
func (this *StringList) UnmarshalJSON(data []byte) error {
|
||||
var strarray []string
|
||||
if err := json.Unmarshal(data, &strarray); err == nil {
|
||||
*this = *NewStringLiteralList(strarray)
|
||||
*this = *NewStringList(strarray)
|
||||
return nil
|
||||
}
|
||||
|
||||
var rawstr string
|
||||
if err := json.Unmarshal(data, &rawstr); err == nil {
|
||||
strlist := strings.Split(rawstr, ",")
|
||||
*this = *NewStringLiteralList(strlist)
|
||||
*this = *NewStringList(strlist)
|
||||
return nil
|
||||
}
|
||||
return errors.New("Unknown format of a string list: " + string(data))
|
||||
@@ -1,21 +1,20 @@
|
||||
// +build json
|
||||
|
||||
package serial_test
|
||||
package collect_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/serial"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
. "github.com/v2ray/v2ray-core/common/collect"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestStringListUnmarshalError(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rawJson := `1234`
|
||||
list := new(StringLiteralList)
|
||||
list := new(StringList)
|
||||
err := json.Unmarshal([]byte(rawJson), list)
|
||||
assert.Error(err).IsNotNil()
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package collect
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
)
|
||||
|
||||
type Validity interface {
|
||||
IsValid() bool
|
||||
}
|
||||
|
||||
type entry struct {
|
||||
key string
|
||||
value Validity
|
||||
}
|
||||
|
||||
type ValidityMap struct {
|
||||
sync.RWMutex
|
||||
cache map[string]Validity
|
||||
opCount int32
|
||||
}
|
||||
|
||||
func NewValidityMap(cleanupIntervalSec int) *ValidityMap {
|
||||
instance := &ValidityMap{
|
||||
cache: make(map[string]Validity),
|
||||
}
|
||||
return instance
|
||||
}
|
||||
|
||||
func (this *ValidityMap) cleanup() {
|
||||
entry2Remove := make([]entry, 0, 128)
|
||||
this.RLock()
|
||||
for key, value := range this.cache {
|
||||
if !value.IsValid() {
|
||||
entry2Remove = append(entry2Remove, entry{
|
||||
key: key,
|
||||
value: value,
|
||||
})
|
||||
}
|
||||
}
|
||||
this.RUnlock()
|
||||
|
||||
for _, entry := range entry2Remove {
|
||||
if !entry.value.IsValid() {
|
||||
this.Lock()
|
||||
delete(this.cache, entry.key)
|
||||
this.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ValidityMap) Set(key serial.String, value Validity) {
|
||||
this.Lock()
|
||||
this.cache[key.String()] = value
|
||||
this.Unlock()
|
||||
opCount := atomic.AddInt32(&this.opCount, 1)
|
||||
if opCount > 1000 {
|
||||
atomic.StoreInt32(&this.opCount, 0)
|
||||
go this.cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ValidityMap) Get(key serial.String) Validity {
|
||||
this.RLock()
|
||||
defer this.RUnlock()
|
||||
if value, found := this.cache[key.String()]; found {
|
||||
return value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -7,7 +7,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorAlreadyReleased = errors.New("Object already released.")
|
||||
ErrObjectReleased = errors.New("Object already released.")
|
||||
ErrBadConfiguration = errors.New("Bad configuration.")
|
||||
)
|
||||
|
||||
// Releasable interface is for those types that can release its members.
|
||||
|
||||
@@ -5,11 +5,15 @@ import (
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
// NewAesDecryptionStream creates a new AES encryption stream based on given key and IV.
|
||||
// Caller must ensure the length of key and IV is either 16, 24 or 32 bytes.
|
||||
func NewAesDecryptionStream(key []byte, iv []byte) cipher.Stream {
|
||||
aesBlock, _ := aes.NewCipher(key)
|
||||
return cipher.NewCFBDecrypter(aesBlock, iv)
|
||||
}
|
||||
|
||||
// NewAesEncryptionStream creates a new AES description stream based on given key and IV.
|
||||
// Caller must ensure the length of key and IV is either 16, 24 or 32 bytes.
|
||||
func NewAesEncryptionStream(key []byte, iv []byte) cipher.Stream {
|
||||
aesBlock, _ := aes.NewCipher(key)
|
||||
return cipher.NewCFBEncrypter(aesBlock, iv)
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"github.com/v2ray/v2ray-core/common/crypto/internal"
|
||||
)
|
||||
|
||||
// NewChaCha20Stream creates a new Chacha20 encryption/descryption stream based on give key and IV.
|
||||
// Caller must ensure the length of key is 32 bytes, and length of IV is either 8 or 12 bytes.
|
||||
func NewChaCha20Stream(key []byte, iv []byte) cipher.Stream {
|
||||
return internal.NewChaCha20Stream(key, iv, 20)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/crypto"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
@@ -19,7 +18,7 @@ func mustDecodeHex(s string) []byte {
|
||||
}
|
||||
|
||||
func TestChaCha20Stream(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
var cases = []struct {
|
||||
key []byte
|
||||
@@ -58,7 +57,7 @@ func TestChaCha20Stream(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestChaCha20Decoding(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
key := make([]byte, 32)
|
||||
rand.Read(key)
|
||||
|
||||
@@ -21,7 +21,7 @@ func NewCryptionReader(stream cipher.Stream, reader io.Reader) *CryptionReader {
|
||||
|
||||
func (this *CryptionReader) Read(data []byte) (int, error) {
|
||||
if this.reader == nil {
|
||||
return 0, common.ErrorAlreadyReleased
|
||||
return 0, common.ErrObjectReleased
|
||||
}
|
||||
nBytes, err := this.reader.Read(data)
|
||||
if nBytes > 0 {
|
||||
@@ -49,7 +49,7 @@ func NewCryptionWriter(stream cipher.Stream, writer io.Writer) *CryptionWriter {
|
||||
|
||||
func (this *CryptionWriter) Write(data []byte) (int, error) {
|
||||
if this.writer == nil {
|
||||
return 0, common.ErrorAlreadyReleased
|
||||
return 0, common.ErrObjectReleased
|
||||
}
|
||||
this.stream.XORKeyStream(data, data)
|
||||
return this.writer.Write(data)
|
||||
|
||||
@@ -5,12 +5,11 @@ import (
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
. "github.com/v2ray/v2ray-core/common/io"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestBufferedReader(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
content := alloc.NewLargeBuffer()
|
||||
len := content.Len()
|
||||
|
||||
@@ -22,6 +22,28 @@ func NewBufferedWriter(rawWriter io.Writer) *BufferedWriter {
|
||||
}
|
||||
}
|
||||
|
||||
func (this *BufferedWriter) ReadFrom(reader io.Reader) (int64, error) {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
if this.writer == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
totalBytes := int64(0)
|
||||
for {
|
||||
nBytes, err := this.buffer.FillFrom(reader)
|
||||
totalBytes += int64(nBytes)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return totalBytes, nil
|
||||
}
|
||||
return totalBytes, err
|
||||
}
|
||||
this.FlushWithoutLock()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *BufferedWriter) Write(b []byte) (int, error) {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
@@ -35,7 +57,7 @@ func (this *BufferedWriter) Write(b []byte) (int, error) {
|
||||
}
|
||||
nBytes, _ := this.buffer.Write(b)
|
||||
if this.buffer.IsFull() {
|
||||
go this.Flush()
|
||||
this.FlushWithoutLock()
|
||||
}
|
||||
return nBytes, nil
|
||||
}
|
||||
@@ -43,10 +65,15 @@ func (this *BufferedWriter) Write(b []byte) (int, error) {
|
||||
func (this *BufferedWriter) Flush() error {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
if this.writer == nil {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
return this.FlushWithoutLock()
|
||||
}
|
||||
|
||||
func (this *BufferedWriter) FlushWithoutLock() error {
|
||||
defer this.buffer.Clear()
|
||||
for !this.buffer.IsEmpty() {
|
||||
nBytes, err := this.writer.Write(this.buffer.Value)
|
||||
|
||||
@@ -5,12 +5,11 @@ import (
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
. "github.com/v2ray/v2ray-core/common/io"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestBufferedWriter(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
content := alloc.NewLargeBuffer().Clear()
|
||||
|
||||
|
||||
48
common/io/chain_writer.go
Normal file
48
common/io/chain_writer.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
)
|
||||
|
||||
type ChainWriter struct {
|
||||
sync.Mutex
|
||||
writer Writer
|
||||
}
|
||||
|
||||
func NewChainWriter(writer Writer) *ChainWriter {
|
||||
return &ChainWriter{
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ChainWriter) Write(payload []byte) (int, error) {
|
||||
if this.writer == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
size := len(payload)
|
||||
buffer := alloc.NewBufferWithSize(size).Clear()
|
||||
buffer.Append(payload)
|
||||
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
if this.writer == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
err := this.writer.Write(buffer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func (this *ChainWriter) Release() {
|
||||
this.Lock()
|
||||
this.writer.Release()
|
||||
this.writer = nil
|
||||
this.Unlock()
|
||||
}
|
||||
64
common/io/chan_reader.go
Normal file
64
common/io/chan_reader.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
)
|
||||
|
||||
type ChanReader struct {
|
||||
sync.Mutex
|
||||
stream Reader
|
||||
current *alloc.Buffer
|
||||
eof bool
|
||||
}
|
||||
|
||||
func NewChanReader(stream Reader) *ChanReader {
|
||||
this := &ChanReader{
|
||||
stream: stream,
|
||||
}
|
||||
this.Fill()
|
||||
return this
|
||||
}
|
||||
|
||||
// @Private
|
||||
func (this *ChanReader) Fill() {
|
||||
b, err := this.stream.Read()
|
||||
this.current = b
|
||||
if err != nil {
|
||||
this.eof = true
|
||||
this.current = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ChanReader) Read(b []byte) (int, error) {
|
||||
if this.eof {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
if this.current == nil {
|
||||
this.Fill()
|
||||
if this.eof {
|
||||
return 0, io.EOF
|
||||
}
|
||||
}
|
||||
nBytes, err := this.current.Read(b)
|
||||
if this.current.IsEmpty() {
|
||||
this.current.Release()
|
||||
this.current = nil
|
||||
}
|
||||
return nBytes, err
|
||||
}
|
||||
|
||||
func (this *ChanReader) Release() {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
this.eof = true
|
||||
this.current.Release()
|
||||
this.current = nil
|
||||
this.stream = nil
|
||||
}
|
||||
@@ -7,17 +7,6 @@ import (
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
)
|
||||
|
||||
// ReadFrom reads from a reader and put all content to a buffer.
|
||||
// If buffer is nil, ReadFrom creates a new normal buffer.
|
||||
func ReadFrom(reader io.Reader, buffer *alloc.Buffer) (*alloc.Buffer, error) {
|
||||
if buffer == nil {
|
||||
buffer = alloc.NewBuffer()
|
||||
}
|
||||
nBytes, err := reader.Read(buffer.Value)
|
||||
buffer.Slice(0, nBytes)
|
||||
return buffer, err
|
||||
}
|
||||
|
||||
// Reader extends io.Reader with alloc.Buffer.
|
||||
type Reader interface {
|
||||
common.Releasable
|
||||
@@ -42,7 +31,12 @@ func NewAdaptiveReader(reader io.Reader) *AdaptiveReader {
|
||||
|
||||
// Read implements Reader.Read().
|
||||
func (this *AdaptiveReader) Read() (*alloc.Buffer, error) {
|
||||
buffer, err := ReadFrom(this.reader, this.allocate())
|
||||
buffer := this.allocate().Clear()
|
||||
_, err := buffer.FillFrom(this.reader)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if buffer.Len() >= alloc.BufferSize {
|
||||
this.allocate = alloc.NewLargeBuffer
|
||||
@@ -50,10 +44,6 @@ func (this *AdaptiveReader) Read() (*alloc.Buffer, error) {
|
||||
this.allocate = alloc.NewBuffer
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return nil, err
|
||||
}
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,11 @@ import (
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
. "github.com/v2ray/v2ray-core/common/io"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestAdaptiveReader(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rawContent := make([]byte, 1024*1024)
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ func Pipe(reader Reader, writer Writer) error {
|
||||
err = writer.Write(buffer)
|
||||
if err != nil {
|
||||
log.Debug("IO: Pipe exits as ", err)
|
||||
buffer.Release()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,11 @@ import (
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
. "github.com/v2ray/v2ray-core/common/io"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestAdaptiveWriter(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
lb := alloc.NewLargeBuffer()
|
||||
rand.Read(lb.Value)
|
||||
|
||||
61
common/loader/json_conf.go
Normal file
61
common/loader/json_conf.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// +build json
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
)
|
||||
|
||||
type JSONConfigLoader struct {
|
||||
*BaseConfigLoader
|
||||
idKey string
|
||||
configKey string
|
||||
}
|
||||
|
||||
func NewJSONConfigLoader(idKey string, configKey string) *JSONConfigLoader {
|
||||
return &JSONConfigLoader{
|
||||
idKey: idKey,
|
||||
configKey: configKey,
|
||||
BaseConfigLoader: NewBaseConfigLoader(),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *JSONConfigLoader) LoadWithID(raw []byte, id string) (interface{}, error) {
|
||||
config, err := this.CreateConfig(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(raw, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (this *JSONConfigLoader) Load(raw []byte) (interface{}, error) {
|
||||
var obj map[string]json.RawMessage
|
||||
if err := json.Unmarshal(raw, &obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawID, found := obj[this.idKey]
|
||||
if !found {
|
||||
log.Error(this.idKey, " not found in JSON content.")
|
||||
return nil, ErrConfigIDKeyNotFound
|
||||
}
|
||||
var id string
|
||||
if err := json.Unmarshal(rawID, &id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawConfig := json.RawMessage(raw)
|
||||
if len(this.configKey) > 0 {
|
||||
configValue, found := obj[this.configKey]
|
||||
if !found {
|
||||
log.Error(this.configKey, " not found in JSON content.")
|
||||
return nil, ErrConfigIDKeyNotFound
|
||||
}
|
||||
rawConfig = configValue
|
||||
}
|
||||
return this.LoadWithID([]byte(rawConfig), id)
|
||||
}
|
||||
47
common/loader/loader.go
Normal file
47
common/loader/loader.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package loader
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrConfigIDKeyNotFound = errors.New("Config ID key is not found.")
|
||||
ErrConfigIDExists = errors.New("Config ID already exists.")
|
||||
ErrUnknownConfigID = errors.New("Unknown config ID.")
|
||||
)
|
||||
|
||||
type ConfigCreator func() interface{}
|
||||
|
||||
type ConfigLoader interface {
|
||||
RegisterCreator(string, ConfigCreator) error
|
||||
CreateConfig(string) (interface{}, error)
|
||||
Load([]byte) (interface{}, error)
|
||||
LoadWithID([]byte, string) (interface{}, error)
|
||||
}
|
||||
|
||||
type BaseConfigLoader struct {
|
||||
creators map[string]ConfigCreator
|
||||
}
|
||||
|
||||
func NewBaseConfigLoader() *BaseConfigLoader {
|
||||
return &BaseConfigLoader{
|
||||
creators: make(map[string]ConfigCreator),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *BaseConfigLoader) RegisterCreator(id string, creator ConfigCreator) error {
|
||||
if _, found := this.creators[id]; found {
|
||||
return ErrConfigIDExists
|
||||
}
|
||||
|
||||
this.creators[id] = creator
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *BaseConfigLoader) CreateConfig(id string) (interface{}, error) {
|
||||
creator, found := this.creators[id]
|
||||
if !found {
|
||||
return nil, ErrUnknownConfigID
|
||||
}
|
||||
return creator(), nil
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package log
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/log/internal"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
)
|
||||
|
||||
// AccessStatus is the status of an access request from clients.
|
||||
@@ -29,7 +28,7 @@ func InitAccessLogger(file string) error {
|
||||
}
|
||||
|
||||
// Access writes an access log.
|
||||
func Access(from, to serial.String, status AccessStatus, reason serial.String) {
|
||||
func Access(from, to interface{}, status AccessStatus, reason interface{}) {
|
||||
accessLoggerInstance.Log(&internal.AccessLog{
|
||||
From: from,
|
||||
To: to,
|
||||
|
||||
@@ -2,15 +2,35 @@ package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common"
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
)
|
||||
|
||||
func InterfaceToString(value interface{}) string {
|
||||
if value == nil {
|
||||
return " "
|
||||
}
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
return value
|
||||
case *string:
|
||||
return *value
|
||||
case fmt.Stringer:
|
||||
return value.String()
|
||||
case error:
|
||||
return value.Error()
|
||||
case []byte:
|
||||
return serial.BytesToHexString(value)
|
||||
default:
|
||||
return fmt.Sprint(value)
|
||||
}
|
||||
}
|
||||
|
||||
type LogEntry interface {
|
||||
common.Releasable
|
||||
serial.String
|
||||
fmt.Stringer
|
||||
}
|
||||
|
||||
type ErrorLog struct {
|
||||
@@ -26,33 +46,19 @@ func (this *ErrorLog) Release() {
|
||||
}
|
||||
|
||||
func (this *ErrorLog) String() string {
|
||||
b := alloc.NewSmallBuffer().Clear()
|
||||
defer b.Release()
|
||||
|
||||
b.AppendString(this.Prefix)
|
||||
|
||||
for _, value := range this.Values {
|
||||
switch typedVal := value.(type) {
|
||||
case string:
|
||||
b.AppendString(typedVal)
|
||||
case *string:
|
||||
b.AppendString(*typedVal)
|
||||
case serial.String:
|
||||
b.AppendString(typedVal.String())
|
||||
case error:
|
||||
b.AppendString(typedVal.Error())
|
||||
default:
|
||||
b.AppendString(fmt.Sprint(value))
|
||||
}
|
||||
values := make([]string, len(this.Values)+1)
|
||||
values[0] = this.Prefix
|
||||
for i, value := range this.Values {
|
||||
values[i+1] = InterfaceToString(value)
|
||||
}
|
||||
return b.String()
|
||||
return strings.Join(values, "")
|
||||
}
|
||||
|
||||
type AccessLog struct {
|
||||
From serial.String
|
||||
To serial.String
|
||||
From interface{}
|
||||
To interface{}
|
||||
Status string
|
||||
Reason serial.String
|
||||
Reason interface{}
|
||||
}
|
||||
|
||||
func (this *AccessLog) Release() {
|
||||
@@ -62,8 +68,5 @@ func (this *AccessLog) Release() {
|
||||
}
|
||||
|
||||
func (this *AccessLog) String() string {
|
||||
b := alloc.NewSmallBuffer().Clear()
|
||||
defer b.Release()
|
||||
|
||||
return b.AppendString(this.From.String()).AppendString(" ").AppendString(this.Status).AppendString(" ").AppendString(this.To.String()).AppendString(" ").AppendString(this.Reason.String()).String()
|
||||
return strings.Join([]string{InterfaceToString(this.From), this.Status, InterfaceToString(this.To), InterfaceToString(this.Reason)}, " ")
|
||||
}
|
||||
|
||||
@@ -4,24 +4,22 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/log/internal"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestAccessLog(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
entry := &AccessLog{
|
||||
From: serial.StringLiteral("test_from"),
|
||||
To: serial.StringLiteral("test_to"),
|
||||
From: "test_from",
|
||||
To: "test_to",
|
||||
Status: "Accepted",
|
||||
Reason: serial.StringLiteral("test_reason"),
|
||||
Reason: "test_reason",
|
||||
}
|
||||
|
||||
entryStr := entry.String()
|
||||
assert.StringLiteral(entryStr).Contains(serial.StringLiteral("test_from"))
|
||||
assert.StringLiteral(entryStr).Contains(serial.StringLiteral("test_to"))
|
||||
assert.StringLiteral(entryStr).Contains(serial.StringLiteral("test_reason"))
|
||||
assert.StringLiteral(entryStr).Contains(serial.StringLiteral("Accepted"))
|
||||
assert.String(entryStr).Contains("test_from")
|
||||
assert.String(entryStr).Contains("test_to")
|
||||
assert.String(entryStr).Contains("test_reason")
|
||||
assert.String(entryStr).Contains("Accepted")
|
||||
}
|
||||
|
||||
@@ -3,12 +3,15 @@ package internal
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/platform"
|
||||
"github.com/v2ray/v2ray-core/common/signal"
|
||||
)
|
||||
|
||||
type LogWriter interface {
|
||||
Log(LogEntry)
|
||||
Close()
|
||||
}
|
||||
|
||||
type NoOpLogWriter struct {
|
||||
@@ -18,13 +21,18 @@ func (this *NoOpLogWriter) Log(entry LogEntry) {
|
||||
entry.Release()
|
||||
}
|
||||
|
||||
func (this *NoOpLogWriter) Close() {
|
||||
}
|
||||
|
||||
type StdOutLogWriter struct {
|
||||
logger *log.Logger
|
||||
cancel *signal.CancelSignal
|
||||
}
|
||||
|
||||
func NewStdOutLogWriter() LogWriter {
|
||||
return &StdOutLogWriter{
|
||||
logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
|
||||
cancel: signal.NewCloseSignal(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +41,15 @@ func (this *StdOutLogWriter) Log(log LogEntry) {
|
||||
log.Release()
|
||||
}
|
||||
|
||||
func (this *StdOutLogWriter) Close() {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
type FileLogWriter struct {
|
||||
queue chan string
|
||||
logger *log.Logger
|
||||
file *os.File
|
||||
cancel *signal.CancelSignal
|
||||
}
|
||||
|
||||
func (this *FileLogWriter) Log(log LogEntry) {
|
||||
@@ -56,9 +69,12 @@ func (this *FileLogWriter) run() {
|
||||
}
|
||||
this.logger.Print(entry + platform.LineSeparator())
|
||||
}
|
||||
this.cancel.Done()
|
||||
}
|
||||
|
||||
func (this *FileLogWriter) Close() {
|
||||
close(this.queue)
|
||||
<-this.cancel.WaitForDone()
|
||||
this.file.Close()
|
||||
}
|
||||
|
||||
@@ -71,6 +87,7 @@ func NewFileLogWriter(path string) (*FileLogWriter, error) {
|
||||
queue: make(chan string, 16),
|
||||
logger: log.New(file, "", log.Ldate|log.Ltime),
|
||||
file: file,
|
||||
cancel: signal.NewCloseSignal(),
|
||||
}
|
||||
go logger.run()
|
||||
return logger, nil
|
||||
|
||||
@@ -90,3 +90,8 @@ func Error(v ...interface{}) {
|
||||
Values: v,
|
||||
})
|
||||
}
|
||||
|
||||
func Close() {
|
||||
streamLoggerInstance.Close()
|
||||
accessLoggerInstance.Close()
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"github.com/v2ray/v2ray-core/common/predicate"
|
||||
)
|
||||
|
||||
var (
|
||||
LocalHostIP = IPAddress([]byte{127, 0, 0, 1})
|
||||
AnyIP = IPAddress([]byte{0, 0, 0, 0})
|
||||
)
|
||||
|
||||
// Address represents a network address to be communicated with. It may be an IP address or domain
|
||||
@@ -42,7 +43,7 @@ func IPAddress(ip []byte) Address {
|
||||
var addr ipv4Address = [4]byte{ip[0], ip[1], ip[2], ip[3]}
|
||||
return &addr
|
||||
case net.IPv6len:
|
||||
if serial.BytesLiteral(ip[0:10]).All(0) && serial.BytesLiteral(ip[10:12]).All(0xff) {
|
||||
if predicate.BytesAll(ip[0:10], 0) && predicate.BytesAll(ip[10:12], 0xff) {
|
||||
return IPAddress(ip[12:16])
|
||||
}
|
||||
var addr ipv6Address = [16]byte{
|
||||
@@ -132,12 +133,22 @@ func (this *ipv6Address) Equals(another Address) bool {
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
for idx, v := range *this {
|
||||
if anotherIPv6[idx] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
return this[0] == anotherIPv6[0] &&
|
||||
this[1] == anotherIPv6[1] &&
|
||||
this[2] == anotherIPv6[2] &&
|
||||
this[3] == anotherIPv6[3] &&
|
||||
this[4] == anotherIPv6[4] &&
|
||||
this[5] == anotherIPv6[5] &&
|
||||
this[6] == anotherIPv6[6] &&
|
||||
this[7] == anotherIPv6[7] &&
|
||||
this[8] == anotherIPv6[8] &&
|
||||
this[9] == anotherIPv6[9] &&
|
||||
this[10] == anotherIPv6[10] &&
|
||||
this[11] == anotherIPv6[11] &&
|
||||
this[12] == anotherIPv6[12] &&
|
||||
this[13] == anotherIPv6[13] &&
|
||||
this[14] == anotherIPv6[14] &&
|
||||
this[15] == anotherIPv6[15]
|
||||
}
|
||||
|
||||
type domainAddress string
|
||||
|
||||
@@ -8,12 +8,11 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/net"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestIPParsing(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rawJson := "\"8.8.8.8\""
|
||||
var address AddressJson
|
||||
@@ -25,7 +24,7 @@ func TestIPParsing(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDomainParsing(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rawJson := "\"v2ray.com\""
|
||||
var address AddressJson
|
||||
@@ -33,11 +32,11 @@ func TestDomainParsing(t *testing.T) {
|
||||
assert.Error(err).IsNil()
|
||||
assert.Bool(address.Address.IsIPv4()).IsFalse()
|
||||
assert.Bool(address.Address.IsDomain()).IsTrue()
|
||||
assert.StringLiteral(address.Address.Domain()).Equals("v2ray.com")
|
||||
assert.String(address.Address.Domain()).Equals("v2ray.com")
|
||||
}
|
||||
|
||||
func TestInvalidAddressJson(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rawJson := "1234"
|
||||
var address AddressJson
|
||||
|
||||
@@ -5,26 +5,24 @@ import (
|
||||
"testing"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
v2netassert "github.com/v2ray/v2ray-core/common/net/testing/assert"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestIPv4Address(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
ip := []byte{byte(1), byte(2), byte(3), byte(4)}
|
||||
addr := v2net.IPAddress(ip)
|
||||
|
||||
v2netassert.Address(addr).IsIPv4()
|
||||
v2netassert.Address(addr).IsNotIPv6()
|
||||
v2netassert.Address(addr).IsNotDomain()
|
||||
assert.Address(addr).IsIPv4()
|
||||
assert.Address(addr).IsNotIPv6()
|
||||
assert.Address(addr).IsNotDomain()
|
||||
assert.Bytes(addr.IP()).Equals(ip)
|
||||
assert.String(addr).Equals("1.2.3.4")
|
||||
assert.Address(addr).EqualsString("1.2.3.4")
|
||||
}
|
||||
|
||||
func TestIPv6Address(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
ip := []byte{
|
||||
byte(1), byte(2), byte(3), byte(4),
|
||||
@@ -34,15 +32,15 @@ func TestIPv6Address(t *testing.T) {
|
||||
}
|
||||
addr := v2net.IPAddress(ip)
|
||||
|
||||
v2netassert.Address(addr).IsIPv6()
|
||||
v2netassert.Address(addr).IsNotIPv4()
|
||||
v2netassert.Address(addr).IsNotDomain()
|
||||
assert.Bytes(addr.IP()).Equals(ip)
|
||||
assert.String(addr).Equals("[102:304:102:304:102:304:102:304]")
|
||||
assert.Address(addr).IsIPv6()
|
||||
assert.Address(addr).IsNotIPv4()
|
||||
assert.Address(addr).IsNotDomain()
|
||||
assert.IP(addr.IP()).Equals(net.IP(ip))
|
||||
assert.Address(addr).EqualsString("[102:304:102:304:102:304:102:304]")
|
||||
}
|
||||
|
||||
func TestIPv4Asv6(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
ip := []byte{
|
||||
byte(0), byte(0), byte(0), byte(0),
|
||||
byte(0), byte(0), byte(0), byte(0),
|
||||
@@ -50,49 +48,49 @@ func TestIPv4Asv6(t *testing.T) {
|
||||
byte(1), byte(2), byte(3), byte(4),
|
||||
}
|
||||
addr := v2net.IPAddress(ip)
|
||||
assert.String(addr).Equals("1.2.3.4")
|
||||
assert.Address(addr).EqualsString("1.2.3.4")
|
||||
}
|
||||
|
||||
func TestDomainAddress(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
domain := "v2ray.com"
|
||||
addr := v2net.DomainAddress(domain)
|
||||
|
||||
v2netassert.Address(addr).IsDomain()
|
||||
v2netassert.Address(addr).IsNotIPv6()
|
||||
v2netassert.Address(addr).IsNotIPv4()
|
||||
assert.StringLiteral(addr.Domain()).Equals(domain)
|
||||
assert.String(addr).Equals("v2ray.com")
|
||||
assert.Address(addr).IsDomain()
|
||||
assert.Address(addr).IsNotIPv6()
|
||||
assert.Address(addr).IsNotIPv4()
|
||||
assert.String(addr.Domain()).Equals(domain)
|
||||
assert.Address(addr).EqualsString("v2ray.com")
|
||||
}
|
||||
|
||||
func TestNetIPv4Address(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
ip := net.IPv4(1, 2, 3, 4)
|
||||
addr := v2net.IPAddress(ip)
|
||||
v2netassert.Address(addr).IsIPv4()
|
||||
assert.String(addr).Equals("1.2.3.4")
|
||||
assert.Address(addr).IsIPv4()
|
||||
assert.Address(addr).EqualsString("1.2.3.4")
|
||||
}
|
||||
|
||||
func TestIPv4AddressEquals(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
addr := v2net.IPAddress([]byte{1, 2, 3, 4})
|
||||
assert.Bool(addr.Equals(nil)).IsFalse()
|
||||
assert.Address(addr).NotEquals(nil)
|
||||
|
||||
addr2 := v2net.IPAddress([]byte{1, 2, 3, 4})
|
||||
assert.Bool(addr.Equals(addr2)).IsTrue()
|
||||
assert.Address(addr).Equals(addr2)
|
||||
|
||||
addr3 := v2net.IPAddress([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6})
|
||||
assert.Bool(addr.Equals(addr3)).IsFalse()
|
||||
assert.Address(addr).NotEquals(addr3)
|
||||
|
||||
addr4 := v2net.IPAddress([]byte{1, 2, 3, 5})
|
||||
assert.Bool(addr.Equals(addr4)).IsFalse()
|
||||
assert.Address(addr).NotEquals(addr4)
|
||||
}
|
||||
|
||||
func TestIPv6AddressEquals(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
addr := v2net.IPAddress([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6})
|
||||
assert.Bool(addr.Equals(nil)).IsFalse()
|
||||
@@ -108,7 +106,7 @@ func TestIPv6AddressEquals(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDomainAddressEquals(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
addr := v2net.DomainAddress("v2ray.com")
|
||||
assert.Bool(addr.Equals(nil)).IsFalse()
|
||||
|
||||
@@ -4,31 +4,29 @@ import (
|
||||
"testing"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
v2netassert "github.com/v2ray/v2ray-core/common/net/testing/assert"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestTCPDestination(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
dest := v2net.TCPDestination(v2net.IPAddress([]byte{1, 2, 3, 4}), 80)
|
||||
v2netassert.Destination(dest).IsTCP()
|
||||
v2netassert.Destination(dest).IsNotUDP()
|
||||
assert.String(dest).Equals("tcp:1.2.3.4:80")
|
||||
assert.Destination(dest).IsTCP()
|
||||
assert.Destination(dest).IsNotUDP()
|
||||
assert.Destination(dest).EqualsString("tcp:1.2.3.4:80")
|
||||
}
|
||||
|
||||
func TestUDPDestination(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
dest := v2net.UDPDestination(v2net.IPAddress([]byte{0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88}), 53)
|
||||
v2netassert.Destination(dest).IsNotTCP()
|
||||
v2netassert.Destination(dest).IsUDP()
|
||||
assert.String(dest).Equals("udp:[2001:4860:4860::8888]:53")
|
||||
assert.Destination(dest).IsNotTCP()
|
||||
assert.Destination(dest).IsUDP()
|
||||
assert.Destination(dest).EqualsString("udp:[2001:4860:4860::8888]:53")
|
||||
}
|
||||
|
||||
func TestTCPDestinationEquals(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
dest := v2net.TCPDestination(v2net.IPAddress([]byte{1, 2, 3, 4}), 80)
|
||||
assert.Bool(dest.Equals(nil)).IsFalse()
|
||||
@@ -44,7 +42,7 @@ func TestTCPDestinationEquals(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUDPDestinationEquals(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
dest := v2net.UDPDestination(v2net.IPAddress([]byte{1, 2, 3, 4}), 80)
|
||||
assert.Bool(dest.Equals(nil)).IsFalse()
|
||||
|
||||
@@ -5,18 +5,19 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/net"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func parseCIDR(str string) *net.IPNet {
|
||||
_, ipNet, err := net.ParseCIDR(str)
|
||||
assert.Error(err).IsNil()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ipNet
|
||||
}
|
||||
|
||||
func TestIPNet(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
ipNet := NewIPNet()
|
||||
ipNet.Add(parseCIDR(("0.0.0.0/8")))
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"strings"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/collect"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -10,24 +12,31 @@ const (
|
||||
|
||||
// UDPNetwork represents the UDP network.
|
||||
UDPNetwork = Network("udp")
|
||||
|
||||
// KCPNetwork represents the KCP network.
|
||||
KCPNetwork = Network("kcp")
|
||||
)
|
||||
|
||||
// Network represents a communication network on internet.
|
||||
type Network serial.StringLiteral
|
||||
type Network string
|
||||
|
||||
func (this Network) AsList() *NetworkList {
|
||||
list := NetworkList([]Network{this})
|
||||
return &list
|
||||
}
|
||||
|
||||
func (this Network) String() string {
|
||||
return string(this)
|
||||
}
|
||||
|
||||
// NetworkList is a list of Networks.
|
||||
type NetworkList []Network
|
||||
|
||||
// NewNetworkList construsts a NetWorklist from the given StringListeralList.
|
||||
func NewNetworkList(networks serial.StringLiteralList) NetworkList {
|
||||
func NewNetworkList(networks collect.StringList) NetworkList {
|
||||
list := NetworkList(make([]Network, networks.Len()))
|
||||
for idx, network := range networks {
|
||||
list[idx] = Network(network.TrimSpace().ToLower())
|
||||
list[idx] = Network(strings.ToLower(strings.TrimSpace(network)))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ package net
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"github.com/v2ray/v2ray-core/common/collect"
|
||||
)
|
||||
|
||||
func (this *NetworkList) UnmarshalJSON(data []byte) error {
|
||||
var strlist serial.StringLiteralList
|
||||
var strlist collect.StringList
|
||||
if err := json.Unmarshal(data, &strlist); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -7,12 +7,11 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/net"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestArrayNetworkList(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
var list NetworkList
|
||||
err := json.Unmarshal([]byte("[\"Tcp\"]"), &list)
|
||||
@@ -22,7 +21,7 @@ func TestArrayNetworkList(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStringNetworkList(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
var list NetworkList
|
||||
err := json.Unmarshal([]byte("\"TCP, ip\""), &list)
|
||||
@@ -32,7 +31,7 @@ func TestStringNetworkList(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInvalidNetworkJson(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
var list NetworkList
|
||||
err := json.Unmarshal([]byte("0"), &list)
|
||||
|
||||
@@ -8,24 +8,24 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrorInvalidPortRage indicates an error during port range parsing.
|
||||
ErrorInvalidPortRange = errors.New("Invalid port range.")
|
||||
// ErrInvalidPortRage indicates an error during port range parsing.
|
||||
ErrInvalidPortRange = errors.New("Invalid port range.")
|
||||
)
|
||||
|
||||
// Port represents a network port in TCP and UDP protocol.
|
||||
type Port serial.Uint16Literal
|
||||
type Port uint16
|
||||
|
||||
// PortFromBytes converts a byte array to a Port, assuming bytes are in big endian order.
|
||||
// @unsafe Caller must ensure that the byte array has at least 2 elements.
|
||||
func PortFromBytes(port []byte) Port {
|
||||
return Port(serial.BytesLiteral(port).Uint16Value())
|
||||
return Port(serial.BytesToUint16(port))
|
||||
}
|
||||
|
||||
// PortFromInt converts an integer to a Port.
|
||||
// @error when the integer is not positive or larger then 65535
|
||||
func PortFromInt(v int) (Port, error) {
|
||||
if v <= 0 || v > 65535 {
|
||||
return Port(0), ErrorInvalidPortRange
|
||||
return Port(0), ErrInvalidPortRange
|
||||
}
|
||||
return Port(v), nil
|
||||
}
|
||||
@@ -35,7 +35,7 @@ func PortFromInt(v int) (Port, error) {
|
||||
func PortFromString(s string) (Port, error) {
|
||||
v, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return Port(0), ErrorInvalidPortRange
|
||||
return Port(0), ErrInvalidPortRange
|
||||
}
|
||||
return PortFromInt(v)
|
||||
}
|
||||
@@ -46,13 +46,13 @@ func (this Port) Value() uint16 {
|
||||
}
|
||||
|
||||
// Bytes returns the correspoding bytes of this Port, in big endian order.
|
||||
func (this Port) Bytes() []byte {
|
||||
return serial.Uint16Literal(this).Bytes()
|
||||
func (this Port) Bytes(b []byte) []byte {
|
||||
return serial.Uint16ToBytes(this.Value(), b)
|
||||
}
|
||||
|
||||
// String returns the string presentation of this Port.
|
||||
func (this Port) String() string {
|
||||
return serial.Uint16Literal(this).String()
|
||||
return serial.Uint16ToString(this.Value())
|
||||
}
|
||||
|
||||
// PortRange represents a range of ports.
|
||||
|
||||
@@ -26,7 +26,7 @@ func parseStringPort(data []byte) (Port, Port, error) {
|
||||
}
|
||||
pair := strings.SplitN(s, "-", 2)
|
||||
if len(pair) == 0 {
|
||||
return Port(0), Port(0), ErrorInvalidPortRange
|
||||
return Port(0), Port(0), ErrInvalidPortRange
|
||||
}
|
||||
if len(pair) == 1 {
|
||||
port, err := PortFromString(pair[0])
|
||||
@@ -59,11 +59,11 @@ func (this *PortRange) UnmarshalJSON(data []byte) error {
|
||||
this.To = to
|
||||
if this.From > this.To {
|
||||
log.Error("Invalid port range ", this.From, " -> ", this.To)
|
||||
return ErrorInvalidPortRange
|
||||
return ErrInvalidPortRange
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Error("Invalid port range: ", string(data))
|
||||
return ErrorInvalidPortRange
|
||||
return ErrInvalidPortRange
|
||||
}
|
||||
|
||||
@@ -7,12 +7,11 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/net"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestIntPort(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
var portRange PortRange
|
||||
err := json.Unmarshal([]byte("1234"), &portRange)
|
||||
@@ -23,18 +22,18 @@ func TestIntPort(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOverRangeIntPort(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
var portRange PortRange
|
||||
err := json.Unmarshal([]byte("70000"), &portRange)
|
||||
assert.Error(err).Equals(ErrorInvalidPortRange)
|
||||
assert.Error(err).Equals(ErrInvalidPortRange)
|
||||
|
||||
err = json.Unmarshal([]byte("-1"), &portRange)
|
||||
assert.Error(err).Equals(ErrorInvalidPortRange)
|
||||
assert.Error(err).Equals(ErrInvalidPortRange)
|
||||
}
|
||||
|
||||
func TestSingleStringPort(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
var portRange PortRange
|
||||
err := json.Unmarshal([]byte("\"1234\""), &portRange)
|
||||
@@ -45,7 +44,7 @@ func TestSingleStringPort(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStringPairPort(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
var portRange PortRange
|
||||
err := json.Unmarshal([]byte("\"1234-5678\""), &portRange)
|
||||
@@ -56,18 +55,18 @@ func TestStringPairPort(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOverRangeStringPort(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
var portRange PortRange
|
||||
err := json.Unmarshal([]byte("\"65536\""), &portRange)
|
||||
assert.Error(err).Equals(ErrorInvalidPortRange)
|
||||
assert.Error(err).Equals(ErrInvalidPortRange)
|
||||
|
||||
err = json.Unmarshal([]byte("\"70000-80000\""), &portRange)
|
||||
assert.Error(err).Equals(ErrorInvalidPortRange)
|
||||
assert.Error(err).Equals(ErrInvalidPortRange)
|
||||
|
||||
err = json.Unmarshal([]byte("\"1-90000\""), &portRange)
|
||||
assert.Error(err).Equals(ErrorInvalidPortRange)
|
||||
assert.Error(err).Equals(ErrInvalidPortRange)
|
||||
|
||||
err = json.Unmarshal([]byte("\"700-600\""), &portRange)
|
||||
assert.Error(err).Equals(ErrorInvalidPortRange)
|
||||
assert.Error(err).Equals(ErrInvalidPortRange)
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/net"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestPortRangeContains(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
portRange := &PortRange{
|
||||
From: Port(53),
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
package assert
|
||||
|
||||
import (
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func Address(value v2net.Address) *AddressSubject {
|
||||
return &AddressSubject{value: value}
|
||||
}
|
||||
|
||||
type AddressSubject struct {
|
||||
assert.Subject
|
||||
value v2net.Address
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) Named(name string) *AddressSubject {
|
||||
subject.Subject.Named(name)
|
||||
return subject
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) DisplayString() string {
|
||||
return subject.Subject.DisplayString(subject.value.String())
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) Equals(another v2net.Address) {
|
||||
if !subject.value.Equals(another) {
|
||||
subject.Fail(subject.DisplayString(), "equals to", another)
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) IsIPv4() {
|
||||
if !subject.value.IsIPv4() {
|
||||
subject.Fail(subject.DisplayString(), "is", serial.StringLiteral("an IPv4 address"))
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) IsNotIPv4() {
|
||||
if subject.value.IsIPv4() {
|
||||
subject.Fail(subject.DisplayString(), "is not", serial.StringLiteral("an IPv4 address"))
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) IsIPv6() {
|
||||
if !subject.value.IsIPv6() {
|
||||
subject.Fail(subject.DisplayString(), "is", serial.StringLiteral("an IPv6 address"))
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) IsNotIPv6() {
|
||||
if subject.value.IsIPv6() {
|
||||
subject.Fail(subject.DisplayString(), "is not", serial.StringLiteral("an IPv6 address"))
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) IsDomain() {
|
||||
if !subject.value.IsDomain() {
|
||||
subject.Fail(subject.DisplayString(), "is", serial.StringLiteral("a domain address"))
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) IsNotDomain() {
|
||||
if subject.value.IsDomain() {
|
||||
subject.Fail(subject.DisplayString(), "is not", serial.StringLiteral("a domain address"))
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package assert
|
||||
|
||||
import (
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func Destination(value v2net.Destination) *DestinationSubject {
|
||||
return &DestinationSubject{value: value}
|
||||
}
|
||||
|
||||
type DestinationSubject struct {
|
||||
assert.Subject
|
||||
value v2net.Destination
|
||||
}
|
||||
|
||||
func (this *DestinationSubject) Named(name string) *DestinationSubject {
|
||||
this.Subject.Named(name)
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *DestinationSubject) DisplayString() string {
|
||||
return this.Subject.DisplayString(this.value.String())
|
||||
}
|
||||
|
||||
func (this *DestinationSubject) IsTCP() {
|
||||
if !this.value.IsTCP() {
|
||||
this.Fail(this.DisplayString(), "is", serial.StringLiteral("a TCP destination"))
|
||||
}
|
||||
}
|
||||
|
||||
func (this *DestinationSubject) IsNotTCP() {
|
||||
if this.value.IsTCP() {
|
||||
this.Fail(this.DisplayString(), "is not", serial.StringLiteral("a TCP destination"))
|
||||
}
|
||||
}
|
||||
|
||||
func (this *DestinationSubject) IsUDP() {
|
||||
if !this.value.IsUDP() {
|
||||
this.Fail(this.DisplayString(), "is", serial.StringLiteral("a UDP destination"))
|
||||
}
|
||||
}
|
||||
|
||||
func (this *DestinationSubject) IsNotUDP() {
|
||||
if this.value.IsUDP() {
|
||||
this.Fail(this.DisplayString(), "is not", serial.StringLiteral("a UDP destination"))
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package assert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func IP(value net.IP) *IPSubject {
|
||||
return &IPSubject{value: value}
|
||||
}
|
||||
|
||||
type IPSubject struct {
|
||||
assert.Subject
|
||||
value net.IP
|
||||
}
|
||||
|
||||
func (subject *IPSubject) Named(name string) *IPSubject {
|
||||
subject.Subject.Named(name)
|
||||
return subject
|
||||
}
|
||||
|
||||
func (subject *IPSubject) DisplayString() string {
|
||||
return subject.Subject.DisplayString(subject.value.String())
|
||||
}
|
||||
|
||||
func (subject *IPSubject) IsNil() {
|
||||
if subject.value != nil {
|
||||
subject.Fail(subject.DisplayString(), "is", serial.StringLiteral("nil"))
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *IPSubject) Equals(ip net.IP) {
|
||||
if !bytes.Equal([]byte(subject.value), []byte(ip)) {
|
||||
subject.Fail(subject.DisplayString(), "equals to", ip)
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package assert
|
||||
|
||||
import (
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func Port(value v2net.Port) *PortSubject {
|
||||
return &PortSubject{value: value}
|
||||
}
|
||||
|
||||
type PortSubject struct {
|
||||
assert.Subject
|
||||
value v2net.Port
|
||||
}
|
||||
|
||||
func (subject *PortSubject) Named(name string) *PortSubject {
|
||||
subject.Subject.Named(name)
|
||||
return subject
|
||||
}
|
||||
|
||||
func (subject *PortSubject) DisplayString() string {
|
||||
return subject.Subject.DisplayString(subject.value.String())
|
||||
}
|
||||
|
||||
func (subject *PortSubject) Equals(expectation v2net.Port) {
|
||||
if subject.value.Value() != expectation.Value() {
|
||||
subject.Fail(subject.DisplayString(), "is equal to", expectation)
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *PortSubject) GreaterThan(expectation v2net.Port) {
|
||||
if subject.value.Value() <= expectation.Value() {
|
||||
subject.Fail(subject.DisplayString(), "is greater than", expectation)
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *PortSubject) LessThan(expectation v2net.Port) {
|
||||
if subject.value.Value() >= expectation.Value() {
|
||||
subject.Fail(subject.DisplayString(), "is less than", expectation)
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *PortSubject) IsValid() {
|
||||
if subject.value == 0 {
|
||||
subject.Fail(subject.DisplayString(), "is", serial.StringLiteral("a valid port"))
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,5 @@ import (
|
||||
)
|
||||
|
||||
func PickPort() v2net.Port {
|
||||
return v2net.Port(30000 + dice.Roll(5000))
|
||||
return v2net.Port(30000 + dice.Roll(20000))
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/net"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestTimeOutSettings(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
reader := NewTimeOutReader(8, nil)
|
||||
assert.Int(reader.GetTimeOut()).Equals(8)
|
||||
|
||||
10
common/predicate/arrays.go
Normal file
10
common/predicate/arrays.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package predicate
|
||||
|
||||
func BytesAll(array []byte, b byte) bool {
|
||||
for _, v := range array {
|
||||
if v != b {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
package protocol
|
||||
|
||||
type Account interface {
|
||||
Equals(Account) bool
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type RequestEncoder interface {
|
||||
EncodeRequestHeader(*RequestHeader, io.Writer)
|
||||
EncodeRequestBody(io.Writer) io.Writer
|
||||
}
|
||||
|
||||
type RequestDecoder interface {
|
||||
DecodeRequestHeader(io.Reader) (*RequestHeader, error)
|
||||
DecodeRequestBody(io.Reader) io.Reader
|
||||
}
|
||||
|
||||
type ResponseEncoder interface {
|
||||
EncodeResponseHeader(*ResponseHeader, io.Writer)
|
||||
EncodeResponseBody(io.Writer) io.Writer
|
||||
}
|
||||
|
||||
type ResponseDecoder interface {
|
||||
DecodeResponseHeader(io.Reader) (*ResponseHeader, error)
|
||||
DecodeResponseBody(io.Reader) io.Reader
|
||||
}
|
||||
@@ -5,6 +5,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorInvalidUser = errors.New("Invalid user.")
|
||||
ErrorInvalidVersion = errors.New("Invalid version.")
|
||||
ErrInvalidUser = errors.New("Invalid user.")
|
||||
ErrInvalidVersion = errors.New("Invalid version.")
|
||||
)
|
||||
|
||||
@@ -2,7 +2,6 @@ package protocol
|
||||
|
||||
import (
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
)
|
||||
|
||||
@@ -13,14 +12,23 @@ const (
|
||||
RequestCommandUDP = RequestCommand(0x02)
|
||||
)
|
||||
|
||||
const (
|
||||
RequestOptionChunkStream = RequestOption(0x01)
|
||||
)
|
||||
|
||||
type RequestOption byte
|
||||
|
||||
func (this RequestOption) IsChunkStream() bool {
|
||||
return (this & RequestOptionChunkStream) == RequestOptionChunkStream
|
||||
const (
|
||||
RequestOptionChunkStream = RequestOption(0x01)
|
||||
RequestOptionConnectionReuse = RequestOption(0x02)
|
||||
)
|
||||
|
||||
func (this RequestOption) Has(option RequestOption) bool {
|
||||
return (this & option) == option
|
||||
}
|
||||
|
||||
func (this *RequestOption) Set(option RequestOption) {
|
||||
*this = (*this | option)
|
||||
}
|
||||
|
||||
func (this *RequestOption) Clear(option RequestOption) {
|
||||
*this = (*this & (^option))
|
||||
}
|
||||
|
||||
type RequestHeader struct {
|
||||
@@ -39,9 +47,28 @@ func (this *RequestHeader) Destination() v2net.Destination {
|
||||
return v2net.TCPDestination(this.Address, this.Port)
|
||||
}
|
||||
|
||||
type ResponseOption byte
|
||||
|
||||
const (
|
||||
ResponseOptionConnectionReuse = ResponseOption(1)
|
||||
)
|
||||
|
||||
func (this *ResponseOption) Set(option ResponseOption) {
|
||||
*this = (*this | option)
|
||||
}
|
||||
|
||||
func (this ResponseOption) Has(option ResponseOption) bool {
|
||||
return (this | option) == option
|
||||
}
|
||||
|
||||
func (this *ResponseOption) Clear(option ResponseOption) {
|
||||
*this = (*this & (^option))
|
||||
}
|
||||
|
||||
type ResponseCommand interface{}
|
||||
|
||||
type ResponseHeader struct {
|
||||
Option ResponseOption
|
||||
Command ResponseCommand
|
||||
}
|
||||
|
||||
@@ -49,7 +76,7 @@ type CommandSwitchAccount struct {
|
||||
Host v2net.Address
|
||||
Port v2net.Port
|
||||
ID *uuid.UUID
|
||||
AlterIds serial.Uint16Literal
|
||||
AlterIds uint16
|
||||
Level UserLevel
|
||||
ValidMin byte
|
||||
}
|
||||
|
||||
34
common/protocol/headers_test.go
Normal file
34
common/protocol/headers_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package protocol_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestRequestOptionSet(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
var option RequestOption
|
||||
assert.Bool(option.Has(RequestOptionChunkStream)).IsFalse()
|
||||
|
||||
option.Set(RequestOptionChunkStream)
|
||||
assert.Bool(option.Has(RequestOptionChunkStream)).IsTrue()
|
||||
|
||||
option.Set(RequestOptionConnectionReuse)
|
||||
assert.Bool(option.Has(RequestOptionConnectionReuse)).IsTrue()
|
||||
assert.Bool(option.Has(RequestOptionChunkStream)).IsTrue()
|
||||
}
|
||||
|
||||
func TestRequestOptionClear(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
var option RequestOption
|
||||
option.Set(RequestOptionChunkStream)
|
||||
option.Set(RequestOptionConnectionReuse)
|
||||
|
||||
option.Clear(RequestOptionChunkStream)
|
||||
assert.Bool(option.Has(RequestOptionChunkStream)).IsFalse()
|
||||
assert.Bool(option.Has(RequestOptionConnectionReuse)).IsTrue()
|
||||
}
|
||||
@@ -3,16 +3,15 @@ package protocol_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/predicate"
|
||||
. "github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestCmdKey(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
id := NewID(uuid.New())
|
||||
assert.Bool(serial.BytesLiteral(id.CmdKey()).All(0)).IsFalse()
|
||||
assert.Bool(predicate.BytesAll(id.CmdKey(), 0)).IsFalse()
|
||||
}
|
||||
|
||||
90
common/protocol/server_picker.go
Normal file
90
common/protocol/server_picker.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ServerList struct {
|
||||
sync.RWMutex
|
||||
servers []*ServerSpec
|
||||
}
|
||||
|
||||
func NewServerList() *ServerList {
|
||||
return &ServerList{}
|
||||
}
|
||||
|
||||
func (this *ServerList) AddServer(server *ServerSpec) {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
this.servers = append(this.servers, server)
|
||||
}
|
||||
|
||||
func (this *ServerList) Size() uint32 {
|
||||
this.RLock()
|
||||
defer this.RUnlock()
|
||||
|
||||
return uint32(len(this.servers))
|
||||
}
|
||||
|
||||
func (this *ServerList) GetServer(idx uint32) *ServerSpec {
|
||||
this.RLock()
|
||||
defer this.RUnlock()
|
||||
|
||||
for {
|
||||
if idx >= uint32(len(this.servers)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
server := this.servers[idx]
|
||||
if !server.IsValid() {
|
||||
this.RemoveServer(idx)
|
||||
continue
|
||||
}
|
||||
|
||||
return server
|
||||
}
|
||||
}
|
||||
|
||||
// @Private
|
||||
func (this *ServerList) RemoveServer(idx uint32) {
|
||||
n := len(this.servers)
|
||||
this.servers[idx] = this.servers[n-1]
|
||||
this.servers = this.servers[:n-1]
|
||||
}
|
||||
|
||||
type ServerPicker interface {
|
||||
PickServer() *ServerSpec
|
||||
}
|
||||
|
||||
type RoundRobinServerPicker struct {
|
||||
sync.Mutex
|
||||
serverlist *ServerList
|
||||
nextIndex uint32
|
||||
}
|
||||
|
||||
func NewRoundRobinServerPicker(serverlist *ServerList) *RoundRobinServerPicker {
|
||||
return &RoundRobinServerPicker{
|
||||
serverlist: serverlist,
|
||||
nextIndex: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *RoundRobinServerPicker) PickServer() *ServerSpec {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
next := this.nextIndex
|
||||
server := this.serverlist.GetServer(next)
|
||||
if server == nil {
|
||||
next = 0
|
||||
server = this.serverlist.GetServer(0)
|
||||
}
|
||||
next++
|
||||
if next >= this.serverlist.Size() {
|
||||
next = 0
|
||||
}
|
||||
this.nextIndex = next
|
||||
|
||||
return server
|
||||
}
|
||||
54
common/protocol/server_picker_test.go
Normal file
54
common/protocol/server_picker_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package protocol_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
. "github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestServerList(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
list := NewServerList()
|
||||
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(1)), AlwaysValid()))
|
||||
assert.Uint32(list.Size()).Equals(1)
|
||||
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(2)), BeforeTime(time.Now().Add(time.Second))))
|
||||
assert.Uint32(list.Size()).Equals(2)
|
||||
|
||||
server := list.GetServer(1)
|
||||
assert.Port(server.Destination().Port()).Equals(2)
|
||||
time.Sleep(2 * time.Second)
|
||||
server = list.GetServer(1)
|
||||
assert.Pointer(server).IsNil()
|
||||
|
||||
server = list.GetServer(0)
|
||||
assert.Port(server.Destination().Port()).Equals(1)
|
||||
}
|
||||
|
||||
func TestServerPicker(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
list := NewServerList()
|
||||
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(1)), AlwaysValid()))
|
||||
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(2)), BeforeTime(time.Now().Add(time.Second))))
|
||||
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(3)), BeforeTime(time.Now().Add(time.Second))))
|
||||
|
||||
picker := NewRoundRobinServerPicker(list)
|
||||
server := picker.PickServer()
|
||||
assert.Port(server.Destination().Port()).Equals(1)
|
||||
server = picker.PickServer()
|
||||
assert.Port(server.Destination().Port()).Equals(2)
|
||||
server = picker.PickServer()
|
||||
assert.Port(server.Destination().Port()).Equals(3)
|
||||
server = picker.PickServer()
|
||||
assert.Port(server.Destination().Port()).Equals(1)
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
server = picker.PickServer()
|
||||
assert.Port(server.Destination().Port()).Equals(1)
|
||||
server = picker.PickServer()
|
||||
assert.Port(server.Destination().Port()).Equals(1)
|
||||
}
|
||||
100
common/protocol/server_spec.go
Normal file
100
common/protocol/server_spec.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
type ValidationStrategy interface {
|
||||
IsValid() bool
|
||||
Invalidate()
|
||||
}
|
||||
|
||||
type AlwaysValidStrategy struct{}
|
||||
|
||||
func AlwaysValid() ValidationStrategy {
|
||||
return AlwaysValidStrategy{}
|
||||
}
|
||||
|
||||
func (this AlwaysValidStrategy) IsValid() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (this AlwaysValidStrategy) Invalidate() {}
|
||||
|
||||
type TimeoutValidStrategy struct {
|
||||
until time.Time
|
||||
}
|
||||
|
||||
func BeforeTime(t time.Time) ValidationStrategy {
|
||||
return &TimeoutValidStrategy{
|
||||
until: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *TimeoutValidStrategy) IsValid() bool {
|
||||
return this.until.After(time.Now())
|
||||
}
|
||||
|
||||
func (this *TimeoutValidStrategy) Invalidate() {
|
||||
this.until = time.Time{}
|
||||
}
|
||||
|
||||
type ServerSpec struct {
|
||||
sync.RWMutex
|
||||
dest v2net.Destination
|
||||
users []*User
|
||||
valid ValidationStrategy
|
||||
}
|
||||
|
||||
func NewServerSpec(dest v2net.Destination, valid ValidationStrategy, users ...*User) *ServerSpec {
|
||||
return &ServerSpec{
|
||||
dest: dest,
|
||||
users: users,
|
||||
valid: valid,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ServerSpec) Destination() v2net.Destination {
|
||||
return this.dest
|
||||
}
|
||||
|
||||
func (this *ServerSpec) HasUser(user *User) bool {
|
||||
this.RLock()
|
||||
defer this.RUnlock()
|
||||
|
||||
account := user.Account
|
||||
for _, u := range this.users {
|
||||
if u.Account.Equals(account) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *ServerSpec) AddUser(user *User) {
|
||||
if this.HasUser(user) {
|
||||
return
|
||||
}
|
||||
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
this.users = append(this.users, user)
|
||||
}
|
||||
|
||||
func (this *ServerSpec) PickUser() *User {
|
||||
userCount := len(this.users)
|
||||
return this.users[dice.Roll(userCount)]
|
||||
}
|
||||
|
||||
func (this *ServerSpec) IsValid() bool {
|
||||
return this.valid.IsValid()
|
||||
}
|
||||
|
||||
func (this *ServerSpec) Invalidate() {
|
||||
this.valid.Invalidate()
|
||||
}
|
||||
62
common/protocol/server_spec_test.go
Normal file
62
common/protocol/server_spec_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package protocol_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
. "github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
type TestAccount struct {
|
||||
id int
|
||||
}
|
||||
|
||||
func (this *TestAccount) Equals(account Account) bool {
|
||||
return account.(*TestAccount).id == this.id
|
||||
}
|
||||
|
||||
func TestServerSpecUser(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
account := &TestAccount{
|
||||
id: 0,
|
||||
}
|
||||
user := NewUser(UserLevel(0), "")
|
||||
user.Account = account
|
||||
rec := NewServerSpec(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80), AlwaysValid(), user)
|
||||
assert.Bool(rec.HasUser(user)).IsTrue()
|
||||
|
||||
account2 := &TestAccount{
|
||||
id: 1,
|
||||
}
|
||||
user2 := NewUser(UserLevel(0), "")
|
||||
user2.Account = account2
|
||||
assert.Bool(rec.HasUser(user2)).IsFalse()
|
||||
|
||||
rec.AddUser(user2)
|
||||
assert.Bool(rec.HasUser(user2)).IsTrue()
|
||||
}
|
||||
|
||||
func TestAlwaysValidStrategy(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
strategy := AlwaysValid()
|
||||
assert.Bool(strategy.IsValid()).IsTrue()
|
||||
strategy.Invalidate()
|
||||
assert.Bool(strategy.IsValid()).IsTrue()
|
||||
}
|
||||
|
||||
func TestTimeoutValidStrategy(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
strategy := BeforeTime(time.Now().Add(2 * time.Second))
|
||||
assert.Bool(strategy.IsValid()).IsTrue()
|
||||
time.Sleep(3 * time.Second)
|
||||
assert.Bool(strategy.IsValid()).IsFalse()
|
||||
|
||||
strategy = BeforeTime(time.Now().Add(2 * time.Second))
|
||||
strategy.Invalidate()
|
||||
assert.Bool(strategy.IsValid()).IsFalse()
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
)
|
||||
|
||||
type Timestamp int64
|
||||
|
||||
func (this Timestamp) Bytes() []byte {
|
||||
return serial.Int64Literal(this).Bytes()
|
||||
func (this Timestamp) Bytes(b []byte) []byte {
|
||||
return serial.Int64ToBytes(int64(this), b)
|
||||
}
|
||||
|
||||
type TimestampGenerator func() Timestamp
|
||||
@@ -21,7 +21,7 @@ func NowTime() Timestamp {
|
||||
|
||||
func NewTimestampGenerator(base Timestamp, delta int) TimestampGenerator {
|
||||
return func() Timestamp {
|
||||
rangeInDelta := rand.Intn(delta*2) - delta
|
||||
rangeInDelta := dice.Roll(delta*2) - delta
|
||||
return base + Timestamp(rangeInDelta)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,11 @@ import (
|
||||
"time"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/protocol"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestGenerateRandomInt64InRange(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
base := time.Now().Unix()
|
||||
delta := 100
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
)
|
||||
|
||||
type UserLevel byte
|
||||
|
||||
const (
|
||||
@@ -12,28 +8,18 @@ const (
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID *ID
|
||||
AlterIDs []*ID
|
||||
Level UserLevel
|
||||
Email string
|
||||
Account Account
|
||||
Level UserLevel
|
||||
Email string
|
||||
}
|
||||
|
||||
func NewUser(primary *ID, secondary []*ID, level UserLevel, email string) *User {
|
||||
func NewUser(level UserLevel, email string) *User {
|
||||
return &User{
|
||||
ID: primary,
|
||||
AlterIDs: secondary,
|
||||
Level: level,
|
||||
Email: email,
|
||||
Level: level,
|
||||
Email: email,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *User) AnyValidID() *ID {
|
||||
if len(this.AlterIDs) == 0 {
|
||||
return this.ID
|
||||
}
|
||||
return this.AlterIDs[dice.Roll(len(this.AlterIDs))]
|
||||
}
|
||||
|
||||
type UserSettings struct {
|
||||
PayloadReadTimeout int
|
||||
}
|
||||
|
||||
@@ -2,30 +2,20 @@
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
)
|
||||
import "encoding/json"
|
||||
|
||||
func (u *User) UnmarshalJSON(data []byte) error {
|
||||
type rawUser struct {
|
||||
IdString string `json:"id"`
|
||||
EmailString string `json:"email"`
|
||||
LevelByte byte `json:"level"`
|
||||
AlterIdCount uint16 `json:"alterId"`
|
||||
EmailString string `json:"email"`
|
||||
LevelByte byte `json:"level"`
|
||||
}
|
||||
var rawUserValue rawUser
|
||||
if err := json.Unmarshal(data, &rawUserValue); err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := uuid.ParseString(rawUserValue.IdString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
primaryID := NewID(id)
|
||||
alterIDs := NewAlterIDs(primaryID, rawUserValue.AlterIdCount)
|
||||
*u = *NewUser(primaryID, alterIDs, UserLevel(rawUserValue.LevelByte), rawUserValue.EmailString)
|
||||
|
||||
u.Email = rawUserValue.EmailString
|
||||
u.Level = UserLevel(rawUserValue.LevelByte)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,12 +7,11 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/protocol"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestUserParsing(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
user := new(User)
|
||||
err := json.Unmarshal([]byte(`{
|
||||
@@ -22,22 +21,14 @@ func TestUserParsing(t *testing.T) {
|
||||
"alterId": 100
|
||||
}`), user)
|
||||
assert.Error(err).IsNil()
|
||||
assert.String(user.ID).Equals("96edb838-6d68-42ef-a933-25f7ac3a9d09")
|
||||
assert.Byte(byte(user.Level)).Equals(1)
|
||||
assert.String(user.Email).Equals("love@v2ray.com")
|
||||
}
|
||||
|
||||
func TestInvalidUserJson(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
user := new(User)
|
||||
err := json.Unmarshal([]byte(`{"id": 1234}`), user)
|
||||
assert.Error(err).IsNotNil()
|
||||
}
|
||||
|
||||
func TestInvalidIdJson(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
|
||||
user := new(User)
|
||||
err := json.Unmarshal([]byte(`{"id": "1234"}`), user)
|
||||
err := json.Unmarshal([]byte(`{"email": 1234}`), user)
|
||||
assert.Error(err).IsNotNil()
|
||||
}
|
||||
|
||||
@@ -1,145 +1,12 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common"
|
||||
"github.com/v2ray/v2ray-core/common/signal"
|
||||
)
|
||||
|
||||
const (
|
||||
updateIntervalSec = 10
|
||||
cacheDurationSec = 120
|
||||
)
|
||||
|
||||
type idEntry struct {
|
||||
id *ID
|
||||
userIdx int
|
||||
lastSec Timestamp
|
||||
lastSecRemoval Timestamp
|
||||
}
|
||||
|
||||
type UserValidator interface {
|
||||
common.Releasable
|
||||
|
||||
Add(user *User) error
|
||||
Get(timeHash []byte) (*User, Timestamp, bool)
|
||||
}
|
||||
|
||||
type TimedUserValidator struct {
|
||||
validUsers []*User
|
||||
userHash map[[16]byte]*indexTimePair
|
||||
ids []*idEntry
|
||||
access sync.RWMutex
|
||||
hasher IDHash
|
||||
cancel *signal.CancelSignal
|
||||
}
|
||||
|
||||
type indexTimePair struct {
|
||||
index int
|
||||
timeSec Timestamp
|
||||
}
|
||||
|
||||
func NewTimedUserValidator(hasher IDHash) UserValidator {
|
||||
tus := &TimedUserValidator{
|
||||
validUsers: make([]*User, 0, 16),
|
||||
userHash: make(map[[16]byte]*indexTimePair, 512),
|
||||
access: sync.RWMutex{},
|
||||
ids: make([]*idEntry, 0, 512),
|
||||
hasher: hasher,
|
||||
cancel: signal.NewCloseSignal(),
|
||||
}
|
||||
go tus.updateUserHash(time.Tick(updateIntervalSec*time.Second), tus.cancel)
|
||||
return tus
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) Release() {
|
||||
this.cancel.Cancel()
|
||||
<-this.cancel.WaitForDone()
|
||||
|
||||
this.validUsers = nil
|
||||
this.userHash = nil
|
||||
this.ids = nil
|
||||
this.hasher = nil
|
||||
this.cancel = nil
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) generateNewHashes(nowSec Timestamp, idx int, entry *idEntry) {
|
||||
var hashValue [16]byte
|
||||
var hashValueRemoval [16]byte
|
||||
idHash := this.hasher(entry.id.Bytes())
|
||||
for entry.lastSec <= nowSec {
|
||||
idHash.Write(entry.lastSec.Bytes())
|
||||
idHash.Sum(hashValue[:0])
|
||||
idHash.Reset()
|
||||
|
||||
idHash.Write(entry.lastSecRemoval.Bytes())
|
||||
idHash.Sum(hashValueRemoval[:0])
|
||||
idHash.Reset()
|
||||
|
||||
this.access.Lock()
|
||||
this.userHash[hashValue] = &indexTimePair{idx, entry.lastSec}
|
||||
delete(this.userHash, hashValueRemoval)
|
||||
this.access.Unlock()
|
||||
|
||||
entry.lastSec++
|
||||
entry.lastSecRemoval++
|
||||
}
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) updateUserHash(tick <-chan time.Time, cancel *signal.CancelSignal) {
|
||||
L:
|
||||
for {
|
||||
select {
|
||||
case now := <-tick:
|
||||
nowSec := Timestamp(now.Unix() + cacheDurationSec)
|
||||
for _, entry := range this.ids {
|
||||
this.generateNewHashes(nowSec, entry.userIdx, entry)
|
||||
}
|
||||
case <-cancel.WaitForCancel():
|
||||
break L
|
||||
}
|
||||
}
|
||||
cancel.Done()
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) Add(user *User) error {
|
||||
idx := len(this.validUsers)
|
||||
this.validUsers = append(this.validUsers, user)
|
||||
|
||||
nowSec := time.Now().Unix()
|
||||
|
||||
entry := &idEntry{
|
||||
id: user.ID,
|
||||
userIdx: idx,
|
||||
lastSec: Timestamp(nowSec - cacheDurationSec),
|
||||
lastSecRemoval: Timestamp(nowSec - cacheDurationSec*3),
|
||||
}
|
||||
this.generateNewHashes(Timestamp(nowSec+cacheDurationSec), idx, entry)
|
||||
this.ids = append(this.ids, entry)
|
||||
for _, alterid := range user.AlterIDs {
|
||||
entry := &idEntry{
|
||||
id: alterid,
|
||||
userIdx: idx,
|
||||
lastSec: Timestamp(nowSec - cacheDurationSec),
|
||||
lastSecRemoval: Timestamp(nowSec - cacheDurationSec*3),
|
||||
}
|
||||
this.generateNewHashes(Timestamp(nowSec+cacheDurationSec), idx, entry)
|
||||
this.ids = append(this.ids, entry)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) Get(userHash []byte) (*User, Timestamp, bool) {
|
||||
defer this.access.RUnlock()
|
||||
this.access.RLock()
|
||||
var fixedSizeHash [16]byte
|
||||
copy(fixedSizeHash[:], userHash)
|
||||
pair, found := this.userHash[fixedSizeHash]
|
||||
if found {
|
||||
return this.validUsers[pair.index], pair.timeSec, true
|
||||
}
|
||||
return nil, 0, false
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorRetryFailed = errors.New("All retry attempts failed.")
|
||||
ErrRetryFailed = errors.New("All retry attempts failed.")
|
||||
)
|
||||
|
||||
// Strategy is a way to retry on a specific function.
|
||||
@@ -29,7 +29,7 @@ func (r *retryer) On(method func() error) error {
|
||||
}
|
||||
delay := r.NextDelay(attempt)
|
||||
if delay < 0 {
|
||||
return ErrorRetryFailed
|
||||
return ErrRetryFailed
|
||||
}
|
||||
<-time.After(time.Duration(delay) * time.Millisecond)
|
||||
attempt++
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"time"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/retry"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
@@ -15,7 +14,7 @@ var (
|
||||
)
|
||||
|
||||
func TestNoRetry(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
startTime := time.Now().Unix()
|
||||
err := Timed(10, 100000).On(func() error {
|
||||
@@ -28,7 +27,7 @@ func TestNoRetry(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRetryOnce(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
startTime := time.Now()
|
||||
called := 0
|
||||
@@ -46,7 +45,7 @@ func TestRetryOnce(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRetryMultiple(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
startTime := time.Now()
|
||||
called := 0
|
||||
@@ -64,7 +63,7 @@ func TestRetryMultiple(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRetryExhausted(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
startTime := time.Now()
|
||||
called := 0
|
||||
@@ -77,6 +76,6 @@ func TestRetryExhausted(t *testing.T) {
|
||||
})
|
||||
duration := time.Since(startTime)
|
||||
|
||||
assert.Error(err).Equals(ErrorRetryFailed)
|
||||
assert.Error(err).Equals(ErrRetryFailed)
|
||||
assert.Int64(int64(duration / time.Millisecond)).AtLeast(1900)
|
||||
}
|
||||
|
||||
@@ -1,72 +1,40 @@
|
||||
package serial
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Bytes interface {
|
||||
Bytes() []byte
|
||||
func ByteToHexString(value byte) string {
|
||||
return hex.EncodeToString([]byte{value})
|
||||
}
|
||||
|
||||
type BytesLiteral []byte
|
||||
|
||||
func (this BytesLiteral) Value() []byte {
|
||||
return []byte(this)
|
||||
func BytesToUint16(value []byte) uint16 {
|
||||
return uint16(value[0])<<8 | uint16(value[1])
|
||||
}
|
||||
|
||||
func (this BytesLiteral) Equals(another BytesLiteral) bool {
|
||||
return bytes.Equal(this.Value(), another.Value())
|
||||
}
|
||||
|
||||
func (this BytesLiteral) Uint8Value() uint8 {
|
||||
return this.Value()[0]
|
||||
}
|
||||
|
||||
func (this BytesLiteral) Uint16() Uint16Literal {
|
||||
return Uint16Literal(this.Uint16Value())
|
||||
}
|
||||
|
||||
func (this BytesLiteral) Uint16Value() uint16 {
|
||||
value := this.Value()
|
||||
return uint16(value[0])<<8 + uint16(value[1])
|
||||
}
|
||||
|
||||
func (this BytesLiteral) IntValue() int {
|
||||
value := this.Value()
|
||||
return int(value[0])<<24 + int(value[1])<<16 + int(value[2])<<8 + int(value[3])
|
||||
}
|
||||
|
||||
func (this BytesLiteral) Uint32Value() uint32 {
|
||||
value := this.Value()
|
||||
return uint32(value[0])<<24 +
|
||||
uint32(value[1])<<16 +
|
||||
uint32(value[2])<<8 +
|
||||
func BytesToUint32(value []byte) uint32 {
|
||||
return uint32(value[0])<<24 |
|
||||
uint32(value[1])<<16 |
|
||||
uint32(value[2])<<8 |
|
||||
uint32(value[3])
|
||||
}
|
||||
|
||||
func (this BytesLiteral) Int64Value() int64 {
|
||||
value := this.Value()
|
||||
return int64(value[0])<<56 +
|
||||
int64(value[1])<<48 +
|
||||
int64(value[2])<<40 +
|
||||
int64(value[3])<<32 +
|
||||
int64(value[4])<<24 +
|
||||
int64(value[5])<<16 +
|
||||
int64(value[6])<<8 +
|
||||
func BytesToInt64(value []byte) int64 {
|
||||
return int64(value[0])<<56 |
|
||||
int64(value[1])<<48 |
|
||||
int64(value[2])<<40 |
|
||||
int64(value[3])<<32 |
|
||||
int64(value[4])<<24 |
|
||||
int64(value[5])<<16 |
|
||||
int64(value[6])<<8 |
|
||||
int64(value[7])
|
||||
}
|
||||
|
||||
// String returns a string presentation of this ByteLiteral
|
||||
func (this BytesLiteral) String() string {
|
||||
return string(this.Value())
|
||||
}
|
||||
|
||||
// All returns true if all bytes in the ByteLiteral are the same as given value.
|
||||
func (this BytesLiteral) All(v byte) bool {
|
||||
for _, b := range this {
|
||||
if b != v {
|
||||
return false
|
||||
}
|
||||
func BytesToHexString(value []byte) string {
|
||||
strs := make([]string, len(value))
|
||||
for i, b := range value {
|
||||
strs[i] = hex.EncodeToString([]byte{b})
|
||||
}
|
||||
return true
|
||||
return "[" + strings.Join(strs, ",") + "]"
|
||||
}
|
||||
|
||||
9
common/serial/interface.go
Normal file
9
common/serial/interface.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package serial
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func PointerToString(value interface{}) string {
|
||||
return fmt.Sprint(value)
|
||||
}
|
||||
@@ -4,68 +4,42 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Uint16 interface {
|
||||
Value() uint16
|
||||
func Uint16ToBytes(value uint16, b []byte) []byte {
|
||||
return append(b, byte(value>>8), byte(value))
|
||||
}
|
||||
|
||||
type Uint16Literal uint16
|
||||
|
||||
func (this Uint16Literal) String() string {
|
||||
return strconv.Itoa(int(this))
|
||||
func Uint16ToString(value uint16) string {
|
||||
return strconv.Itoa(int(value))
|
||||
}
|
||||
|
||||
func (this Uint16Literal) Value() uint16 {
|
||||
return uint16(this)
|
||||
func Uint32ToBytes(value uint32, b []byte) []byte {
|
||||
return append(b, byte(value>>24), byte(value>>16), byte(value>>8), byte(value))
|
||||
}
|
||||
|
||||
func (this Uint16Literal) Bytes() []byte {
|
||||
return []byte{byte(this >> 8), byte(this)}
|
||||
func Uint32ToString(value uint32) string {
|
||||
return strconv.FormatUint(uint64(value), 10)
|
||||
}
|
||||
|
||||
type Int interface {
|
||||
Value() int
|
||||
func IntToBytes(value int, b []byte) []byte {
|
||||
return append(b, byte(value>>24), byte(value>>16), byte(value>>8), byte(value))
|
||||
}
|
||||
|
||||
type IntLiteral int
|
||||
|
||||
func (this IntLiteral) String() string {
|
||||
return strconv.Itoa(int(this))
|
||||
func IntToString(value int) string {
|
||||
return Int64ToString(int64(value))
|
||||
}
|
||||
|
||||
func (this IntLiteral) Value() int {
|
||||
return int(this)
|
||||
func Int64ToBytes(value int64, b []byte) []byte {
|
||||
return append(b,
|
||||
byte(value>>56),
|
||||
byte(value>>48),
|
||||
byte(value>>40),
|
||||
byte(value>>32),
|
||||
byte(value>>24),
|
||||
byte(value>>16),
|
||||
byte(value>>8),
|
||||
byte(value))
|
||||
}
|
||||
|
||||
func (this IntLiteral) Bytes() []byte {
|
||||
value := this.Value()
|
||||
return []byte{
|
||||
byte(value >> 24),
|
||||
byte(value >> 16),
|
||||
byte(value >> 8),
|
||||
byte(value),
|
||||
}
|
||||
}
|
||||
|
||||
type Int64Literal int64
|
||||
|
||||
func (this Int64Literal) String() string {
|
||||
return strconv.FormatInt(this.Value(), 10)
|
||||
}
|
||||
|
||||
func (this Int64Literal) Value() int64 {
|
||||
return int64(this)
|
||||
}
|
||||
|
||||
func (this Int64Literal) Bytes() []byte {
|
||||
value := this.Value()
|
||||
return []byte{
|
||||
byte(value >> 56),
|
||||
byte(value >> 48),
|
||||
byte(value >> 40),
|
||||
byte(value >> 32),
|
||||
byte(value >> 24),
|
||||
byte(value >> 16),
|
||||
byte(value >> 8),
|
||||
byte(value),
|
||||
}
|
||||
func Int64ToString(value int64) string {
|
||||
return strconv.FormatInt(value, 10)
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package serial
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// An interface for any objects that has string presentation.
|
||||
type String interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
type StringLiteral string
|
||||
|
||||
func NewStringLiteral(str String) StringLiteral {
|
||||
return StringLiteral(str.String())
|
||||
}
|
||||
|
||||
func (this StringLiteral) Contains(str String) bool {
|
||||
return strings.Contains(this.String(), str.String())
|
||||
}
|
||||
|
||||
func (this StringLiteral) String() string {
|
||||
return string(this)
|
||||
}
|
||||
|
||||
func (this StringLiteral) ToLower() StringLiteral {
|
||||
return StringLiteral(strings.ToLower(string(this)))
|
||||
}
|
||||
|
||||
func (this StringLiteral) ToUpper() StringLiteral {
|
||||
return StringLiteral(strings.ToUpper(string(this)))
|
||||
}
|
||||
|
||||
func (this StringLiteral) TrimSpace() StringLiteral {
|
||||
return StringLiteral(strings.TrimSpace(string(this)))
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// +build json
|
||||
|
||||
package serial
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func (this *StringLiteral) UnmarshalJSON(data []byte) error {
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err != nil {
|
||||
return err
|
||||
}
|
||||
*this = StringLiteral(str)
|
||||
return nil
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// +build json
|
||||
|
||||
package serial_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/serial"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestInvalidStringLiteralJson(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
|
||||
var s StringLiteral
|
||||
err := json.Unmarshal([]byte("1"), &s)
|
||||
assert.Error(err).IsNotNil()
|
||||
}
|
||||
|
||||
func TestStringLiteralParsing(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
|
||||
var s StringLiteral
|
||||
err := json.Unmarshal([]byte("\"1\""), &s)
|
||||
assert.Error(err).IsNil()
|
||||
assert.String(s).Equals("1")
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package serial
|
||||
|
||||
type StringLiteralList []StringLiteral
|
||||
|
||||
func NewStringLiteralList(raw []string) *StringLiteralList {
|
||||
list := StringLiteralList(make([]StringLiteral, len(raw)))
|
||||
for idx, str := range raw {
|
||||
list[idx] = StringLiteral(str)
|
||||
}
|
||||
return &list
|
||||
}
|
||||
|
||||
func (this *StringLiteralList) Len() int {
|
||||
return len(*this)
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package serial_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/serial"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
type TestString struct {
|
||||
value string
|
||||
}
|
||||
|
||||
func (this *TestString) String() string {
|
||||
return this.value
|
||||
}
|
||||
|
||||
func TestNewStringSerial(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
|
||||
testString := &TestString{value: "abcd"}
|
||||
assert.String(NewStringLiteral(testString)).Equals("abcd")
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user