You've already forked v2ray-core
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -86,7 +90,7 @@ func (this *UDPNameServer) AssignUnusedID(response chan<- *ARecord) uint16 {
|
||||
}
|
||||
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,12 +143,10 @@ 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{
|
||||
@@ -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,6 +32,7 @@ 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) {
|
||||
@@ -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,18 @@ 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"
|
||||
)
|
||||
|
||||
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}))
|
||||
space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, outboundHandlerManager)
|
||||
space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space))
|
||||
|
||||
@@ -37,5 +36,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}))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -303,6 +314,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 +331,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 +358,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 +366,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 +406,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 +417,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 +472,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,12 +2,10 @@ 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"
|
||||
)
|
||||
@@ -17,39 +15,16 @@ var (
|
||||
ErrorNoRuleApplicable = 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) {
|
||||
@@ -108,14 +83,14 @@ func (this *Router) takeDetourWithoutCache(dest v2net.Destination) (string, erro
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -159,3 +159,15 @@ 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()
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ func (p *BufferPool) Free(buffer *Buffer) {
|
||||
}
|
||||
|
||||
const (
|
||||
SmallBufferSize = 1024 - defaultOffset
|
||||
BufferSize = 8*1024 - defaultOffset
|
||||
LargeBufferSize = 64*1024 - defaultOffset
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -1,27 +1,29 @@
|
||||
package http
|
||||
package io
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
v2io "github.com/v2ray/v2ray-core/common/io"
|
||||
)
|
||||
|
||||
type ChanReader struct {
|
||||
stream v2io.Reader
|
||||
sync.Mutex
|
||||
stream Reader
|
||||
current *alloc.Buffer
|
||||
eof bool
|
||||
}
|
||||
|
||||
func NewChanReader(stream v2io.Reader) *ChanReader {
|
||||
func NewChanReader(stream Reader) *ChanReader {
|
||||
this := &ChanReader{
|
||||
stream: stream,
|
||||
}
|
||||
this.fill()
|
||||
this.Fill()
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *ChanReader) fill() {
|
||||
// @Private
|
||||
func (this *ChanReader) Fill() {
|
||||
b, err := this.stream.Read()
|
||||
this.current = b
|
||||
if err != nil {
|
||||
@@ -31,8 +33,14 @@ func (this *ChanReader) fill() {
|
||||
}
|
||||
|
||||
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()
|
||||
this.Fill()
|
||||
if this.eof {
|
||||
return 0, io.EOF
|
||||
}
|
||||
@@ -46,3 +54,13 @@ func (this *ChanReader) Read(b []byte) (int, error) {
|
||||
}
|
||||
return nBytes, nil
|
||||
}
|
||||
|
||||
func (this *ChanReader) Release() {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
this.eof = true
|
||||
this.current.Release()
|
||||
this.current = nil
|
||||
this.stream = 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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -8,9 +8,29 @@ import (
|
||||
"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 {
|
||||
@@ -32,27 +52,16 @@ func (this *ErrorLog) String() string {
|
||||
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))
|
||||
}
|
||||
b.AppendString(InterfaceToString(value))
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
type AccessLog struct {
|
||||
From serial.String
|
||||
To serial.String
|
||||
From interface{}
|
||||
To interface{}
|
||||
Status string
|
||||
Reason serial.String
|
||||
Reason interface{}
|
||||
}
|
||||
|
||||
func (this *AccessLog) Release() {
|
||||
@@ -65,5 +74,9 @@ 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()
|
||||
b.AppendString(InterfaceToString(this.From)).AppendString(" ")
|
||||
b.AppendString(this.Status).AppendString(" ")
|
||||
b.AppendString(InterfaceToString(this.To)).AppendString(" ")
|
||||
b.AppendString(InterfaceToString(this.Reason))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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 (
|
||||
@@ -13,21 +15,25 @@ const (
|
||||
)
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -13,12 +13,12 @@ var (
|
||||
)
|
||||
|
||||
// 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.
|
||||
@@ -47,12 +47,12 @@ 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()
|
||||
return serial.Uint16ToBytes(this.Value())
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -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,7 +22,7 @@ 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)
|
||||
@@ -34,7 +33,7 @@ func TestOverRangeIntPort(t *testing.T) {
|
||||
}
|
||||
|
||||
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,7 +55,7 @@ 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)
|
||||
|
||||
@@ -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,20 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
)
|
||||
|
||||
type Account interface {
|
||||
}
|
||||
|
||||
type VMessAccount struct {
|
||||
ID *ID
|
||||
AlterIDs []*ID
|
||||
}
|
||||
|
||||
func (this *VMessAccount) AnyValidID() *ID {
|
||||
if len(this.AlterIDs) == 0 {
|
||||
return this.ID
|
||||
}
|
||||
return this.AlterIDs[dice.Roll(len(this.AlterIDs))]
|
||||
}
|
||||
|
||||
36
common/protocol/account_json.go
Normal file
36
common/protocol/account_json.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// +build json
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
)
|
||||
|
||||
type AccountJson struct {
|
||||
ID string `json:"id"`
|
||||
AlterIds uint16 `json:"alterId"`
|
||||
|
||||
Username string `json:"user"`
|
||||
Password string `json:"pass"`
|
||||
}
|
||||
|
||||
func (this *AccountJson) GetAccount() (Account, error) {
|
||||
if len(this.ID) > 0 {
|
||||
id, err := uuid.ParseString(this.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
primaryID := NewID(id)
|
||||
alterIDs := NewAlterIDs(primaryID, this.AlterIds)
|
||||
|
||||
return &VMessAccount{
|
||||
ID: primaryID,
|
||||
AlterIDs: alterIDs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("Protocol: Malformed account.")
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -14,13 +13,22 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
RequestOptionChunkStream = RequestOption(0x01)
|
||||
RequestOptionChunkStream = RequestOption(0x01)
|
||||
RequestOptionConnectionReuse = RequestOption(0x02)
|
||||
)
|
||||
|
||||
type RequestOption byte
|
||||
|
||||
func (this RequestOption) IsChunkStream() bool {
|
||||
return (this & RequestOptionChunkStream) == RequestOptionChunkStream
|
||||
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
|
||||
|
||||
var (
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func NewClientSession(idHash protocol.IDHash) *ClientSession {
|
||||
|
||||
func (this *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) {
|
||||
timestamp := protocol.NewTimestampGenerator(protocol.NowTime(), 30)()
|
||||
idHash := this.idHash(header.User.AnyValidID().Bytes())
|
||||
idHash := this.idHash(header.User.Account.(*protocol.VMessAccount).AnyValidID().Bytes())
|
||||
idHash.Write(timestamp.Bytes())
|
||||
writer.Write(idHash.Sum(nil))
|
||||
|
||||
@@ -87,7 +87,8 @@ func (this *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, w
|
||||
timestampHash := md5.New()
|
||||
timestampHash.Write(hashTimestamp(timestamp))
|
||||
iv := timestampHash.Sum(nil)
|
||||
aesStream := crypto.NewAesEncryptionStream(header.User.ID.CmdKey(), iv)
|
||||
account := header.User.Account.(*protocol.VMessAccount)
|
||||
aesStream := crypto.NewAesEncryptionStream(account.ID.CmdKey(), iv)
|
||||
aesStream.XORKeyStream(buffer.Value, buffer.Value)
|
||||
writer.Write(buffer.Value)
|
||||
|
||||
@@ -117,7 +118,9 @@ func (this *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Res
|
||||
return nil, transport.ErrorCorruptedPacket
|
||||
}
|
||||
|
||||
header := new(protocol.ResponseHeader)
|
||||
header := &protocol.ResponseHeader{
|
||||
Option: protocol.ResponseOption(buffer.Value[1]),
|
||||
}
|
||||
|
||||
if buffer.Value[2] != 0 {
|
||||
cmdId := buffer.Value[2]
|
||||
|
||||
@@ -55,7 +55,7 @@ func UnmarshalCommand(cmdId byte, data []byte) (protocol.ResponseCommand, error)
|
||||
return nil, transport.ErrorCorruptedPacket
|
||||
}
|
||||
expectedAuth := Authenticate(data[4:])
|
||||
actualAuth := serial.BytesLiteral(data[:4]).Uint32Value()
|
||||
actualAuth := serial.BytesToUint32(data[:4])
|
||||
if expectedAuth != actualAuth {
|
||||
return nil, transport.ErrorCorruptedPacket
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func (this *CommandSwitchAccountFactory) Marshal(command interface{}, writer io.
|
||||
idBytes := cmd.ID.Bytes()
|
||||
writer.Write(idBytes)
|
||||
|
||||
writer.Write(cmd.AlterIds.Bytes())
|
||||
writer.Write(serial.Uint16ToBytes(cmd.AlterIds))
|
||||
writer.Write([]byte{byte(cmd.Level)})
|
||||
|
||||
writer.Write([]byte{cmd.ValidMin})
|
||||
@@ -132,7 +132,7 @@ func (this *CommandSwitchAccountFactory) Unmarshal(data []byte) (interface{}, er
|
||||
if len(data) < alterIdStart+2 {
|
||||
return nil, transport.ErrorCorruptedPacket
|
||||
}
|
||||
cmd.AlterIds = serial.BytesLiteral(data[alterIdStart : alterIdStart+2]).Uint16()
|
||||
cmd.AlterIds = serial.BytesToUint16(data[alterIdStart : alterIdStart+2])
|
||||
levelStart := alterIdStart + 2
|
||||
if len(data) < levelStart+1 {
|
||||
return nil, transport.ErrorCorruptedPacket
|
||||
|
||||
@@ -4,16 +4,14 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
netassert "github.com/v2ray/v2ray-core/common/net/testing/assert"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
. "github.com/v2ray/v2ray-core/common/protocol/raw"
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestSwitchAccount(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
sa := &protocol.CommandSwitchAccount{
|
||||
Port: 1234,
|
||||
@@ -34,9 +32,9 @@ func TestSwitchAccount(t *testing.T) {
|
||||
assert.Bool(ok).IsTrue()
|
||||
assert.Pointer(sa.Host).IsNil()
|
||||
assert.Pointer(sa2.Host).IsNil()
|
||||
netassert.Port(sa.Port).Equals(sa2.Port)
|
||||
assert.String(sa.ID).Equals(sa2.ID.String())
|
||||
assert.Uint16(sa.AlterIds.Value()).Equals(sa2.AlterIds.Value())
|
||||
assert.Port(sa.Port).Equals(sa2.Port)
|
||||
assert.String(sa.ID.String()).Equals(sa2.ID.String())
|
||||
assert.Uint16(sa.AlterIds).Equals(sa2.AlterIds)
|
||||
assert.Byte(byte(sa.Level)).Equals(byte(sa2.Level))
|
||||
assert.Byte(sa.ValidMin).Equals(sa2.ValidMin)
|
||||
}
|
||||
|
||||
@@ -5,20 +5,20 @@ import (
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
netassert "github.com/v2ray/v2ray-core/common/net/testing/assert"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
. "github.com/v2ray/v2ray-core/common/protocol/raw"
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestRequestSerialization(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
user := protocol.NewUser(
|
||||
protocol.NewID(uuid.New()),
|
||||
nil,
|
||||
&protocol.VMessAccount{
|
||||
ID: protocol.NewID(uuid.New()),
|
||||
AlterIDs: nil,
|
||||
},
|
||||
protocol.UserLevelUntrusted,
|
||||
"test@v2ray.com")
|
||||
|
||||
@@ -45,6 +45,6 @@ func TestRequestSerialization(t *testing.T) {
|
||||
assert.Byte(expectedRequest.Version).Equals(actualRequest.Version)
|
||||
assert.Byte(byte(expectedRequest.Command)).Equals(byte(actualRequest.Command))
|
||||
assert.Byte(byte(expectedRequest.Option)).Equals(byte(actualRequest.Option))
|
||||
netassert.Address(expectedRequest.Address).Equals(actualRequest.Address)
|
||||
netassert.Port(expectedRequest.Port).Equals(actualRequest.Port)
|
||||
assert.Address(expectedRequest.Address).Equals(actualRequest.Address)
|
||||
assert.Port(expectedRequest.Port).Equals(actualRequest.Port)
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
|
||||
|
||||
_, err := io.ReadFull(reader, buffer.Value[:protocol.IDBytesLen])
|
||||
if err != nil {
|
||||
log.Error("Raw: Failed to read request header: ", err)
|
||||
return nil, err
|
||||
log.Info("Raw: Failed to read request header: ", err)
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
user, timestamp, valid := this.userValidator.Get(buffer.Value[:protocol.IDBytesLen])
|
||||
@@ -60,7 +60,8 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
|
||||
timestampHash := md5.New()
|
||||
timestampHash.Write(hashTimestamp(timestamp))
|
||||
iv := timestampHash.Sum(nil)
|
||||
aesStream := crypto.NewAesDecryptionStream(user.ID.CmdKey(), iv)
|
||||
account := user.Account.(*protocol.VMessAccount)
|
||||
aesStream := crypto.NewAesDecryptionStream(account.ID.CmdKey(), iv)
|
||||
decryptor := crypto.NewCryptionReader(aesStream, reader)
|
||||
|
||||
nBytes, err := io.ReadFull(decryptor, buffer.Value[:41])
|
||||
@@ -76,7 +77,7 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
|
||||
}
|
||||
|
||||
if request.Version != Version {
|
||||
log.Warning("Raw: Invalid protocol version ", request.Version)
|
||||
log.Info("Raw: Invalid protocol version ", request.Version)
|
||||
return nil, protocol.ErrorInvalidVersion
|
||||
}
|
||||
|
||||
@@ -134,7 +135,7 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
|
||||
fnv1a := fnv.New32a()
|
||||
fnv1a.Write(buffer.Value[:bufferLen])
|
||||
actualHash := fnv1a.Sum32()
|
||||
expectedHash := serial.BytesLiteral(buffer.Value[bufferLen : bufferLen+4]).Uint32Value()
|
||||
expectedHash := serial.BytesToUint32(buffer.Value[bufferLen : bufferLen+4])
|
||||
|
||||
if actualHash != expectedHash {
|
||||
return nil, transport.ErrorCorruptedPacket
|
||||
@@ -158,7 +159,7 @@ func (this *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader,
|
||||
encryptionWriter := crypto.NewCryptionWriter(aesStream, writer)
|
||||
this.responseWriter = encryptionWriter
|
||||
|
||||
encryptionWriter.Write([]byte{this.responseHeader, 0x00})
|
||||
encryptionWriter.Write([]byte{this.responseHeader, byte(header.Option)})
|
||||
err := MarshalCommand(header.Command, encryptionWriter)
|
||||
if err != nil {
|
||||
encryptionWriter.Write([]byte{0x00, 0x00})
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
type Timestamp int64
|
||||
|
||||
func (this Timestamp) Bytes() []byte {
|
||||
return serial.Int64Literal(this).Bytes()
|
||||
return serial.Int64ToBytes(int64(this))
|
||||
}
|
||||
|
||||
type TimestampGenerator func() Timestamp
|
||||
|
||||
@@ -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,19 @@ 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(account Account, level UserLevel, email string) *User {
|
||||
return &User{
|
||||
ID: primary,
|
||||
AlterIDs: secondary,
|
||||
Level: level,
|
||||
Email: email,
|
||||
Account: account,
|
||||
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,28 @@
|
||||
|
||||
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)
|
||||
|
||||
var rawAccount AccountJson
|
||||
if err := json.Unmarshal(data, &rawAccount); err != nil {
|
||||
return err
|
||||
}
|
||||
account, err := rawAccount.GetAccount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
primaryID := NewID(id)
|
||||
alterIDs := NewAlterIDs(primaryID, rawUserValue.AlterIdCount)
|
||||
*u = *NewUser(primaryID, alterIDs, UserLevel(rawUserValue.LevelByte), rawUserValue.EmailString)
|
||||
|
||||
*u = *NewUser(account, UserLevel(rawUserValue.LevelByte), rawUserValue.EmailString)
|
||||
|
||||
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,12 +21,15 @@ 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)
|
||||
|
||||
account, ok := user.Account.(*VMessAccount)
|
||||
assert.Bool(ok).IsTrue()
|
||||
assert.String(account.ID.String()).Equals("96edb838-6d68-42ef-a933-25f7ac3a9d09")
|
||||
}
|
||||
|
||||
func TestInvalidUserJson(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
user := new(User)
|
||||
err := json.Unmarshal([]byte(`{"id": 1234}`), user)
|
||||
@@ -35,7 +37,7 @@ func TestInvalidUserJson(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInvalidIdJson(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
user := new(User)
|
||||
err := json.Unmarshal([]byte(`{"id": "1234"}`), user)
|
||||
|
||||
@@ -50,7 +50,7 @@ func NewTimedUserValidator(hasher IDHash) UserValidator {
|
||||
hasher: hasher,
|
||||
cancel: signal.NewCloseSignal(),
|
||||
}
|
||||
go tus.updateUserHash(time.Tick(updateIntervalSec*time.Second), tus.cancel)
|
||||
go tus.updateUserHash(updateIntervalSec*time.Second, tus.cancel)
|
||||
return tus
|
||||
}
|
||||
|
||||
@@ -88,11 +88,11 @@ func (this *TimedUserValidator) generateNewHashes(nowSec Timestamp, idx int, ent
|
||||
}
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) updateUserHash(tick <-chan time.Time, cancel *signal.CancelSignal) {
|
||||
func (this *TimedUserValidator) updateUserHash(interval time.Duration, cancel *signal.CancelSignal) {
|
||||
L:
|
||||
for {
|
||||
select {
|
||||
case now := <-tick:
|
||||
case now := <-time.After(interval):
|
||||
nowSec := Timestamp(now.Unix() + cacheDurationSec)
|
||||
for _, entry := range this.ids {
|
||||
this.generateNewHashes(nowSec, entry.userIdx, entry)
|
||||
@@ -107,18 +107,19 @@ L:
|
||||
func (this *TimedUserValidator) Add(user *User) error {
|
||||
idx := len(this.validUsers)
|
||||
this.validUsers = append(this.validUsers, user)
|
||||
account := user.Account.(*VMessAccount)
|
||||
|
||||
nowSec := time.Now().Unix()
|
||||
|
||||
entry := &idEntry{
|
||||
id: user.ID,
|
||||
id: account.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 {
|
||||
for _, alterid := range account.AlterIDs {
|
||||
entry := &idEntry{
|
||||
id: alterid,
|
||||
userIdx: idx,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,51 +1,26 @@
|
||||
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 (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()
|
||||
func BytesToUint16(value []byte) uint16 {
|
||||
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()
|
||||
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()
|
||||
func BytesToInt64(value []byte) int64 {
|
||||
return int64(value[0])<<56 +
|
||||
int64(value[1])<<48 +
|
||||
int64(value[2])<<40 +
|
||||
@@ -56,17 +31,10 @@ func (this BytesLiteral) Int64Value() int64 {
|
||||
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,40 +4,15 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Uint16 interface {
|
||||
Value() uint16
|
||||
func Uint16ToBytes(value uint16) []byte {
|
||||
return []byte{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 (this Uint16Literal) Bytes() []byte {
|
||||
return []byte{byte(this >> 8), byte(this)}
|
||||
}
|
||||
|
||||
type Int interface {
|
||||
Value() int
|
||||
}
|
||||
|
||||
type IntLiteral int
|
||||
|
||||
func (this IntLiteral) String() string {
|
||||
return strconv.Itoa(int(this))
|
||||
}
|
||||
|
||||
func (this IntLiteral) Value() int {
|
||||
return int(this)
|
||||
}
|
||||
|
||||
func (this IntLiteral) Bytes() []byte {
|
||||
value := this.Value()
|
||||
func Uint32ToBytes(value uint32) []byte {
|
||||
return []byte{
|
||||
byte(value >> 24),
|
||||
byte(value >> 16),
|
||||
@@ -46,18 +21,20 @@ func (this IntLiteral) Bytes() []byte {
|
||||
}
|
||||
}
|
||||
|
||||
type Int64Literal int64
|
||||
|
||||
func (this Int64Literal) String() string {
|
||||
return strconv.FormatInt(this.Value(), 10)
|
||||
func IntToBytes(value int) []byte {
|
||||
return []byte{
|
||||
byte(value >> 24),
|
||||
byte(value >> 16),
|
||||
byte(value >> 8),
|
||||
byte(value),
|
||||
}
|
||||
}
|
||||
|
||||
func (this Int64Literal) Value() int64 {
|
||||
return int64(this)
|
||||
func IntToString(value int) string {
|
||||
return Int64ToString(int64(value))
|
||||
}
|
||||
|
||||
func (this Int64Literal) Bytes() []byte {
|
||||
value := this.Value()
|
||||
func Int64ToBytes(value int64) []byte {
|
||||
return []byte{
|
||||
byte(value >> 56),
|
||||
byte(value >> 48),
|
||||
@@ -69,3 +46,7 @@ func (this Int64Literal) Bytes() []byte {
|
||||
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")
|
||||
}
|
||||
@@ -4,26 +4,25 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/common/uuid"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestParseBytes(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
str := "2418d087-648d-4990-86e8-19dca1d006d3"
|
||||
bytes := []byte{0x24, 0x18, 0xd0, 0x87, 0x64, 0x8d, 0x49, 0x90, 0x86, 0xe8, 0x19, 0xdc, 0xa1, 0xd0, 0x06, 0xd3}
|
||||
|
||||
uuid, err := ParseBytes(bytes)
|
||||
assert.Error(err).IsNil()
|
||||
assert.String(uuid).Equals(str)
|
||||
assert.String(uuid.String()).Equals(str)
|
||||
|
||||
_, err = ParseBytes([]byte{1, 3, 2, 4})
|
||||
assert.Error(err).Equals(ErrorInvalidID)
|
||||
}
|
||||
|
||||
func TestParseString(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
str := "2418d087-648d-4990-86e8-19dca1d006d3"
|
||||
expectedBytes := []byte{0x24, 0x18, 0xd0, 0x87, 0x64, 0x8d, 0x49, 0x90, 0x86, 0xe8, 0x19, 0xdc, 0xa1, 0xd0, 0x06, 0xd3}
|
||||
@@ -40,28 +39,28 @@ func TestParseString(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewUUID(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
uuid := New()
|
||||
uuid2, err := ParseString(uuid.String())
|
||||
|
||||
assert.Error(err).IsNil()
|
||||
assert.StringLiteral(uuid.String()).Equals(uuid2.String())
|
||||
assert.String(uuid.String()).Equals(uuid2.String())
|
||||
assert.Bytes(uuid.Bytes()).Equals(uuid2.Bytes())
|
||||
}
|
||||
|
||||
func TestRandom(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
uuid := New()
|
||||
uuid2 := New()
|
||||
|
||||
assert.StringLiteral(uuid.String()).NotEquals(uuid2.String())
|
||||
assert.String(uuid.String()).NotEquals(uuid2.String())
|
||||
assert.Bytes(uuid.Bytes()).NotEquals(uuid2.Bytes())
|
||||
}
|
||||
|
||||
func TestEquals(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
var uuid *UUID = nil
|
||||
var uuid2 *UUID = nil
|
||||
@@ -70,7 +69,7 @@ func TestEquals(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNext(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
uuid := New()
|
||||
uuid2 := uuid.Next()
|
||||
|
||||
2
core.go
2
core.go
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "1.12"
|
||||
version = "1.14"
|
||||
build = "Custom"
|
||||
codename = "New Order"
|
||||
intro = "An unified platform for anti-censorship."
|
||||
|
||||
@@ -11,10 +11,13 @@ import (
|
||||
|
||||
// BlackHole is an outbound connection that sliently swallow the entire payload.
|
||||
type BlackHole struct {
|
||||
meta *proxy.OutboundHandlerMeta
|
||||
}
|
||||
|
||||
func NewBlackHole() *BlackHole {
|
||||
return &BlackHole{}
|
||||
func NewBlackHole(space app.Space, config *Config, meta *proxy.OutboundHandlerMeta) *BlackHole {
|
||||
return &BlackHole{
|
||||
meta: meta,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *BlackHole) Dispatch(destination v2net.Destination, payload *alloc.Buffer, ray ray.OutboundRay) error {
|
||||
@@ -31,7 +34,7 @@ func (this *BlackHole) Dispatch(destination v2net.Destination, payload *alloc.Bu
|
||||
|
||||
func init() {
|
||||
internal.MustRegisterOutboundHandlerCreator("blackhole",
|
||||
func(space app.Space, config interface{}) (proxy.OutboundHandler, error) {
|
||||
return NewBlackHole(), nil
|
||||
func(space app.Space, config interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {
|
||||
return NewBlackHole(space, config.(*Config), meta), nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -25,14 +25,15 @@ type DokodemoDoor struct {
|
||||
tcpListener *hub.TCPHub
|
||||
udpHub *hub.UDPHub
|
||||
udpServer *hub.UDPServer
|
||||
listeningPort v2net.Port
|
||||
meta *proxy.InboundHandlerMeta
|
||||
}
|
||||
|
||||
func NewDokodemoDoor(config *Config, space app.Space) *DokodemoDoor {
|
||||
func NewDokodemoDoor(config *Config, space app.Space, meta *proxy.InboundHandlerMeta) *DokodemoDoor {
|
||||
d := &DokodemoDoor{
|
||||
config: config,
|
||||
address: config.Address,
|
||||
port: config.Port,
|
||||
meta: meta,
|
||||
}
|
||||
space.InitializeApplication(func() error {
|
||||
if !space.HasApp(dispatcher.APP_ID) {
|
||||
@@ -46,7 +47,7 @@ func NewDokodemoDoor(config *Config, space app.Space) *DokodemoDoor {
|
||||
}
|
||||
|
||||
func (this *DokodemoDoor) Port() v2net.Port {
|
||||
return this.listeningPort
|
||||
return this.meta.Port
|
||||
}
|
||||
|
||||
func (this *DokodemoDoor) Close() {
|
||||
@@ -65,25 +66,20 @@ func (this *DokodemoDoor) Close() {
|
||||
}
|
||||
}
|
||||
|
||||
func (this *DokodemoDoor) Listen(port v2net.Port) error {
|
||||
func (this *DokodemoDoor) Start() error {
|
||||
if this.accepting {
|
||||
if this.listeningPort == port {
|
||||
return nil
|
||||
} else {
|
||||
return proxy.ErrorAlreadyListening
|
||||
}
|
||||
return nil
|
||||
}
|
||||
this.listeningPort = port
|
||||
this.accepting = true
|
||||
|
||||
if this.config.Network.HasNetwork(v2net.TCPNetwork) {
|
||||
err := this.ListenTCP(port)
|
||||
err := this.ListenTCP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if this.config.Network.HasNetwork(v2net.UDPNetwork) {
|
||||
err := this.ListenUDP(port)
|
||||
err := this.ListenUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -91,11 +87,11 @@ func (this *DokodemoDoor) Listen(port v2net.Port) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *DokodemoDoor) ListenUDP(port v2net.Port) error {
|
||||
func (this *DokodemoDoor) ListenUDP() error {
|
||||
this.udpServer = hub.NewUDPServer(this.packetDispatcher)
|
||||
udpHub, err := hub.ListenUDP(port, this.handleUDPPackets)
|
||||
udpHub, err := hub.ListenUDP(this.meta.Address, this.meta.Port, this.handleUDPPackets)
|
||||
if err != nil {
|
||||
log.Error("Dokodemo failed to listen on port ", port, ": ", err)
|
||||
log.Error("Dokodemo failed to listen on ", this.meta.Address, ":", this.meta.Port, ": ", err)
|
||||
return err
|
||||
}
|
||||
this.udpMutex.Lock()
|
||||
@@ -118,10 +114,10 @@ func (this *DokodemoDoor) handleUDPResponse(dest v2net.Destination, payload *all
|
||||
this.udpHub.WriteTo(payload.Value, dest)
|
||||
}
|
||||
|
||||
func (this *DokodemoDoor) ListenTCP(port v2net.Port) error {
|
||||
tcpListener, err := hub.ListenTCP(port, this.HandleTCPConnection, nil)
|
||||
func (this *DokodemoDoor) ListenTCP() error {
|
||||
tcpListener, err := hub.ListenTCP(this.meta.Address, this.meta.Port, this.HandleTCPConnection, nil)
|
||||
if err != nil {
|
||||
log.Error("Dokodemo: Failed to listen on port ", port, ": ", err)
|
||||
log.Error("Dokodemo: Failed to listen on ", this.meta.Address, ":", this.meta.Port, ": ", err)
|
||||
return err
|
||||
}
|
||||
this.tcpMutex.Lock()
|
||||
@@ -166,7 +162,7 @@ func (this *DokodemoDoor) HandleTCPConnection(conn *hub.Connection) {
|
||||
|
||||
func init() {
|
||||
internal.MustRegisterInboundHandlerCreator("dokodemo-door",
|
||||
func(space app.Space, rawConfig interface{}) (proxy.InboundHandler, error) {
|
||||
return NewDokodemoDoor(rawConfig.(*Config), space), nil
|
||||
func(space app.Space, rawConfig interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {
|
||||
return NewDokodemoDoor(rawConfig.(*Config), space, meta), nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,17 +10,16 @@ import (
|
||||
"github.com/v2ray/v2ray-core/app/proxyman"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
|
||||
netassert "github.com/v2ray/v2ray-core/common/net/testing/assert"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
. "github.com/v2ray/v2ray-core/proxy/dokodemo"
|
||||
"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/testing/servers/tcp"
|
||||
"github.com/v2ray/v2ray-core/testing/servers/udp"
|
||||
)
|
||||
|
||||
func TestDokodemoTCP(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
tcpServer := &tcp.Server{
|
||||
Port: v2nettesting.PickPort(),
|
||||
@@ -39,25 +38,27 @@ func TestDokodemoTCP(t *testing.T) {
|
||||
space := app.NewSpace()
|
||||
space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space))
|
||||
ohm := proxyman.NewDefaultOutboundHandlerManager()
|
||||
ohm.SetDefaultHandler(&freedom.FreedomConnection{})
|
||||
ohm.SetDefaultHandler(
|
||||
freedom.NewFreedomConnection(
|
||||
&freedom.Config{}, space, &proxy.OutboundHandlerMeta{Address: v2net.LocalHostIP}))
|
||||
space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, ohm)
|
||||
|
||||
data2Send := "Data to be sent to remote."
|
||||
|
||||
port := v2nettesting.PickPort()
|
||||
dokodemo := NewDokodemoDoor(&Config{
|
||||
Address: v2net.LocalHostIP,
|
||||
Port: tcpServer.Port,
|
||||
Network: v2net.TCPNetwork.AsList(),
|
||||
Timeout: 600,
|
||||
}, space)
|
||||
}, space, &proxy.InboundHandlerMeta{Address: v2net.LocalHostIP, Port: port})
|
||||
defer dokodemo.Close()
|
||||
|
||||
assert.Error(space.Initialize()).IsNil()
|
||||
|
||||
port := v2nettesting.PickPort()
|
||||
err = dokodemo.Listen(port)
|
||||
err = dokodemo.Start()
|
||||
assert.Error(err).IsNil()
|
||||
netassert.Port(port).Equals(dokodemo.Port())
|
||||
assert.Port(port).Equals(dokodemo.Port())
|
||||
|
||||
tcpClient, err := net.DialTCP("tcp", nil, &net.TCPAddr{
|
||||
IP: []byte{127, 0, 0, 1},
|
||||
@@ -74,11 +75,11 @@ func TestDokodemoTCP(t *testing.T) {
|
||||
assert.Error(err).IsNil()
|
||||
tcpClient.Close()
|
||||
|
||||
assert.StringLiteral("Processed: " + data2Send).Equals(string(response[:nBytes]))
|
||||
assert.String("Processed: " + data2Send).Equals(string(response[:nBytes]))
|
||||
}
|
||||
|
||||
func TestDokodemoUDP(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
udpServer := &udp.Server{
|
||||
Port: v2nettesting.PickPort(),
|
||||
@@ -97,25 +98,27 @@ func TestDokodemoUDP(t *testing.T) {
|
||||
space := app.NewSpace()
|
||||
space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space))
|
||||
ohm := proxyman.NewDefaultOutboundHandlerManager()
|
||||
ohm.SetDefaultHandler(&freedom.FreedomConnection{})
|
||||
ohm.SetDefaultHandler(
|
||||
freedom.NewFreedomConnection(
|
||||
&freedom.Config{}, space, &proxy.OutboundHandlerMeta{Address: v2net.AnyIP}))
|
||||
space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, ohm)
|
||||
|
||||
data2Send := "Data to be sent to remote."
|
||||
|
||||
port := v2nettesting.PickPort()
|
||||
dokodemo := NewDokodemoDoor(&Config{
|
||||
Address: v2net.LocalHostIP,
|
||||
Port: udpServer.Port,
|
||||
Network: v2net.UDPNetwork.AsList(),
|
||||
Timeout: 600,
|
||||
}, space)
|
||||
}, space, &proxy.InboundHandlerMeta{Address: v2net.LocalHostIP, Port: port})
|
||||
defer dokodemo.Close()
|
||||
|
||||
assert.Error(space.Initialize()).IsNil()
|
||||
|
||||
port := v2nettesting.PickPort()
|
||||
err = dokodemo.Listen(port)
|
||||
err = dokodemo.Start()
|
||||
assert.Error(err).IsNil()
|
||||
netassert.Port(port).Equals(dokodemo.Port())
|
||||
assert.Port(port).Equals(dokodemo.Port())
|
||||
|
||||
udpClient, err := net.DialUDP("udp", nil, &net.UDPAddr{
|
||||
IP: []byte{127, 0, 0, 1},
|
||||
@@ -130,6 +133,6 @@ func TestDokodemoUDP(t *testing.T) {
|
||||
response := make([]byte, 1024)
|
||||
nBytes, addr, err := udpClient.ReadFromUDP(response)
|
||||
assert.Error(err).IsNil()
|
||||
netassert.IP(addr.IP).Equals(v2net.LocalHostIP.IP())
|
||||
assert.IP(addr.IP).Equals(v2net.LocalHostIP.IP())
|
||||
assert.Bytes(response[:nBytes]).Equals([]byte("Processed: " + data2Send))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
package freedom
|
||||
|
||||
type DomainStrategy int
|
||||
|
||||
const (
|
||||
DomainStrategyAsIs = DomainStrategy(0)
|
||||
DomainStrategyUseIP = DomainStrategy(1)
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DomainStrategy DomainStrategy
|
||||
Timeout uint32
|
||||
}
|
||||
|
||||
@@ -3,12 +3,37 @@
|
||||
package freedom
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/v2ray/v2ray-core/proxy/internal/config"
|
||||
)
|
||||
|
||||
func (this *Config) UnmarshalJSON(data []byte) error {
|
||||
type JsonConfig struct {
|
||||
DomainStrategy string `json:"domainStrategy"`
|
||||
Timeout uint32 `json:"timeout"`
|
||||
}
|
||||
jsonConfig := new(JsonConfig)
|
||||
if err := json.Unmarshal(data, jsonConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
this.DomainStrategy = DomainStrategyAsIs
|
||||
domainStrategy := strings.ToLower(jsonConfig.DomainStrategy)
|
||||
if domainStrategy == "useip" {
|
||||
this.DomainStrategy = DomainStrategyUseIP
|
||||
}
|
||||
this.Timeout = jsonConfig.Timeout
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
config.RegisterOutboundConfig("freedom",
|
||||
func(data []byte) (interface{}, error) {
|
||||
return new(Config), nil
|
||||
c := new(Config)
|
||||
if err := json.Unmarshal(data, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,16 +5,67 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
"github.com/v2ray/v2ray-core/app/dns"
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
v2io "github.com/v2ray/v2ray-core/common/io"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/retry"
|
||||
"github.com/v2ray/v2ray-core/transport/dialer"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/proxy/internal"
|
||||
"github.com/v2ray/v2ray-core/transport/hub"
|
||||
"github.com/v2ray/v2ray-core/transport/ray"
|
||||
)
|
||||
|
||||
type FreedomConnection struct {
|
||||
domainStrategy DomainStrategy
|
||||
timeout uint32
|
||||
dns dns.Server
|
||||
meta *proxy.OutboundHandlerMeta
|
||||
}
|
||||
|
||||
func NewFreedomConnection(config *Config, space app.Space, meta *proxy.OutboundHandlerMeta) *FreedomConnection {
|
||||
f := &FreedomConnection{
|
||||
domainStrategy: config.DomainStrategy,
|
||||
timeout: config.Timeout,
|
||||
meta: meta,
|
||||
}
|
||||
space.InitializeApplication(func() error {
|
||||
if config.DomainStrategy == DomainStrategyUseIP {
|
||||
if !space.HasApp(dns.APP_ID) {
|
||||
log.Error("Freedom: DNS server is not found in the space.")
|
||||
return app.ErrorMissingApplication
|
||||
}
|
||||
f.dns = space.GetApp(dns.APP_ID).(dns.Server)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return f
|
||||
}
|
||||
|
||||
// @Private
|
||||
func (this *FreedomConnection) ResolveIP(destination v2net.Destination) v2net.Destination {
|
||||
if !destination.Address().IsDomain() {
|
||||
return destination
|
||||
}
|
||||
|
||||
ips := this.dns.Get(destination.Address().Domain())
|
||||
if len(ips) == 0 {
|
||||
log.Info("Freedom: DNS returns nil answer. Keep domain as is.")
|
||||
return destination
|
||||
}
|
||||
|
||||
ip := ips[dice.Roll(len(ips))]
|
||||
var newDest v2net.Destination
|
||||
if destination.IsTCP() {
|
||||
newDest = v2net.TCPDestination(v2net.IPAddress(ip), destination.Port())
|
||||
} else {
|
||||
newDest = v2net.UDPDestination(v2net.IPAddress(ip), destination.Port())
|
||||
}
|
||||
log.Info("Freedom: Changing destination from ", destination, " to ", newDest)
|
||||
return newDest
|
||||
}
|
||||
|
||||
func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload *alloc.Buffer, ray ray.OutboundRay) error {
|
||||
@@ -25,8 +76,11 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload *
|
||||
defer ray.OutboundOutput().Close()
|
||||
|
||||
var conn net.Conn
|
||||
if this.domainStrategy == DomainStrategyUseIP && destination.Address().IsDomain() {
|
||||
destination = this.ResolveIP(destination)
|
||||
}
|
||||
err := retry.Timed(5, 100).On(func() error {
|
||||
rawConn, err := dialer.Dial(destination)
|
||||
rawConn, err := hub.DialWithoutCache(this.meta.Address, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -34,7 +88,7 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload *
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Freedom: Failed to open connection to ", destination, ": ", err)
|
||||
log.Warning("Freedom: Failed to open connection to ", destination, ": ", err)
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
@@ -60,8 +114,12 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload *
|
||||
|
||||
var reader io.Reader = conn
|
||||
|
||||
timeout := this.timeout
|
||||
if destination.IsUDP() {
|
||||
reader = v2net.NewTimeOutReader(16 /* seconds */, conn)
|
||||
timeout = 16
|
||||
}
|
||||
if timeout > 0 {
|
||||
reader = v2net.NewTimeOutReader(int(timeout) /* seconds */, conn)
|
||||
}
|
||||
|
||||
v2reader := v2io.NewAdaptiveReader(reader)
|
||||
@@ -79,3 +137,10 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload *
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
internal.MustRegisterOutboundHandlerCreator("freedom",
|
||||
func(space app.Space, config interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {
|
||||
return NewFreedomConnection(config.(*Config), space, meta), nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
package freedom_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
"github.com/v2ray/v2ray-core/app/dispatcher"
|
||||
dispatchers "github.com/v2ray/v2ray-core/app/dispatcher/impl"
|
||||
"github.com/v2ray/v2ray-core/app/dns"
|
||||
"github.com/v2ray/v2ray-core/app/proxyman"
|
||||
"github.com/v2ray/v2ray-core/app/router"
|
||||
"github.com/v2ray/v2ray-core/app/router/rules"
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
|
||||
"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/testing/servers/tcp"
|
||||
"github.com/v2ray/v2ray-core/transport/ray"
|
||||
)
|
||||
|
||||
func TestSinglePacket(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
port := v2nettesting.PickPort()
|
||||
|
||||
tcpServer := &tcp.Server{
|
||||
@@ -29,7 +37,10 @@ func TestSinglePacket(t *testing.T) {
|
||||
_, err := tcpServer.Start()
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
freedom := &FreedomConnection{}
|
||||
space := app.NewSpace()
|
||||
freedom := NewFreedomConnection(&Config{}, space, &proxy.OutboundHandlerMeta{Address: v2net.AnyIP})
|
||||
space.Initialize()
|
||||
|
||||
traffic := ray.NewRay()
|
||||
data2Send := "Data to be sent to remote"
|
||||
payload := alloc.NewSmallBuffer().Clear().Append([]byte(data2Send))
|
||||
@@ -45,9 +56,9 @@ func TestSinglePacket(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUnreachableDestination(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
freedom := &FreedomConnection{}
|
||||
freedom := NewFreedomConnection(&Config{}, app.NewSpace(), &proxy.OutboundHandlerMeta{Address: v2net.AnyIP})
|
||||
traffic := ray.NewRay()
|
||||
data2Send := "Data to be sent to remote"
|
||||
payload := alloc.NewSmallBuffer().Clear().Append([]byte(data2Send))
|
||||
@@ -55,3 +66,30 @@ func TestUnreachableDestination(t *testing.T) {
|
||||
err := freedom.Dispatch(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 128), payload, traffic)
|
||||
assert.Error(err).IsNotNil()
|
||||
}
|
||||
|
||||
func TestIPResolution(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
space := app.NewSpace()
|
||||
space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, proxyman.NewDefaultOutboundHandlerManager())
|
||||
space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space))
|
||||
r, _ := router.CreateRouter("rules", &rules.RouterRuleConfig{}, space)
|
||||
space.BindApp(router.APP_ID, r)
|
||||
dnsServer := dns.NewCacheServer(space, &dns.Config{
|
||||
Hosts: map[string]net.IP{
|
||||
"v2ray.com": net.IP([]byte{127, 0, 0, 1}),
|
||||
},
|
||||
})
|
||||
space.BindApp(dns.APP_ID, dnsServer)
|
||||
|
||||
freedom := NewFreedomConnection(
|
||||
&Config{DomainStrategy: DomainStrategyUseIP},
|
||||
space,
|
||||
&proxy.OutboundHandlerMeta{Address: v2net.AnyIP})
|
||||
|
||||
space.Initialize()
|
||||
|
||||
ipDest := freedom.ResolveIP(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), v2net.Port(80)))
|
||||
assert.Destination(ipDest).IsTCP()
|
||||
assert.Address(ipDest.Address()).Equals(v2net.LocalHostIP)
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package freedom
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/proxy/internal"
|
||||
)
|
||||
|
||||
func init() {
|
||||
internal.MustRegisterOutboundHandlerCreator("freedom",
|
||||
func(space app.Space, config interface{}) (proxy.OutboundHandler, error) {
|
||||
return &FreedomConnection{}, nil
|
||||
})
|
||||
}
|
||||
@@ -1 +1,4 @@
|
||||
package http
|
||||
|
||||
type Client struct {
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
import "crypto/tls"
|
||||
|
||||
// CertificateConfig is the config for TLS certificates used in HTTP proxy.
|
||||
type CertificateConfig struct {
|
||||
Domain string
|
||||
Certificate tls.Certificate
|
||||
}
|
||||
|
||||
type TlsConfig struct {
|
||||
// TlsConfig is the config for TLS connections.
|
||||
type TLSConfig struct {
|
||||
Enabled bool
|
||||
Certs []*CertificateConfig
|
||||
}
|
||||
|
||||
func (this *TlsConfig) GetConfig() *tls.Config {
|
||||
// GetConfig returns corresponding tls.Config.
|
||||
func (this *TLSConfig) GetConfig() *tls.Config {
|
||||
if !this.Enabled {
|
||||
return nil
|
||||
}
|
||||
@@ -35,16 +34,11 @@ func (this *TlsConfig) GetConfig() *tls.Config {
|
||||
return config
|
||||
}
|
||||
|
||||
// Config for HTTP proxy server.
|
||||
type Config struct {
|
||||
OwnHosts []v2net.Address
|
||||
TlsConfig *TlsConfig
|
||||
TLSConfig *TLSConfig
|
||||
}
|
||||
|
||||
func (this *Config) IsOwnHost(host v2net.Address) bool {
|
||||
for _, ownHost := range this.OwnHosts {
|
||||
if ownHost.Equals(host) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
// ClientConfig for HTTP proxy client.
|
||||
type ClientConfig struct {
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy/internal/config"
|
||||
)
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler
|
||||
func (this *CertificateConfig) UnmarshalJSON(data []byte) error {
|
||||
type JsonConfig struct {
|
||||
Domain string `json:"domain"`
|
||||
@@ -30,7 +30,8 @@ func (this *CertificateConfig) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *TlsConfig) UnmarshalJSON(data []byte) error {
|
||||
// UnmarshalJSON implements json.Unmarshaler
|
||||
func (this *TLSConfig) UnmarshalJSON(data []byte) error {
|
||||
type JsonConfig struct {
|
||||
Enabled bool `json:"enable"`
|
||||
Certs []*CertificateConfig `json:"certs"`
|
||||
@@ -45,26 +46,17 @@ func (this *TlsConfig) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler
|
||||
func (this *Config) UnmarshalJSON(data []byte) error {
|
||||
type JsonConfig struct {
|
||||
Hosts []v2net.AddressJson `json:"ownHosts"`
|
||||
Tls *TlsConfig `json:"tls"`
|
||||
Tls *TLSConfig `json:"tls"`
|
||||
}
|
||||
jsonConfig := new(JsonConfig)
|
||||
if err := json.Unmarshal(data, jsonConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
this.OwnHosts = make([]v2net.Address, len(jsonConfig.Hosts), len(jsonConfig.Hosts)+1)
|
||||
for idx, host := range jsonConfig.Hosts {
|
||||
this.OwnHosts[idx] = host.Address
|
||||
}
|
||||
|
||||
v2rayHost := v2net.DomainAddress("local.v2ray.com")
|
||||
if !this.IsOwnHost(v2rayHost) {
|
||||
this.OwnHosts = append(this.OwnHosts, v2rayHost)
|
||||
}
|
||||
|
||||
this.TlsConfig = jsonConfig.Tls
|
||||
this.TLSConfig = jsonConfig.Tls
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,32 +1,3 @@
|
||||
// +build json
|
||||
|
||||
package http_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
. "github.com/v2ray/v2ray-core/proxy/http"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestOwnHosts(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
|
||||
rawJson := `{
|
||||
"ownHosts": [
|
||||
"127.0.0.1",
|
||||
"google.com"
|
||||
]
|
||||
}`
|
||||
|
||||
config := new(Config)
|
||||
err := json.Unmarshal([]byte(rawJson), config)
|
||||
assert.Error(err).IsNil()
|
||||
assert.Bool(config.IsOwnHost(v2net.IPAddress([]byte{127, 0, 0, 1}))).IsTrue()
|
||||
assert.Bool(config.IsOwnHost(v2net.DomainAddress("google.com"))).IsTrue()
|
||||
assert.Bool(config.IsOwnHost(v2net.DomainAddress("local.v2ray.com"))).IsTrue()
|
||||
assert.Bool(config.IsOwnHost(v2net.DomainAddress("v2ray.com"))).IsFalse()
|
||||
}
|
||||
|
||||
@@ -16,34 +16,35 @@ import (
|
||||
v2io "github.com/v2ray/v2ray-core/common/io"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/proxy/internal"
|
||||
"github.com/v2ray/v2ray-core/transport/hub"
|
||||
"github.com/v2ray/v2ray-core/transport/ray"
|
||||
)
|
||||
|
||||
type HttpProxyServer struct {
|
||||
// Server is a HTTP proxy server.
|
||||
type Server struct {
|
||||
sync.Mutex
|
||||
accepting bool
|
||||
packetDispatcher dispatcher.PacketDispatcher
|
||||
config *Config
|
||||
tcpListener *hub.TCPHub
|
||||
listeningPort v2net.Port
|
||||
meta *proxy.InboundHandlerMeta
|
||||
}
|
||||
|
||||
func NewHttpProxyServer(config *Config, packetDispatcher dispatcher.PacketDispatcher) *HttpProxyServer {
|
||||
return &HttpProxyServer{
|
||||
func NewServer(config *Config, packetDispatcher dispatcher.PacketDispatcher, meta *proxy.InboundHandlerMeta) *Server {
|
||||
return &Server{
|
||||
packetDispatcher: packetDispatcher,
|
||||
config: config,
|
||||
meta: meta,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *HttpProxyServer) Port() v2net.Port {
|
||||
return this.listeningPort
|
||||
func (this *Server) Port() v2net.Port {
|
||||
return this.meta.Port
|
||||
}
|
||||
|
||||
func (this *HttpProxyServer) Close() {
|
||||
func (this *Server) Close() {
|
||||
this.accepting = false
|
||||
if this.tcpListener != nil {
|
||||
this.Lock()
|
||||
@@ -53,23 +54,18 @@ func (this *HttpProxyServer) Close() {
|
||||
}
|
||||
}
|
||||
|
||||
func (this *HttpProxyServer) Listen(port v2net.Port) error {
|
||||
func (this *Server) Start() error {
|
||||
if this.accepting {
|
||||
if this.listeningPort == port {
|
||||
return nil
|
||||
} else {
|
||||
return proxy.ErrorAlreadyListening
|
||||
}
|
||||
return nil
|
||||
}
|
||||
this.listeningPort = port
|
||||
|
||||
var tlsConfig *tls.Config = nil
|
||||
if this.config.TlsConfig != nil {
|
||||
tlsConfig = this.config.TlsConfig.GetConfig()
|
||||
var tlsConfig *tls.Config
|
||||
if this.config.TLSConfig != nil {
|
||||
tlsConfig = this.config.TLSConfig.GetConfig()
|
||||
}
|
||||
tcpListener, err := hub.ListenTCP(port, this.handleConnection, tlsConfig)
|
||||
tcpListener, err := hub.ListenTCP(this.meta.Address, this.meta.Port, this.handleConnection, tlsConfig)
|
||||
if err != nil {
|
||||
log.Error("Http: Failed listen on port ", port, ": ", err)
|
||||
log.Error("HTTP: Failed listen on ", this.meta.Address, ":", this.meta.Port, ": ", err)
|
||||
return err
|
||||
}
|
||||
this.Lock()
|
||||
@@ -102,16 +98,18 @@ func parseHost(rawHost string, defaultPort v2net.Port) (v2net.Destination, error
|
||||
return v2net.TCPDestination(v2net.DomainAddress(host), port), nil
|
||||
}
|
||||
|
||||
func (this *HttpProxyServer) handleConnection(conn *hub.Connection) {
|
||||
func (this *Server) handleConnection(conn *hub.Connection) {
|
||||
defer conn.Close()
|
||||
reader := bufio.NewReader(conn)
|
||||
|
||||
request, err := http.ReadRequest(reader)
|
||||
if err != nil {
|
||||
log.Warning("Failed to read http request: ", err)
|
||||
if err != io.EOF {
|
||||
log.Warning("HTTP: Failed to read http request: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
log.Info("Request to Method [", request.Method, "] Host [", request.Host, "] with URL [", request.URL, "]")
|
||||
log.Info("HTTP: Request to Method [", request.Method, "] Host [", request.Host, "] with URL [", request.URL, "]")
|
||||
defaultPort := v2net.Port(80)
|
||||
if strings.ToLower(request.URL.Scheme) == "https" {
|
||||
defaultPort = v2net.Port(443)
|
||||
@@ -122,7 +120,7 @@ func (this *HttpProxyServer) handleConnection(conn *hub.Connection) {
|
||||
}
|
||||
dest, err := parseHost(host, defaultPort)
|
||||
if err != nil {
|
||||
log.Warning("Malformed proxy host (", host, "): ", err)
|
||||
log.Warning("HTTP: Malformed proxy host (", host, "): ", err)
|
||||
return
|
||||
}
|
||||
if strings.ToUpper(request.Method) == "CONNECT" {
|
||||
@@ -132,7 +130,7 @@ func (this *HttpProxyServer) handleConnection(conn *hub.Connection) {
|
||||
}
|
||||
}
|
||||
|
||||
func (this *HttpProxyServer) handleConnect(request *http.Request, destination v2net.Destination, reader io.Reader, writer io.Writer) {
|
||||
func (this *Server) handleConnect(request *http.Request, destination v2net.Destination, reader io.Reader, writer io.Writer) {
|
||||
response := &http.Response{
|
||||
Status: "200 OK",
|
||||
StatusCode: 200,
|
||||
@@ -154,7 +152,7 @@ func (this *HttpProxyServer) handleConnect(request *http.Request, destination v2
|
||||
this.transport(reader, writer, ray)
|
||||
}
|
||||
|
||||
func (this *HttpProxyServer) transport(input io.Reader, output io.Writer, ray ray.InboundRay) {
|
||||
func (this *Server) transport(input io.Reader, output io.Writer, ray ray.InboundRay) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
defer wg.Wait()
|
||||
@@ -203,21 +201,25 @@ func StripHopByHopHeaders(request *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (this *HttpProxyServer) handlePlainHTTP(request *http.Request, dest v2net.Destination, reader *bufio.Reader, writer io.Writer) {
|
||||
func (this *Server) GenerateResponse(statusCode int, status string) *http.Response {
|
||||
hdr := http.Header(make(map[string][]string))
|
||||
hdr.Set("Connection", "close")
|
||||
return &http.Response{
|
||||
Status: status,
|
||||
StatusCode: statusCode,
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: hdr,
|
||||
Body: nil,
|
||||
ContentLength: 0,
|
||||
Close: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Server) handlePlainHTTP(request *http.Request, dest v2net.Destination, reader *bufio.Reader, writer io.Writer) {
|
||||
if len(request.URL.Host) <= 0 {
|
||||
hdr := http.Header(make(map[string][]string))
|
||||
hdr.Set("Connection", "close")
|
||||
response := &http.Response{
|
||||
Status: "400 Bad Request",
|
||||
StatusCode: 400,
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: hdr,
|
||||
Body: nil,
|
||||
ContentLength: 0,
|
||||
Close: false,
|
||||
}
|
||||
response := this.GenerateResponse(400, "Bad Request")
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
response.Write(buffer)
|
||||
@@ -229,40 +231,52 @@ func (this *HttpProxyServer) handlePlainHTTP(request *http.Request, dest v2net.D
|
||||
request.Host = request.URL.Host
|
||||
StripHopByHopHeaders(request)
|
||||
|
||||
requestBuffer := alloc.NewBuffer().Clear() // Don't release this buffer as it is passed into a Packet.
|
||||
request.Write(requestBuffer)
|
||||
log.Debug("Request to remote:\n", serial.BytesLiteral(requestBuffer.Value))
|
||||
|
||||
ray := this.packetDispatcher.DispatchToOutbound(dest)
|
||||
ray.InboundInput().Write(requestBuffer)
|
||||
defer ray.InboundInput().Close()
|
||||
defer ray.InboundOutput().Release()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
var finish sync.WaitGroup
|
||||
finish.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
responseReader := bufio.NewReader(NewChanReader(ray.InboundOutput()))
|
||||
response, err := http.ReadResponse(responseReader, request)
|
||||
defer finish.Done()
|
||||
requestWriter := v2io.NewBufferedWriter(v2io.NewChainWriter(ray.InboundInput()))
|
||||
err := request.Write(requestWriter)
|
||||
if err != nil {
|
||||
log.Warning("HTTP: Failed to write request: ", err)
|
||||
return
|
||||
}
|
||||
responseBuffer := alloc.NewBuffer().Clear()
|
||||
defer responseBuffer.Release()
|
||||
response.Write(responseBuffer)
|
||||
writer.Write(responseBuffer.Value)
|
||||
response.Body.Close()
|
||||
requestWriter.Flush()
|
||||
}()
|
||||
wg.Wait()
|
||||
|
||||
finish.Add(1)
|
||||
go func() {
|
||||
defer finish.Done()
|
||||
responseReader := bufio.NewReader(v2io.NewChanReader(ray.InboundOutput()))
|
||||
response, err := http.ReadResponse(responseReader, request)
|
||||
if err != nil {
|
||||
log.Warning("HTTP: Failed to read response: ", err)
|
||||
response = this.GenerateResponse(503, "Service Unavailable")
|
||||
}
|
||||
responseWriter := v2io.NewBufferedWriter(writer)
|
||||
err = response.Write(responseWriter)
|
||||
if err != nil {
|
||||
log.Warning("HTTP: Failed to write response: ", err)
|
||||
return
|
||||
}
|
||||
responseWriter.Flush()
|
||||
}()
|
||||
finish.Wait()
|
||||
}
|
||||
|
||||
func init() {
|
||||
internal.MustRegisterInboundHandlerCreator("http",
|
||||
func(space app.Space, rawConfig interface{}) (proxy.InboundHandler, error) {
|
||||
func(space app.Space, rawConfig interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {
|
||||
if !space.HasApp(dispatcher.APP_ID) {
|
||||
return nil, internal.ErrorBadConfiguration
|
||||
}
|
||||
return NewHttpProxyServer(
|
||||
return NewServer(
|
||||
rawConfig.(*Config),
|
||||
space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher)), nil
|
||||
space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher),
|
||||
meta), nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,15 +7,15 @@ import (
|
||||
"testing"
|
||||
|
||||
testdispatcher "github.com/v2ray/v2ray-core/app/dispatcher/testing"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
|
||||
netassert "github.com/v2ray/v2ray-core/common/net/testing/assert"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
. "github.com/v2ray/v2ray-core/proxy/http"
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestHopByHopHeadersStrip(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
rawRequest := `GET /pkg/net/http/ HTTP/1.1
|
||||
Host: golang.org
|
||||
@@ -34,32 +34,32 @@ Accept-Language: de,en;q=0.7,en-us;q=0.3
|
||||
b := bufio.NewReader(strings.NewReader(rawRequest))
|
||||
req, err := http.ReadRequest(b)
|
||||
assert.Error(err).IsNil()
|
||||
assert.StringLiteral(req.Header.Get("Foo")).Equals("foo")
|
||||
assert.StringLiteral(req.Header.Get("Bar")).Equals("bar")
|
||||
assert.StringLiteral(req.Header.Get("Connection")).Equals("keep-alive,Foo, Bar")
|
||||
assert.StringLiteral(req.Header.Get("Proxy-Connection")).Equals("keep-alive")
|
||||
assert.StringLiteral(req.Header.Get("Proxy-Authenticate")).Equals("abc")
|
||||
assert.String(req.Header.Get("Foo")).Equals("foo")
|
||||
assert.String(req.Header.Get("Bar")).Equals("bar")
|
||||
assert.String(req.Header.Get("Connection")).Equals("keep-alive,Foo, Bar")
|
||||
assert.String(req.Header.Get("Proxy-Connection")).Equals("keep-alive")
|
||||
assert.String(req.Header.Get("Proxy-Authenticate")).Equals("abc")
|
||||
|
||||
StripHopByHopHeaders(req)
|
||||
assert.StringLiteral(req.Header.Get("Connection")).Equals("close")
|
||||
assert.StringLiteral(req.Header.Get("Foo")).Equals("")
|
||||
assert.StringLiteral(req.Header.Get("Bar")).Equals("")
|
||||
assert.StringLiteral(req.Header.Get("Proxy-Connection")).Equals("")
|
||||
assert.StringLiteral(req.Header.Get("Proxy-Authenticate")).Equals("")
|
||||
assert.String(req.Header.Get("Connection")).Equals("close")
|
||||
assert.String(req.Header.Get("Foo")).Equals("")
|
||||
assert.String(req.Header.Get("Bar")).Equals("")
|
||||
assert.String(req.Header.Get("Proxy-Connection")).Equals("")
|
||||
assert.String(req.Header.Get("Proxy-Authenticate")).Equals("")
|
||||
}
|
||||
|
||||
func TestNormalGetRequest(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
|
||||
testPacketDispatcher := testdispatcher.NewTestPacketDispatcher(nil)
|
||||
|
||||
httpProxy := NewHttpProxyServer(&Config{}, testPacketDispatcher)
|
||||
port := v2nettesting.PickPort()
|
||||
httpProxy := NewServer(&Config{}, testPacketDispatcher, &proxy.InboundHandlerMeta{Address: v2net.LocalHostIP, Port: port})
|
||||
defer httpProxy.Close()
|
||||
|
||||
port := v2nettesting.PickPort()
|
||||
err := httpProxy.Listen(port)
|
||||
err := httpProxy.Start()
|
||||
assert.Error(err).IsNil()
|
||||
netassert.Port(port).Equals(httpProxy.Port())
|
||||
assert.Port(port).Equals(httpProxy.Port())
|
||||
|
||||
httpClient := &http.Client{}
|
||||
resp, err := httpClient.Get("http://127.0.0.1:" + port.String() + "/")
|
||||
|
||||
@@ -3,12 +3,11 @@ package config
|
||||
import (
|
||||
"testing"
|
||||
|
||||
v2testing "github.com/v2ray/v2ray-core/testing"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestRegisterInboundConfig(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
initializeConfigCache()
|
||||
|
||||
protocol := "test_protocol"
|
||||
@@ -29,7 +28,7 @@ func TestRegisterInboundConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRegisterOutboundConfig(t *testing.T) {
|
||||
v2testing.Current(t)
|
||||
assert := assert.On(t)
|
||||
initializeConfigCache()
|
||||
|
||||
protocol := "test_protocol"
|
||||
|
||||
@@ -5,5 +5,5 @@ import (
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
)
|
||||
|
||||
type InboundHandlerCreator func(space app.Space, config interface{}) (proxy.InboundHandler, error)
|
||||
type OutboundHandlerCreator func(space app.Space, config interface{}) (proxy.OutboundHandler, error)
|
||||
type InboundHandlerCreator func(space app.Space, config interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error)
|
||||
type OutboundHandlerCreator func(space app.Space, config interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error)
|
||||
|
||||
@@ -45,7 +45,7 @@ func MustRegisterOutboundHandlerCreator(name string, creator OutboundHandlerCrea
|
||||
}
|
||||
}
|
||||
|
||||
func CreateInboundHandler(name string, space app.Space, rawConfig []byte) (proxy.InboundHandler, error) {
|
||||
func CreateInboundHandler(name string, space app.Space, rawConfig []byte, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {
|
||||
creator, found := inboundFactories[name]
|
||||
if !found {
|
||||
return nil, ErrorProxyNotFound
|
||||
@@ -55,12 +55,12 @@ func CreateInboundHandler(name string, space app.Space, rawConfig []byte) (proxy
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return creator(space, proxyConfig)
|
||||
return creator(space, proxyConfig, meta)
|
||||
}
|
||||
return creator(space, nil)
|
||||
return creator(space, nil, meta)
|
||||
}
|
||||
|
||||
func CreateOutboundHandler(name string, space app.Space, rawConfig []byte) (proxy.OutboundHandler, error) {
|
||||
func CreateOutboundHandler(name string, space app.Space, rawConfig []byte, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {
|
||||
creator, found := outboundFactories[name]
|
||||
if !found {
|
||||
return nil, ErrorNameExists
|
||||
@@ -71,8 +71,8 @@ func CreateOutboundHandler(name string, space app.Space, rawConfig []byte) (prox
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return creator(space, proxyConfig)
|
||||
return creator(space, proxyConfig, meta)
|
||||
}
|
||||
|
||||
return creator(space, nil)
|
||||
return creator(space, nil, meta)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user