Compare commits

..

66 Commits

Author SHA1 Message Date
v2ray
798c870069 Merge branch 'master' of https://github.com/v2ray/v2ray-core 2016-06-04 17:48:51 +02:00
v2ray
372a32a66f more china sites 2016-06-04 17:48:23 +02:00
v2ray
50ca869929 organize handler metadata 2016-06-04 14:25:13 +02:00
v2ray
030fca10ba wait for test ready 2016-06-04 13:27:17 +02:00
v2ray
243e3ced89 update 2016-06-04 12:41:48 +02:00
v2ray
a4d76dc394 support using specific address 2016-06-04 00:38:22 +02:00
v2ray
6c31ff91e6 Merge branch 'master' of https://github.com/v2ray/v2ray-core 2016-06-03 20:21:53 +02:00
v2ray
4b92e6b25b update log 2016-06-03 20:21:46 +02:00
v2ray
e0c22860f1 more china sites 2016-06-03 12:47:22 +02:00
Darien Raymond
a2d8d55ba1 Update io_test.go 2016-06-03 11:40:50 +02:00
v2ray
0a7deabd46 remove unnecessary log 2016-06-02 23:23:04 +02:00
v2ray
47338fba1e timeout setting for freedom 2016-06-02 23:18:44 +02:00
v2ray
2f47074c98 release buffer 2016-06-02 22:23:05 +02:00
v2ray
f3975aafbf remove unnecessary log 2016-06-02 21:49:57 +02:00
v2ray
7a082ce63a update test 2016-06-02 21:48:49 +02:00
v2ray
40ebd1e9ae remove unused definition 2016-06-02 21:48:43 +02:00
v2ray
72fb5a256c send reuse option in header 2016-06-02 21:34:25 +02:00
v2ray
b9c88b673b unexpected eof 2016-06-02 21:20:58 +02:00
v2ray
ff0cb89efa simplify connection reuse settings 2016-06-02 20:52:52 +02:00
v2ray
9a8bc3fc3c async connection close 2016-06-02 02:40:46 +02:00
v2ray
dfe1ac1f2b Fix connection reuse 2016-06-02 02:20:53 +02:00
v2ray
a86cd36ad2 configuration for connection reuse 2016-06-02 01:49:25 +02:00
v2ray
6ce7b1d532 informational logs 2016-06-02 00:57:08 +02:00
v2ray
9713bf9adf completely fix detour config 2016-06-01 22:45:12 +02:00
v2ray
957eaf38db Assign detour config 2016-06-01 22:33:45 +02:00
v2ray
43f6998d31 discard bad connections 2016-06-01 22:09:34 +02:00
v2ray
4ec96efe84 smarter DNS query 2016-06-01 22:09:24 +02:00
v2ray
2c82f65189 return correct length in ReadFrom 2016-06-01 22:09:12 +02:00
v2ray
202ac9bb56 ReadFrom in BufferedWriter 2016-06-01 21:17:44 +02:00
v2ray
0fac2084c7 reusable connection 2016-05-31 00:21:41 +02:00
v2ray
3a6bf38686 remove validity map 2016-05-31 00:21:29 +02:00
v2ray
21ab26f41f missing function 2016-05-29 22:42:15 +02:00
v2ray
9457c4b349 clean up validity map 2016-05-29 22:42:03 +02:00
v2ray
da24d00367 reusable connection 2016-05-29 22:33:04 +02:00
v2ray
9b07ffd68f cleanup http proxy package 2016-05-29 16:46:31 +02:00
v2ray
b47c1ca609 get rid of annoying firewall warnings 2016-05-29 16:37:52 +02:00
v2ray
c3aa839227 add vscode task 2016-05-29 16:37:40 +02:00
v2ray
06aaaf970b update build tag settings due to plugin change 2016-05-29 16:37:30 +02:00
v2ray
8f4bd95bc3 fix default uuid and port 2016-05-28 14:09:01 +02:00
v2ray
3f9cb1136a allow dynamic type of user accounts 2016-05-28 13:44:11 +02:00
v2ray
87f664048b ready for 1.14 release 2016-05-27 17:45:02 +02:00
v2ray
07c3f2d7cd update help context 2016-05-27 17:44:56 +02:00
v2ray
3c4c14c89c allow local binary 2016-05-27 17:20:31 +02:00
v2ray
46f76e55e5 reorg chan reader 2016-05-25 22:36:52 +02:00
v2ray
e610faaff6 update default server address 2016-05-25 22:23:59 +02:00
v2ray
81cdaa0e4e Unify buffer allocation 2016-05-25 22:05:47 +02:00
v2ray
a2268d2a7b Allow customized target dir in build 2016-05-25 21:51:44 +02:00
v2ray
e9f0eea0d1 force install 2016-05-25 12:32:53 +02:00
v2ray
f48f51c6b4 bug fixes 2016-05-25 12:30:04 +02:00
v2ray
a4296f22df check version in install-release.sh 2016-05-25 12:27:40 +02:00
v2ray
3156c4586c Allow data stream passing through http proxy 2016-05-25 09:32:26 +02:00
v2ray
c75d840706 Remove serial.String 2016-05-24 22:41:51 +02:00
v2ray
444808a51a Remove Intxx interfaces 2016-05-24 22:15:46 +02:00
v2ray
ab39750ceb Remove serial.Bytes 2016-05-24 22:09:22 +02:00
v2ray
fc63f0432c simplify testing 2016-05-24 21:55:46 +02:00
v2ray
3582b9d869 move net/testing/assert into assert 2016-05-24 15:29:08 +02:00
v2ray
bbdc692a93 fix test break 2016-05-23 20:32:08 +02:00
v2ray
50957b9973 Fix build break 2016-05-23 20:25:24 +02:00
v2ray
47b2fafb32 Shorten StringLiteral 2016-05-23 20:23:40 +02:00
v2ray
cfdda19834 Shorten BytesLiteral 2016-05-23 20:21:23 +02:00
v2ray
5b23d25e35 address 2016-05-23 20:16:36 +02:00
v2ray
28cf3aa0c7 uint32 2016-05-23 20:16:31 +02:00
v2ray
ac58eed310 more china sites 2016-05-23 14:19:38 +02:00
v2ray
54cf3f75b1 daemon script 2016-05-23 01:21:03 +02:00
v2ray
0ea2678e72 Allow freedom to consume DNS settings 2016-05-22 22:30:21 +02:00
v2ray
bb503c6954 Static hosts in DNS server 2016-05-22 22:30:08 +02:00
202 changed files with 2674 additions and 1985 deletions

View File

@@ -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
View 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
}
]
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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}))
}

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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()

View File

@@ -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()

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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",

View File

@@ -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 {

View File

@@ -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")
}

View 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
}

View File

@@ -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()
}

View File

@@ -50,6 +50,7 @@ func (p *BufferPool) Free(buffer *Buffer) {
}
const (
SmallBufferSize = 1024 - defaultOffset
BufferSize = 8*1024 - defaultOffset
LargeBufferSize = 64*1024 - defaultOffset
)

View File

@@ -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")
}

View 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)
}

View File

@@ -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))

View File

@@ -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()
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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()

View File

@@ -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)

View File

@@ -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
View 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()
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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,

View File

@@ -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()
}

View File

@@ -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")
}

View File

@@ -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{

View File

@@ -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

View File

@@ -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()

View File

@@ -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()

View File

@@ -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")))

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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.

View File

@@ -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)

View File

@@ -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),

View File

@@ -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"))
}
}

View File

@@ -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"))
}
}

View File

@@ -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)
}
}

View File

@@ -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"))
}
}

View File

@@ -6,5 +6,5 @@ import (
)
func PickPort() v2net.Port {
return v2net.Port(30000 + dice.Roll(5000))
return v2net.Port(30000 + dice.Roll(20000))
}

View File

@@ -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)

View 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
}

View File

@@ -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))]
}

View 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.")
}

View File

@@ -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
}

View 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()
}

View File

@@ -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()
}

View File

@@ -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]

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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})

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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,

View File

@@ -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

View File

@@ -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, ",") + "]"
}

View File

@@ -0,0 +1,9 @@
package serial
import (
"fmt"
)
func PointerToString(value interface{}) string {
return fmt.Sprint(value)
}

View File

@@ -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)
}

View File

@@ -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)))
}

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -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)
}

View File

@@ -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")
}

View File

@@ -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()

View File

@@ -8,7 +8,7 @@ import (
)
var (
version = "1.12"
version = "1.14"
build = "Custom"
codename = "New Order"
intro = "An unified platform for anti-censorship."

View File

@@ -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
})
}

View File

@@ -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
})
}

View File

@@ -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))
}

View File

@@ -1,4 +1,13 @@
package freedom
type DomainStrategy int
const (
DomainStrategyAsIs = DomainStrategy(0)
DomainStrategyUseIP = DomainStrategy(1)
)
type Config struct {
DomainStrategy DomainStrategy
Timeout uint32
}

View File

@@ -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
})
}

View File

@@ -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
})
}

View File

@@ -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)
}

View File

@@ -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
})
}

View File

@@ -1 +1,4 @@
package http
type Client struct {
}

View File

@@ -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 {
}

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -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
})
}

View File

@@ -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() + "/")

View File

@@ -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"

View File

@@ -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)

View File

@@ -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