You've already forked v2ray-core
Compare commits
128 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07dee80f54 | ||
|
|
4419f1e3d6 | ||
|
|
a9d583b92f | ||
|
|
a2651f4dfc | ||
|
|
bcd27ba36f | ||
|
|
a43ee2f1c2 | ||
|
|
f03c86f468 | ||
|
|
9e997a5be7 | ||
|
|
25b30704a4 | ||
|
|
5dc80a2cc5 | ||
|
|
0b2df7f658 | ||
|
|
27f3401848 | ||
|
|
697d44abbd | ||
|
|
c6ad190f0a | ||
|
|
dbf5f948cd | ||
|
|
b01a972076 | ||
|
|
b38137bd13 | ||
|
|
86490e884c | ||
|
|
60a4e3cc61 | ||
|
|
c82b56b760 | ||
|
|
545fa5b53c | ||
|
|
75b8dd9521 | ||
|
|
81855469aa | ||
|
|
4a7400ef2a | ||
|
|
2f0d602833 | ||
|
|
0caf2e6d30 | ||
|
|
6678da2fe2 | ||
|
|
8d6a9167b5 | ||
|
|
c7d6302292 | ||
|
|
6ffc24f895 | ||
|
|
0d73726930 | ||
|
|
a15e33f720 | ||
|
|
00841583d2 | ||
|
|
c42ed62fd5 | ||
|
|
3e4bdc4b0c | ||
|
|
589d20205e | ||
|
|
36107927bf | ||
|
|
1f503ec99f | ||
|
|
283bafdd4a | ||
|
|
e6a61930d9 | ||
|
|
7f277c9bcc | ||
|
|
6fa84624cb | ||
|
|
53cdcfdc2b | ||
|
|
b8421f659f | ||
|
|
7385c05f29 | ||
|
|
42efa5dde0 | ||
|
|
34b4fceaa4 | ||
|
|
1e9f4a1a08 | ||
|
|
5cf36c4520 | ||
|
|
d2233136ba | ||
|
|
7095c5e19b | ||
|
|
db02a2dcc2 | ||
|
|
330abd126c | ||
|
|
b7ff8b8e4c | ||
|
|
443e9d4725 | ||
|
|
c5e5a7265d | ||
|
|
6fe7463bf4 | ||
|
|
0040881b84 | ||
|
|
347ae8fd75 | ||
|
|
ca0ae50e62 | ||
|
|
27a29da7bf | ||
|
|
d1b44619b0 | ||
|
|
b72afc7c35 | ||
|
|
6a620d1c6d | ||
|
|
a509252c24 | ||
|
|
a29a6a586d | ||
|
|
c96533f278 | ||
|
|
f4aa50a160 | ||
|
|
2034d54bab | ||
|
|
2049759640 | ||
|
|
b02bd5b1d8 | ||
|
|
7dfa852677 | ||
|
|
03af6e6856 | ||
|
|
dbee8c620a | ||
|
|
ea39a6fde6 | ||
|
|
0857fdb0f8 | ||
|
|
f871ca63e2 | ||
|
|
723a693c78 | ||
|
|
edd9885d0e | ||
|
|
96ec5cfc58 | ||
|
|
6f5b54747e | ||
|
|
e304e2761d | ||
|
|
128e866021 | ||
|
|
08526a32ff | ||
|
|
3be5045dfc | ||
|
|
84a7a2ad61 | ||
|
|
0cd0033245 | ||
|
|
85d6e1ad13 | ||
|
|
1931820c4c | ||
|
|
7e499ee0b6 | ||
|
|
089de290ad | ||
|
|
a39132f4c2 | ||
|
|
bf2d45a83d | ||
|
|
00a747afe3 | ||
|
|
f072d26fe4 | ||
|
|
187a89484c | ||
|
|
b2cd65d6bc | ||
|
|
acb01e6177 | ||
|
|
45423add40 | ||
|
|
3f396078ab | ||
|
|
4aadaa7513 | ||
|
|
f45fa6e918 | ||
|
|
8d3da0ed8e | ||
|
|
e586f3c127 | ||
|
|
17ea740e7e | ||
|
|
066e5e216d | ||
|
|
72ee6436eb | ||
|
|
d5cd919cec | ||
|
|
f6d4e599bd | ||
|
|
b6a6c154a3 | ||
|
|
33e0cfe233 | ||
|
|
9e8ed30052 | ||
|
|
9523cb3ec3 | ||
|
|
631db6e69a | ||
|
|
ef2d49302d | ||
|
|
ffdce592ec | ||
|
|
906d846c2e | ||
|
|
c32f1a0152 | ||
|
|
991b2703dc | ||
|
|
0acd3b6d62 | ||
|
|
6f998fa18b | ||
|
|
476b3c68d2 | ||
|
|
05ed05b9a0 | ||
|
|
73afe9a001 | ||
|
|
c7b0264f9a | ||
|
|
fc69c77369 | ||
|
|
d602025908 | ||
|
|
c0a1198514 |
17
.github/ISSUE_TEMPLATE
vendored
Normal file
17
.github/ISSUE_TEMPLATE
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
提交 Issue 之前请先阅读 [Issue 指引](https://www.v2ray.com/zh_cn/chapter_01/issue.html),然后回答下面的问题,谢谢。
|
||||
Please answer the following questions before submitting your issue. Thank you.
|
||||
|
||||
1) 你正在使用哪个版本的 V2Ray?(如果服务器和客户端使用了不同版本,请注明)
|
||||
1) What version of V2Ray are you using?
|
||||
|
||||
2) 你的使用场景是什么?比如使用 Chrome 通过 Socks/VMess 代理观看 YouTube 视频。
|
||||
2) What your scenario of using V2Ray? E.g., Watching YouTube videos in Chrome via Socks/VMess proxy.
|
||||
|
||||
3) 你看到的不正常的现象是什么?
|
||||
3) What did you see?
|
||||
|
||||
4) 你期待看到的正确表现是怎样的?
|
||||
4) What do you expected to see instead?
|
||||
|
||||
5) 请附上你的配置文件。
|
||||
5) Please attach your configuration file.
|
||||
14
.travis.yml
14
.travis.yml
@@ -1,13 +1,11 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.6.2
|
||||
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/onsi/gomega
|
||||
- go get github.com/onsi/ginkgo
|
||||
- 1.6.3
|
||||
|
||||
git:
|
||||
depth: 5
|
||||
|
||||
script:
|
||||
- go test -tags json github.com/v2ray/v2ray-core/...
|
||||
|
||||
@@ -16,7 +14,7 @@ after_success:
|
||||
|
||||
before_deploy:
|
||||
- rvm 1.9.3 do gem install mime-types -v 2.6.2
|
||||
- "./release/release.sh"
|
||||
- "./tools/release/release-ci.sh"
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
@@ -31,6 +29,8 @@ deploy:
|
||||
- "$GOPATH/bin/v2ray-linux-arm.zip"
|
||||
- "$GOPATH/bin/v2ray-linux-arm64.zip"
|
||||
- "$GOPATH/bin/v2ray-linux-mips64.zip"
|
||||
- "$GOPATH/bin/v2ray-freebsd-64.zip"
|
||||
- "$GOPATH/bin/v2ray-freebsd-32.zip"
|
||||
- "$GOPATH/bin/metadata.txt"
|
||||
skip_cleanup: true
|
||||
on:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Project V2Ray
|
||||
|
||||
[![Build Status][1]][2] [![codecov.io][3]][4] [![Go Report][5]][6] [![GoDoc][7]][8] [![codebeat][9]][10] [![Downloads][11]][12]
|
||||
[![Build Status][1]][2] [![codecov.io][3]][4] [![Go Report][5]][6] [![GoDoc][7]][8] [![codebeat][9]][10]
|
||||
[1]: https://travis-ci.org/v2ray/v2ray-core.svg?branch=master "Build Status badge"
|
||||
[2]: https://travis-ci.org/v2ray/v2ray-core "Travis-CI Build Status"
|
||||
[3]: https://codecov.io/github/v2ray/v2ray-core/coverage.svg?branch=master "Coverage badge"
|
||||
@@ -11,8 +11,6 @@
|
||||
[8]: https://godoc.org/github.com/v2ray/v2ray-core "GoDoc"
|
||||
[9]: https://codebeat.co/badges/f2354ca8-3e24-463d-a2e3-159af73b2477 "Codebeat badge"
|
||||
[10]: https://codebeat.co/projects/github-com-v2ray-v2ray-core "Codebeat"
|
||||
[11]: https://img.shields.io/github/downloads/v2ray/v2ray-core/total.svg?maxAge=2592000
|
||||
[12]: https://github.com/v2ray/v2ray-core/releases
|
||||
|
||||
V2Ray 是一个模块化的代理软件包,它的目标是提供常用的代理软件模块,简化网络代理软件的开发。
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package dispatcher
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/transport/ray"
|
||||
)
|
||||
|
||||
@@ -12,5 +12,5 @@ const (
|
||||
|
||||
// PacketDispatcher dispatch a packet and possibly further network payload to its destination.
|
||||
type PacketDispatcher interface {
|
||||
DispatchToOutbound(destination v2net.Destination) ray.InboundRay
|
||||
DispatchToOutbound(meta *proxy.InboundHandlerMeta, session *proxy.SessionInfo) ray.InboundRay
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
"github.com/v2ray/v2ray-core/app/proxyman"
|
||||
"github.com/v2ray/v2ray-core/app/router"
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
@@ -42,9 +43,10 @@ func (this *DefaultDispatcher) Release() {
|
||||
|
||||
}
|
||||
|
||||
func (this *DefaultDispatcher) DispatchToOutbound(destination v2net.Destination) ray.InboundRay {
|
||||
func (this *DefaultDispatcher) DispatchToOutbound(meta *proxy.InboundHandlerMeta, session *proxy.SessionInfo) ray.InboundRay {
|
||||
direct := ray.NewRay()
|
||||
dispatcher := this.ohm.GetDefaultHandler()
|
||||
destination := session.Destination
|
||||
|
||||
if this.router != nil {
|
||||
if tag, err := this.router.TakeDetour(destination); err == nil {
|
||||
@@ -59,7 +61,11 @@ func (this *DefaultDispatcher) DispatchToOutbound(destination v2net.Destination)
|
||||
}
|
||||
}
|
||||
|
||||
go this.FilterPacketAndDispatch(destination, direct, dispatcher)
|
||||
if meta.AllowPassiveConnection {
|
||||
go dispatcher.Dispatch(destination, alloc.NewLocalBuffer(32).Clear(), direct)
|
||||
} else {
|
||||
go this.FilterPacketAndDispatch(destination, direct, dispatcher)
|
||||
}
|
||||
|
||||
return direct
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package testing
|
||||
|
||||
import (
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/transport/ray"
|
||||
)
|
||||
|
||||
@@ -29,10 +30,10 @@ func NewTestPacketDispatcher(handler func(destination v2net.Destination, traffic
|
||||
}
|
||||
}
|
||||
|
||||
func (this *TestPacketDispatcher) DispatchToOutbound(destination v2net.Destination) ray.InboundRay {
|
||||
func (this *TestPacketDispatcher) DispatchToOutbound(meta *proxy.InboundHandlerMeta, session *proxy.SessionInfo) ray.InboundRay {
|
||||
traffic := ray.NewRay()
|
||||
this.Destination <- destination
|
||||
go this.Handler(destination, traffic)
|
||||
this.Destination <- session.Destination
|
||||
go this.Handler(session.Destination, traffic)
|
||||
|
||||
return traffic
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func (this *Config) UnmarshalJSON(data []byte) error {
|
||||
if jsonConfig.Hosts != nil {
|
||||
this.Hosts = make(map[string]net.IP)
|
||||
for domain, ip := range jsonConfig.Hosts {
|
||||
if ip.Address.IsDomain() {
|
||||
if ip.Address.Family().IsDomain() {
|
||||
return errors.New(ip.Address.String() + " is not an IP.")
|
||||
}
|
||||
this.Hosts[domain] = ip.Address.IP()
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/transport/internet/udp"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
@@ -49,9 +50,11 @@ type UDPNameServer struct {
|
||||
|
||||
func NewUDPNameServer(address v2net.Destination, dispatcher dispatcher.PacketDispatcher) *UDPNameServer {
|
||||
s := &UDPNameServer{
|
||||
address: address,
|
||||
requests: make(map[uint16]*PendingRequest),
|
||||
udpServer: udp.NewUDPServer(dispatcher),
|
||||
address: address,
|
||||
requests: make(map[uint16]*PendingRequest),
|
||||
udpServer: udp.NewUDPServer(&proxy.InboundHandlerMeta{
|
||||
AllowPassiveConnection: false,
|
||||
}, dispatcher),
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -149,7 +152,7 @@ func (this *UDPNameServer) BuildQueryA(domain string, id uint16) *alloc.Buffer {
|
||||
msg.Id = id
|
||||
msg.RecursionDesired = true
|
||||
msg.Question = []dns.Question{
|
||||
dns.Question{
|
||||
{
|
||||
Name: dns.Fqdn(domain),
|
||||
Qtype: dns.TypeA,
|
||||
Qclass: dns.ClassINET,
|
||||
@@ -162,7 +165,7 @@ func (this *UDPNameServer) BuildQueryA(domain string, id uint16) *alloc.Buffer {
|
||||
}
|
||||
|
||||
func (this *UDPNameServer) DispatchQuery(payload *alloc.Buffer) {
|
||||
this.udpServer.Dispatch(pseudoDestination, this.address, payload, this.HandleResponse)
|
||||
this.udpServer.Dispatch(&proxy.SessionInfo{Source: pseudoDestination, Destination: this.address}, payload, this.HandleResponse)
|
||||
}
|
||||
|
||||
func (this *UDPNameServer) QueryA(domain string) <-chan *ARecord {
|
||||
|
||||
@@ -42,7 +42,7 @@ func NewCacheServer(space app.Space, config *Config) *CacheServer {
|
||||
|
||||
dispatcher := space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher)
|
||||
for idx, ns := range config.NameServers {
|
||||
if ns.Address().IsDomain() && ns.Address().Domain() == "localhost" {
|
||||
if ns.Address().Family().IsDomain() && ns.Address().Domain() == "localhost" {
|
||||
server.servers[idx] = &LocalNameServer{}
|
||||
} else {
|
||||
server.servers[idx] = NewUDPNameServer(ns, dispatcher)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -73,7 +73,7 @@ func NewPlainDomainMatcher(pattern string) *PlainDomainMatcher {
|
||||
}
|
||||
|
||||
func (this *PlainDomainMatcher) Apply(dest v2net.Destination) bool {
|
||||
if !dest.Address().IsDomain() {
|
||||
if !dest.Address().Family().IsDomain() {
|
||||
return false
|
||||
}
|
||||
domain := dest.Address().Domain()
|
||||
@@ -95,7 +95,7 @@ func NewRegexpDomainMatcher(pattern string) (*RegexpDomainMatcher, error) {
|
||||
}
|
||||
|
||||
func (this *RegexpDomainMatcher) Apply(dest v2net.Destination) bool {
|
||||
if !dest.Address().IsDomain() {
|
||||
if !dest.Address().Family().IsDomain() {
|
||||
return false
|
||||
}
|
||||
domain := dest.Address().Domain()
|
||||
@@ -117,7 +117,7 @@ func NewCIDRMatcher(ipnet string) (*CIDRMatcher, error) {
|
||||
}
|
||||
|
||||
func (this *CIDRMatcher) Apply(dest v2net.Destination) bool {
|
||||
if !dest.Address().IsIPv4() && !dest.Address().IsIPv6() {
|
||||
if !dest.Address().Family().Either(v2net.AddressFamilyIPv4, v2net.AddressFamilyIPv6) {
|
||||
return false
|
||||
}
|
||||
return this.cidr.Contains(dest.Address().IP())
|
||||
@@ -134,7 +134,7 @@ func NewIPv4Matcher(ipnet *v2net.IPNet) *IPv4Matcher {
|
||||
}
|
||||
|
||||
func (this *IPv4Matcher) Apply(dest v2net.Destination) bool {
|
||||
if !dest.Address().IsIPv4() {
|
||||
if !dest.Address().Family().Either(v2net.AddressFamilyIPv4) {
|
||||
return false
|
||||
}
|
||||
return this.ipv4net.Contains(dest.Address().IP())
|
||||
|
||||
@@ -64,7 +64,7 @@ func (this *Router) takeDetourWithoutCache(dest v2net.Destination) (string, erro
|
||||
return rule.Tag, nil
|
||||
}
|
||||
}
|
||||
if this.config.DomainStrategy == UseIPIfNonMatch && dest.Address().IsDomain() {
|
||||
if this.config.DomainStrategy == UseIPIfNonMatch && dest.Address().Family().IsDomain() {
|
||||
log.Info("Router: Looking up IP for ", dest)
|
||||
ipDests := this.ResolveIP(dest)
|
||||
if ipDests != nil {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package alloc
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"io"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
@@ -16,17 +17,28 @@ const (
|
||||
// quickly.
|
||||
type Buffer struct {
|
||||
head []byte
|
||||
pool *BufferPool
|
||||
pool Pool
|
||||
Value []byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func CreateBuffer(container []byte, parent Pool) *Buffer {
|
||||
b := new(Buffer)
|
||||
b.head = container
|
||||
b.pool = parent
|
||||
b.Value = b.head[defaultOffset:]
|
||||
b.offset = defaultOffset
|
||||
return b
|
||||
}
|
||||
|
||||
// Release recycles the buffer into an internal buffer pool.
|
||||
func (b *Buffer) Release() {
|
||||
if b == nil {
|
||||
if b == nil || b.head == nil {
|
||||
return
|
||||
}
|
||||
b.pool.Free(b)
|
||||
if b.pool != nil {
|
||||
b.pool.Free(b)
|
||||
}
|
||||
b.head = nil
|
||||
b.Value = nil
|
||||
b.pool = nil
|
||||
@@ -98,6 +110,12 @@ func (b *Buffer) PrependUint32(v uint32) *Buffer {
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Buffer) PrependHash(h hash.Hash) *Buffer {
|
||||
b.SliceBack(h.Size())
|
||||
h.Sum(b.Value[:0])
|
||||
return b
|
||||
}
|
||||
|
||||
// Bytes returns the content bytes of this Buffer.
|
||||
func (b *Buffer) Bytes() []byte {
|
||||
return b.Value
|
||||
@@ -122,7 +140,7 @@ func (b *Buffer) SliceFrom(from int) *Buffer {
|
||||
func (b *Buffer) SliceBack(offset int) *Buffer {
|
||||
newoffset := b.offset - offset
|
||||
if newoffset < 0 {
|
||||
newoffset = 0
|
||||
panic("Negative buffer offset.")
|
||||
}
|
||||
b.Value = b.head[newoffset : b.offset+len(b.Value)]
|
||||
b.offset = newoffset
|
||||
@@ -207,3 +225,7 @@ func NewBufferWithSize(size int) *Buffer {
|
||||
|
||||
return NewLargeBuffer()
|
||||
}
|
||||
|
||||
func NewLocalBuffer(size int) *Buffer {
|
||||
return CreateBuffer(make([]byte, size), nil)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Pool interface {
|
||||
Allocate() *Buffer
|
||||
Free(*Buffer)
|
||||
}
|
||||
|
||||
type BufferPool struct {
|
||||
chain chan []byte
|
||||
allocator *sync.Pool
|
||||
@@ -16,7 +21,7 @@ func NewBufferPool(bufferSize, poolSize int) *BufferPool {
|
||||
New: func() interface{} { return make([]byte, bufferSize) },
|
||||
},
|
||||
}
|
||||
for i := 0; i < poolSize/2; i++ {
|
||||
for i := 0; i < poolSize; i++ {
|
||||
pool.chain <- make([]byte, bufferSize)
|
||||
}
|
||||
return pool
|
||||
@@ -29,12 +34,7 @@ func (p *BufferPool) Allocate() *Buffer {
|
||||
default:
|
||||
b = p.allocator.Get().([]byte)
|
||||
}
|
||||
return &Buffer{
|
||||
head: b,
|
||||
pool: p,
|
||||
Value: b[defaultOffset:],
|
||||
offset: defaultOffset,
|
||||
}
|
||||
return CreateBuffer(b, p)
|
||||
}
|
||||
|
||||
func (p *BufferPool) Free(buffer *Buffer) {
|
||||
@@ -55,6 +55,6 @@ const (
|
||||
LargeBufferSize = 64*1024 - defaultOffset
|
||||
)
|
||||
|
||||
var smallPool = NewBufferPool(1600, 128)
|
||||
var mediumPool = NewBufferPool(8*1024, 128)
|
||||
var largePool = NewBufferPool(64*1024, 64)
|
||||
var smallPool = NewBufferPool(1600, 256)
|
||||
var mediumPool = NewBufferPool(8*1024, 1024)
|
||||
var largePool = NewBufferPool(64*1024, 32)
|
||||
|
||||
@@ -5,11 +5,15 @@ import (
|
||||
"crypto/cipher"
|
||||
)
|
||||
|
||||
// NewAesDecryptionStream creates a new AES encryption stream based on given key and IV.
|
||||
// Caller must ensure the length of key and IV is either 16, 24 or 32 bytes.
|
||||
func NewAesDecryptionStream(key []byte, iv []byte) cipher.Stream {
|
||||
aesBlock, _ := aes.NewCipher(key)
|
||||
return cipher.NewCFBDecrypter(aesBlock, iv)
|
||||
}
|
||||
|
||||
// NewAesEncryptionStream creates a new AES description stream based on given key and IV.
|
||||
// Caller must ensure the length of key and IV is either 16, 24 or 32 bytes.
|
||||
func NewAesEncryptionStream(key []byte, iv []byte) cipher.Stream {
|
||||
aesBlock, _ := aes.NewCipher(key)
|
||||
return cipher.NewCFBEncrypter(aesBlock, iv)
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"github.com/v2ray/v2ray-core/common/crypto/internal"
|
||||
)
|
||||
|
||||
// NewChaCha20Stream creates a new Chacha20 encryption/descryption stream based on give key and IV.
|
||||
// Caller must ensure the length of key is 32 bytes, and length of IV is either 8 or 12 bytes.
|
||||
func NewChaCha20Stream(key []byte, iv []byte) cipher.Stream {
|
||||
return internal.NewChaCha20Stream(key, iv, 20)
|
||||
}
|
||||
|
||||
@@ -45,14 +45,12 @@ func (this *ChanReader) Read(b []byte) (int, error) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
}
|
||||
nBytes := copy(b, this.current.Value)
|
||||
if nBytes == this.current.Len() {
|
||||
nBytes, err := this.current.Read(b)
|
||||
if this.current.IsEmpty() {
|
||||
this.current.Release()
|
||||
this.current = nil
|
||||
} else {
|
||||
this.current.SliceFrom(nBytes)
|
||||
}
|
||||
return nBytes, nil
|
||||
return nBytes, err
|
||||
}
|
||||
|
||||
func (this *ChanReader) Release() {
|
||||
|
||||
@@ -7,17 +7,6 @@ import (
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
)
|
||||
|
||||
// ReadFrom reads from a reader and put all content to a buffer.
|
||||
// If buffer is nil, ReadFrom creates a new normal buffer.
|
||||
func ReadFrom(reader io.Reader, buffer *alloc.Buffer) (*alloc.Buffer, error) {
|
||||
if buffer == nil {
|
||||
buffer = alloc.NewBuffer()
|
||||
}
|
||||
nBytes, err := reader.Read(buffer.Value)
|
||||
buffer.Slice(0, nBytes)
|
||||
return buffer, err
|
||||
}
|
||||
|
||||
// Reader extends io.Reader with alloc.Buffer.
|
||||
type Reader interface {
|
||||
common.Releasable
|
||||
@@ -42,7 +31,12 @@ func NewAdaptiveReader(reader io.Reader) *AdaptiveReader {
|
||||
|
||||
// Read implements Reader.Read().
|
||||
func (this *AdaptiveReader) Read() (*alloc.Buffer, error) {
|
||||
buffer, err := ReadFrom(this.reader, this.allocate())
|
||||
buffer := this.allocate().Clear()
|
||||
_, err := buffer.FillFrom(this.reader)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if buffer.Len() >= alloc.BufferSize {
|
||||
this.allocate = alloc.NewLargeBuffer
|
||||
@@ -50,10 +44,6 @@ func (this *AdaptiveReader) Read() (*alloc.Buffer, error) {
|
||||
this.allocate = alloc.NewBuffer
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return nil, err
|
||||
}
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ func Pipe(reader Reader, writer Writer) error {
|
||||
err = writer.Write(buffer)
|
||||
if err != nil {
|
||||
log.Debug("IO: Pipe exits as ", err)
|
||||
buffer.Release()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,28 +34,32 @@ func (this *JSONConfigLoader) LoadWithID(raw []byte, id string) (interface{}, er
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (this *JSONConfigLoader) Load(raw []byte) (interface{}, error) {
|
||||
func (this *JSONConfigLoader) Load(raw []byte) (interface{}, string, error) {
|
||||
var obj map[string]json.RawMessage
|
||||
if err := json.Unmarshal(raw, &obj); err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
rawID, found := obj[this.idKey]
|
||||
if !found {
|
||||
log.Error(this.idKey, " not found in JSON content.")
|
||||
return nil, ErrConfigIDKeyNotFound
|
||||
return nil, "", ErrConfigIDKeyNotFound
|
||||
}
|
||||
var id string
|
||||
if err := json.Unmarshal(rawID, &id); err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
rawConfig := json.RawMessage(raw)
|
||||
if len(this.configKey) > 0 {
|
||||
configValue, found := obj[this.configKey]
|
||||
if !found {
|
||||
log.Error(this.configKey, " not found in JSON content.")
|
||||
return nil, ErrConfigIDKeyNotFound
|
||||
return nil, "", ErrConfigIDKeyNotFound
|
||||
}
|
||||
rawConfig = configValue
|
||||
}
|
||||
return this.LoadWithID([]byte(rawConfig), id)
|
||||
config, err := this.LoadWithID([]byte(rawConfig), id)
|
||||
if err != nil {
|
||||
return nil, id, err
|
||||
}
|
||||
return config, id, nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type ConfigCreator func() interface{}
|
||||
type ConfigLoader interface {
|
||||
RegisterCreator(string, ConfigCreator) error
|
||||
CreateConfig(string) (interface{}, error)
|
||||
Load([]byte) (interface{}, error)
|
||||
Load([]byte) (interface{}, string, error)
|
||||
LoadWithID([]byte, string) (interface{}, error)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common"
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
)
|
||||
|
||||
@@ -46,15 +46,12 @@ func (this *ErrorLog) Release() {
|
||||
}
|
||||
|
||||
func (this *ErrorLog) String() string {
|
||||
b := alloc.NewSmallBuffer().Clear()
|
||||
defer b.Release()
|
||||
|
||||
b.AppendString(this.Prefix)
|
||||
|
||||
for _, value := range this.Values {
|
||||
b.AppendString(InterfaceToString(value))
|
||||
values := make([]string, len(this.Values)+1)
|
||||
values[0] = this.Prefix
|
||||
for i, value := range this.Values {
|
||||
values[i+1] = InterfaceToString(value)
|
||||
}
|
||||
return b.String()
|
||||
return strings.Join(values, "")
|
||||
}
|
||||
|
||||
type AccessLog struct {
|
||||
@@ -71,12 +68,5 @@ func (this *AccessLog) Release() {
|
||||
}
|
||||
|
||||
func (this *AccessLog) String() string {
|
||||
b := alloc.NewSmallBuffer().Clear()
|
||||
defer b.Release()
|
||||
|
||||
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()
|
||||
return strings.Join([]string{InterfaceToString(this.From), this.Status, InterfaceToString(this.To), InterfaceToString(this.Reason)}, " ")
|
||||
}
|
||||
|
||||
@@ -12,15 +12,41 @@ var (
|
||||
AnyIP = IPAddress([]byte{0, 0, 0, 0})
|
||||
)
|
||||
|
||||
type AddressFamily int
|
||||
|
||||
const (
|
||||
AddressFamilyIPv4 = AddressFamily(0)
|
||||
AddressFamilyIPv6 = AddressFamily(1)
|
||||
AddressFamilyDomain = AddressFamily(2)
|
||||
)
|
||||
|
||||
func (this AddressFamily) Either(fs ...AddressFamily) bool {
|
||||
for _, f := range fs {
|
||||
if this == f {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (this AddressFamily) IsIPv4() bool {
|
||||
return this == AddressFamilyIPv4
|
||||
}
|
||||
|
||||
func (this AddressFamily) IsIPv6() bool {
|
||||
return this == AddressFamilyIPv6
|
||||
}
|
||||
|
||||
func (this AddressFamily) IsDomain() bool {
|
||||
return this == AddressFamilyDomain
|
||||
}
|
||||
|
||||
// Address represents a network address to be communicated with. It may be an IP address or domain
|
||||
// address, not both. This interface doesn't resolve IP address for a given domain.
|
||||
type Address interface {
|
||||
IP() net.IP // IP of this Address
|
||||
Domain() string // Domain of this Address
|
||||
|
||||
IsIPv4() bool // True if this Address is an IPv4 address
|
||||
IsIPv6() bool // True if this Address is an IPv6 address
|
||||
IsDomain() bool // True if this Address is an domain address
|
||||
Family() AddressFamily
|
||||
|
||||
String() string // String representation of this Address
|
||||
Equals(Address) bool
|
||||
@@ -75,16 +101,8 @@ func (addr *ipv4Address) Domain() string {
|
||||
panic("Calling Domain() on an IPv4Address.")
|
||||
}
|
||||
|
||||
func (addr *ipv4Address) IsIPv4() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (addr *ipv4Address) IsIPv6() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (addr *ipv4Address) IsDomain() bool {
|
||||
return false
|
||||
func (addr *ipv4Address) Family() AddressFamily {
|
||||
return AddressFamilyIPv4
|
||||
}
|
||||
|
||||
func (this *ipv4Address) String() string {
|
||||
@@ -112,16 +130,8 @@ func (addr *ipv6Address) Domain() string {
|
||||
panic("Calling Domain() on an IPv6Address.")
|
||||
}
|
||||
|
||||
func (addr *ipv6Address) IsIPv4() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (addr *ipv6Address) IsIPv6() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (addr *ipv6Address) IsDomain() bool {
|
||||
return false
|
||||
func (this *ipv6Address) Family() AddressFamily {
|
||||
return AddressFamilyIPv6
|
||||
}
|
||||
|
||||
func (this *ipv6Address) String() string {
|
||||
@@ -133,12 +143,22 @@ func (this *ipv6Address) Equals(another Address) bool {
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
for idx, v := range *this {
|
||||
if anotherIPv6[idx] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
return this[0] == anotherIPv6[0] &&
|
||||
this[1] == anotherIPv6[1] &&
|
||||
this[2] == anotherIPv6[2] &&
|
||||
this[3] == anotherIPv6[3] &&
|
||||
this[4] == anotherIPv6[4] &&
|
||||
this[5] == anotherIPv6[5] &&
|
||||
this[6] == anotherIPv6[6] &&
|
||||
this[7] == anotherIPv6[7] &&
|
||||
this[8] == anotherIPv6[8] &&
|
||||
this[9] == anotherIPv6[9] &&
|
||||
this[10] == anotherIPv6[10] &&
|
||||
this[11] == anotherIPv6[11] &&
|
||||
this[12] == anotherIPv6[12] &&
|
||||
this[13] == anotherIPv6[13] &&
|
||||
this[14] == anotherIPv6[14] &&
|
||||
this[15] == anotherIPv6[15]
|
||||
}
|
||||
|
||||
type domainAddress string
|
||||
@@ -151,16 +171,8 @@ func (addr *domainAddress) Domain() string {
|
||||
return string(*addr)
|
||||
}
|
||||
|
||||
func (addr *domainAddress) IsIPv4() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (addr *domainAddress) IsIPv6() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (addr *domainAddress) IsDomain() bool {
|
||||
return true
|
||||
func (addr *domainAddress) Family() AddressFamily {
|
||||
return AddressFamilyDomain
|
||||
}
|
||||
|
||||
func (this *domainAddress) String() string {
|
||||
|
||||
@@ -18,8 +18,8 @@ func TestIPParsing(t *testing.T) {
|
||||
var address AddressJson
|
||||
err := json.Unmarshal([]byte(rawJson), &address)
|
||||
assert.Error(err).IsNil()
|
||||
assert.Bool(address.Address.IsIPv4()).IsTrue()
|
||||
assert.Bool(address.Address.IsDomain()).IsFalse()
|
||||
assert.Bool(address.Address.Family().Either(AddressFamilyIPv4)).IsTrue()
|
||||
assert.Bool(address.Address.Family().Either(AddressFamilyDomain)).IsFalse()
|
||||
assert.Bool(address.Address.IP().Equal(net.ParseIP("8.8.8.8"))).IsTrue()
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ func TestDomainParsing(t *testing.T) {
|
||||
var address AddressJson
|
||||
err := json.Unmarshal([]byte(rawJson), &address)
|
||||
assert.Error(err).IsNil()
|
||||
assert.Bool(address.Address.IsIPv4()).IsFalse()
|
||||
assert.Bool(address.Address.IsDomain()).IsTrue()
|
||||
assert.Bool(address.Address.Family().Either(AddressFamilyIPv4)).IsFalse()
|
||||
assert.Bool(address.Address.Family().Either(AddressFamilyDomain)).IsTrue()
|
||||
assert.String(address.Address.Domain()).Equals("v2ray.com")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Destination represents a network destination including address and protocol (tcp / udp).
|
||||
type Destination interface {
|
||||
Network() Network // Protocol of communication (tcp / udp)
|
||||
@@ -13,11 +17,19 @@ type Destination interface {
|
||||
IsUDP() bool // True if destination is reachable via UDP
|
||||
}
|
||||
|
||||
func TCPDestinationFromAddr(addr *net.TCPAddr) Destination {
|
||||
return TCPDestination(IPAddress(addr.IP), Port(addr.Port))
|
||||
}
|
||||
|
||||
// TCPDestination creates a TCP destination with given address
|
||||
func TCPDestination(address Address, port Port) Destination {
|
||||
return &tcpDestination{address: address, port: port}
|
||||
}
|
||||
|
||||
func UDPDestinationFromAddr(addr *net.UDPAddr) Destination {
|
||||
return UDPDestination(IPAddress(addr.IP), Port(addr.Port))
|
||||
}
|
||||
|
||||
// UDPDestination creates a UDP destination with given address
|
||||
func UDPDestination(address Address, port Port) Destination {
|
||||
return &udpDestination{address: address, port: port}
|
||||
|
||||
@@ -1,20 +1,5 @@
|
||||
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))]
|
||||
Equals(Account) bool
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
// +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.")
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type RequestEncoder interface {
|
||||
EncodeRequestHeader(*RequestHeader, io.Writer)
|
||||
EncodeRequestBody(io.Writer) io.Writer
|
||||
}
|
||||
|
||||
type RequestDecoder interface {
|
||||
DecodeRequestHeader(io.Reader) (*RequestHeader, error)
|
||||
DecodeRequestBody(io.Reader) io.Reader
|
||||
}
|
||||
|
||||
type ResponseEncoder interface {
|
||||
EncodeResponseHeader(*ResponseHeader, io.Writer)
|
||||
EncodeResponseBody(io.Writer) io.Writer
|
||||
}
|
||||
|
||||
type ResponseDecoder interface {
|
||||
DecodeResponseHeader(io.Reader) (*ResponseHeader, error)
|
||||
DecodeResponseBody(io.Reader) io.Reader
|
||||
}
|
||||
90
common/protocol/server_picker.go
Normal file
90
common/protocol/server_picker.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ServerList struct {
|
||||
sync.RWMutex
|
||||
servers []*ServerSpec
|
||||
}
|
||||
|
||||
func NewServerList() *ServerList {
|
||||
return &ServerList{}
|
||||
}
|
||||
|
||||
func (this *ServerList) AddServer(server *ServerSpec) {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
this.servers = append(this.servers, server)
|
||||
}
|
||||
|
||||
func (this *ServerList) Size() uint32 {
|
||||
this.RLock()
|
||||
defer this.RUnlock()
|
||||
|
||||
return uint32(len(this.servers))
|
||||
}
|
||||
|
||||
func (this *ServerList) GetServer(idx uint32) *ServerSpec {
|
||||
this.RLock()
|
||||
defer this.RUnlock()
|
||||
|
||||
for {
|
||||
if idx >= uint32(len(this.servers)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
server := this.servers[idx]
|
||||
if !server.IsValid() {
|
||||
this.RemoveServer(idx)
|
||||
continue
|
||||
}
|
||||
|
||||
return server
|
||||
}
|
||||
}
|
||||
|
||||
// @Private
|
||||
func (this *ServerList) RemoveServer(idx uint32) {
|
||||
n := len(this.servers)
|
||||
this.servers[idx] = this.servers[n-1]
|
||||
this.servers = this.servers[:n-1]
|
||||
}
|
||||
|
||||
type ServerPicker interface {
|
||||
PickServer() *ServerSpec
|
||||
}
|
||||
|
||||
type RoundRobinServerPicker struct {
|
||||
sync.Mutex
|
||||
serverlist *ServerList
|
||||
nextIndex uint32
|
||||
}
|
||||
|
||||
func NewRoundRobinServerPicker(serverlist *ServerList) *RoundRobinServerPicker {
|
||||
return &RoundRobinServerPicker{
|
||||
serverlist: serverlist,
|
||||
nextIndex: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *RoundRobinServerPicker) PickServer() *ServerSpec {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
next := this.nextIndex
|
||||
server := this.serverlist.GetServer(next)
|
||||
if server == nil {
|
||||
next = 0
|
||||
server = this.serverlist.GetServer(0)
|
||||
}
|
||||
next++
|
||||
if next >= this.serverlist.Size() {
|
||||
next = 0
|
||||
}
|
||||
this.nextIndex = next
|
||||
|
||||
return server
|
||||
}
|
||||
54
common/protocol/server_picker_test.go
Normal file
54
common/protocol/server_picker_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package protocol_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
. "github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestServerList(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
list := NewServerList()
|
||||
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(1)), AlwaysValid()))
|
||||
assert.Uint32(list.Size()).Equals(1)
|
||||
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(2)), BeforeTime(time.Now().Add(time.Second))))
|
||||
assert.Uint32(list.Size()).Equals(2)
|
||||
|
||||
server := list.GetServer(1)
|
||||
assert.Port(server.Destination().Port()).Equals(2)
|
||||
time.Sleep(2 * time.Second)
|
||||
server = list.GetServer(1)
|
||||
assert.Pointer(server).IsNil()
|
||||
|
||||
server = list.GetServer(0)
|
||||
assert.Port(server.Destination().Port()).Equals(1)
|
||||
}
|
||||
|
||||
func TestServerPicker(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
list := NewServerList()
|
||||
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(1)), AlwaysValid()))
|
||||
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(2)), BeforeTime(time.Now().Add(time.Second))))
|
||||
list.AddServer(NewServerSpec(v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(3)), BeforeTime(time.Now().Add(time.Second))))
|
||||
|
||||
picker := NewRoundRobinServerPicker(list)
|
||||
server := picker.PickServer()
|
||||
assert.Port(server.Destination().Port()).Equals(1)
|
||||
server = picker.PickServer()
|
||||
assert.Port(server.Destination().Port()).Equals(2)
|
||||
server = picker.PickServer()
|
||||
assert.Port(server.Destination().Port()).Equals(3)
|
||||
server = picker.PickServer()
|
||||
assert.Port(server.Destination().Port()).Equals(1)
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
server = picker.PickServer()
|
||||
assert.Port(server.Destination().Port()).Equals(1)
|
||||
server = picker.PickServer()
|
||||
assert.Port(server.Destination().Port()).Equals(1)
|
||||
}
|
||||
100
common/protocol/server_spec.go
Normal file
100
common/protocol/server_spec.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
type ValidationStrategy interface {
|
||||
IsValid() bool
|
||||
Invalidate()
|
||||
}
|
||||
|
||||
type AlwaysValidStrategy struct{}
|
||||
|
||||
func AlwaysValid() ValidationStrategy {
|
||||
return AlwaysValidStrategy{}
|
||||
}
|
||||
|
||||
func (this AlwaysValidStrategy) IsValid() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (this AlwaysValidStrategy) Invalidate() {}
|
||||
|
||||
type TimeoutValidStrategy struct {
|
||||
until time.Time
|
||||
}
|
||||
|
||||
func BeforeTime(t time.Time) ValidationStrategy {
|
||||
return &TimeoutValidStrategy{
|
||||
until: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *TimeoutValidStrategy) IsValid() bool {
|
||||
return this.until.After(time.Now())
|
||||
}
|
||||
|
||||
func (this *TimeoutValidStrategy) Invalidate() {
|
||||
this.until = time.Time{}
|
||||
}
|
||||
|
||||
type ServerSpec struct {
|
||||
sync.RWMutex
|
||||
dest v2net.Destination
|
||||
users []*User
|
||||
valid ValidationStrategy
|
||||
}
|
||||
|
||||
func NewServerSpec(dest v2net.Destination, valid ValidationStrategy, users ...*User) *ServerSpec {
|
||||
return &ServerSpec{
|
||||
dest: dest,
|
||||
users: users,
|
||||
valid: valid,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ServerSpec) Destination() v2net.Destination {
|
||||
return this.dest
|
||||
}
|
||||
|
||||
func (this *ServerSpec) HasUser(user *User) bool {
|
||||
this.RLock()
|
||||
defer this.RUnlock()
|
||||
|
||||
account := user.Account
|
||||
for _, u := range this.users {
|
||||
if u.Account.Equals(account) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *ServerSpec) AddUser(user *User) {
|
||||
if this.HasUser(user) {
|
||||
return
|
||||
}
|
||||
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
this.users = append(this.users, user)
|
||||
}
|
||||
|
||||
func (this *ServerSpec) PickUser() *User {
|
||||
userCount := len(this.users)
|
||||
return this.users[dice.Roll(userCount)]
|
||||
}
|
||||
|
||||
func (this *ServerSpec) IsValid() bool {
|
||||
return this.valid.IsValid()
|
||||
}
|
||||
|
||||
func (this *ServerSpec) Invalidate() {
|
||||
this.valid.Invalidate()
|
||||
}
|
||||
62
common/protocol/server_spec_test.go
Normal file
62
common/protocol/server_spec_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package protocol_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
. "github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
type TestAccount struct {
|
||||
id int
|
||||
}
|
||||
|
||||
func (this *TestAccount) Equals(account Account) bool {
|
||||
return account.(*TestAccount).id == this.id
|
||||
}
|
||||
|
||||
func TestServerSpecUser(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
account := &TestAccount{
|
||||
id: 0,
|
||||
}
|
||||
user := NewUser(UserLevel(0), "")
|
||||
user.Account = account
|
||||
rec := NewServerSpec(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80), AlwaysValid(), user)
|
||||
assert.Bool(rec.HasUser(user)).IsTrue()
|
||||
|
||||
account2 := &TestAccount{
|
||||
id: 1,
|
||||
}
|
||||
user2 := NewUser(UserLevel(0), "")
|
||||
user2.Account = account2
|
||||
assert.Bool(rec.HasUser(user2)).IsFalse()
|
||||
|
||||
rec.AddUser(user2)
|
||||
assert.Bool(rec.HasUser(user2)).IsTrue()
|
||||
}
|
||||
|
||||
func TestAlwaysValidStrategy(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
strategy := AlwaysValid()
|
||||
assert.Bool(strategy.IsValid()).IsTrue()
|
||||
strategy.Invalidate()
|
||||
assert.Bool(strategy.IsValid()).IsTrue()
|
||||
}
|
||||
|
||||
func TestTimeoutValidStrategy(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
strategy := BeforeTime(time.Now().Add(2 * time.Second))
|
||||
assert.Bool(strategy.IsValid()).IsTrue()
|
||||
time.Sleep(3 * time.Second)
|
||||
assert.Bool(strategy.IsValid()).IsFalse()
|
||||
|
||||
strategy = BeforeTime(time.Now().Add(2 * time.Second))
|
||||
strategy.Invalidate()
|
||||
assert.Bool(strategy.IsValid()).IsFalse()
|
||||
}
|
||||
@@ -13,11 +13,10 @@ type User struct {
|
||||
Email string
|
||||
}
|
||||
|
||||
func NewUser(account Account, level UserLevel, email string) *User {
|
||||
func NewUser(level UserLevel, email string) *User {
|
||||
return &User{
|
||||
Account: account,
|
||||
Level: level,
|
||||
Email: email,
|
||||
Level: level,
|
||||
Email: email,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,16 +14,8 @@ func (u *User) UnmarshalJSON(data []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var rawAccount AccountJson
|
||||
if err := json.Unmarshal(data, &rawAccount); err != nil {
|
||||
return err
|
||||
}
|
||||
account, err := rawAccount.GetAccount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*u = *NewUser(account, UserLevel(rawUserValue.LevelByte), rawUserValue.EmailString)
|
||||
u.Email = rawUserValue.EmailString
|
||||
u.Level = UserLevel(rawUserValue.LevelByte)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -22,24 +22,13 @@ func TestUserParsing(t *testing.T) {
|
||||
}`), user)
|
||||
assert.Error(err).IsNil()
|
||||
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")
|
||||
assert.String(user.Email).Equals("love@v2ray.com")
|
||||
}
|
||||
|
||||
func TestInvalidUserJson(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
user := new(User)
|
||||
err := json.Unmarshal([]byte(`{"id": 1234}`), user)
|
||||
assert.Error(err).IsNotNil()
|
||||
}
|
||||
|
||||
func TestInvalidIdJson(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
user := new(User)
|
||||
err := json.Unmarshal([]byte(`{"id": "1234"}`), user)
|
||||
err := json.Unmarshal([]byte(`{"email": 1234}`), user)
|
||||
assert.Error(err).IsNotNil()
|
||||
}
|
||||
|
||||
@@ -1,163 +1,12 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common"
|
||||
"github.com/v2ray/v2ray-core/common/signal"
|
||||
)
|
||||
|
||||
const (
|
||||
updateIntervalSec = 10
|
||||
cacheDurationSec = 120
|
||||
)
|
||||
|
||||
type idEntry struct {
|
||||
id *ID
|
||||
userIdx int
|
||||
lastSec Timestamp
|
||||
lastSecRemoval Timestamp
|
||||
}
|
||||
|
||||
type UserValidator interface {
|
||||
common.Releasable
|
||||
|
||||
Add(user *User) error
|
||||
Get(timeHash []byte) (*User, Timestamp, bool)
|
||||
}
|
||||
|
||||
type TimedUserValidator struct {
|
||||
sync.RWMutex
|
||||
running bool
|
||||
validUsers []*User
|
||||
userHash map[[16]byte]*indexTimePair
|
||||
ids []*idEntry
|
||||
hasher IDHash
|
||||
cancel *signal.CancelSignal
|
||||
}
|
||||
|
||||
type indexTimePair struct {
|
||||
index int
|
||||
timeSec Timestamp
|
||||
}
|
||||
|
||||
func NewTimedUserValidator(hasher IDHash) UserValidator {
|
||||
tus := &TimedUserValidator{
|
||||
validUsers: make([]*User, 0, 16),
|
||||
userHash: make(map[[16]byte]*indexTimePair, 512),
|
||||
ids: make([]*idEntry, 0, 512),
|
||||
hasher: hasher,
|
||||
running: true,
|
||||
cancel: signal.NewCloseSignal(),
|
||||
}
|
||||
go tus.updateUserHash(updateIntervalSec * time.Second)
|
||||
return tus
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) Release() {
|
||||
if !this.running {
|
||||
return
|
||||
}
|
||||
|
||||
this.cancel.Cancel()
|
||||
<-this.cancel.WaitForDone()
|
||||
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
if !this.running {
|
||||
return
|
||||
}
|
||||
|
||||
this.running = false
|
||||
this.validUsers = nil
|
||||
this.userHash = nil
|
||||
this.ids = nil
|
||||
this.hasher = nil
|
||||
this.cancel = nil
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) generateNewHashes(nowSec Timestamp, idx int, entry *idEntry) {
|
||||
var hashValue [16]byte
|
||||
var hashValueRemoval [16]byte
|
||||
idHash := this.hasher(entry.id.Bytes())
|
||||
for entry.lastSec <= nowSec {
|
||||
idHash.Write(entry.lastSec.Bytes(nil))
|
||||
idHash.Sum(hashValue[:0])
|
||||
idHash.Reset()
|
||||
|
||||
idHash.Write(entry.lastSecRemoval.Bytes(nil))
|
||||
idHash.Sum(hashValueRemoval[:0])
|
||||
idHash.Reset()
|
||||
|
||||
this.Lock()
|
||||
this.userHash[hashValue] = &indexTimePair{idx, entry.lastSec}
|
||||
delete(this.userHash, hashValueRemoval)
|
||||
this.Unlock()
|
||||
|
||||
entry.lastSec++
|
||||
entry.lastSecRemoval++
|
||||
}
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) updateUserHash(interval time.Duration) {
|
||||
L:
|
||||
for {
|
||||
select {
|
||||
case now := <-time.After(interval):
|
||||
nowSec := Timestamp(now.Unix() + cacheDurationSec)
|
||||
for _, entry := range this.ids {
|
||||
this.generateNewHashes(nowSec, entry.userIdx, entry)
|
||||
}
|
||||
case <-this.cancel.WaitForCancel():
|
||||
break L
|
||||
}
|
||||
}
|
||||
this.cancel.Done()
|
||||
}
|
||||
|
||||
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: 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 account.AlterIDs {
|
||||
entry := &idEntry{
|
||||
id: alterid,
|
||||
userIdx: idx,
|
||||
lastSec: Timestamp(nowSec - cacheDurationSec),
|
||||
lastSecRemoval: Timestamp(nowSec - cacheDurationSec*3),
|
||||
}
|
||||
this.generateNewHashes(Timestamp(nowSec+cacheDurationSec), idx, entry)
|
||||
this.ids = append(this.ids, entry)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) Get(userHash []byte) (*User, Timestamp, bool) {
|
||||
defer this.RUnlock()
|
||||
this.RLock()
|
||||
|
||||
if !this.running {
|
||||
return nil, 0, false
|
||||
}
|
||||
var fixedSizeHash [16]byte
|
||||
copy(fixedSizeHash[:], userHash)
|
||||
pair, found := this.userHash[fixedSizeHash]
|
||||
if found {
|
||||
return this.validUsers[pair.index], pair.timeSec, true
|
||||
}
|
||||
return nil, 0, false
|
||||
}
|
||||
|
||||
2
core.go
2
core.go
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "1.20"
|
||||
version = "1.24"
|
||||
build = "Custom"
|
||||
codename = "New Order"
|
||||
intro = "An unified platform for anti-censorship."
|
||||
|
||||
@@ -31,5 +31,5 @@ Content-Length: 0
|
||||
)
|
||||
|
||||
func (this *HTTPResponse) WriteTo(writer v2io.Writer) {
|
||||
writer.Write(alloc.NewSmallBuffer().Clear().AppendString(http403response))
|
||||
writer.Write(alloc.NewLocalBuffer(512).Clear().AppendString(http403response))
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func (this *Config) UnmarshalJSON(data []byte) error {
|
||||
loader := loader.NewJSONConfigLoader("type", "")
|
||||
loader.RegisterCreator("none", func() interface{} { return new(NoneResponse) })
|
||||
loader.RegisterCreator("http", func() interface{} { return new(HTTPResponse) })
|
||||
response, err := loader.Load(jsonConfig.Response)
|
||||
response, _, err := loader.Load(jsonConfig.Response)
|
||||
if err != nil {
|
||||
return errors.New("Blackhole: Failed to parse response config: " + err.Error())
|
||||
}
|
||||
|
||||
27
proxy/blackhole/config_json_test.go
Normal file
27
proxy/blackhole/config_json_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// +build json
|
||||
|
||||
package blackhole_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
. "github.com/v2ray/v2ray-core/proxy/blackhole"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestHTTPResponseJSON(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
rawJson := `{
|
||||
"response": {
|
||||
"type": "http"
|
||||
}
|
||||
}`
|
||||
config := new(Config)
|
||||
err := json.Unmarshal([]byte(rawJson), config)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
_, ok := config.Response.(*HTTPResponse)
|
||||
assert.Bool(ok).IsTrue()
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package dokodemo
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
@@ -89,7 +90,7 @@ func (this *DokodemoDoor) Start() error {
|
||||
}
|
||||
|
||||
func (this *DokodemoDoor) ListenUDP() error {
|
||||
this.udpServer = udp.NewUDPServer(this.packetDispatcher)
|
||||
this.udpServer = udp.NewUDPServer(this.meta, this.packetDispatcher)
|
||||
udpHub, err := udp.ListenUDP(this.meta.Address, this.meta.Port, this.handleUDPPackets)
|
||||
if err != nil {
|
||||
log.Error("Dokodemo failed to listen on ", this.meta.Address, ":", this.meta.Port, ": ", err)
|
||||
@@ -102,7 +103,8 @@ func (this *DokodemoDoor) ListenUDP() error {
|
||||
}
|
||||
|
||||
func (this *DokodemoDoor) handleUDPPackets(payload *alloc.Buffer, dest v2net.Destination) {
|
||||
this.udpServer.Dispatch(dest, v2net.UDPDestination(this.address, this.port), payload, this.handleUDPResponse)
|
||||
this.udpServer.Dispatch(
|
||||
&proxy.SessionInfo{Source: dest, Destination: v2net.UDPDestination(this.address, this.port)}, payload, this.handleUDPResponse)
|
||||
}
|
||||
|
||||
func (this *DokodemoDoor) handleUDPResponse(dest v2net.Destination, payload *alloc.Buffer) {
|
||||
@@ -148,7 +150,10 @@ func (this *DokodemoDoor) HandleTCPConnection(conn internet.Connection) {
|
||||
}
|
||||
log.Info("Dokodemo: Handling request to ", dest)
|
||||
|
||||
ray := this.packetDispatcher.DispatchToOutbound(dest)
|
||||
ray := this.packetDispatcher.DispatchToOutbound(this.meta, &proxy.SessionInfo{
|
||||
Source: v2net.TCPDestinationFromAddr(conn.RemoteAddr().(*net.TCPAddr)),
|
||||
Destination: dest,
|
||||
})
|
||||
defer ray.InboundOutput().Release()
|
||||
|
||||
var inputFinish, outputFinish sync.Mutex
|
||||
|
||||
@@ -47,7 +47,7 @@ func NewFreedomConnection(config *Config, space app.Space, meta *proxy.OutboundH
|
||||
|
||||
// @Private
|
||||
func (this *FreedomConnection) ResolveIP(destination v2net.Destination) v2net.Destination {
|
||||
if !destination.Address().IsDomain() {
|
||||
if !destination.Address().Family().IsDomain() {
|
||||
return destination
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload *
|
||||
defer ray.OutboundOutput().Close()
|
||||
|
||||
var conn internet.Connection
|
||||
if this.domainStrategy == DomainStrategyUseIP && destination.Address().IsDomain() {
|
||||
if this.domainStrategy == DomainStrategyUseIP && destination.Address().Family().IsDomain() {
|
||||
destination = this.ResolveIP(destination)
|
||||
}
|
||||
err := retry.Timed(5, 100).On(func() error {
|
||||
|
||||
@@ -52,7 +52,7 @@ func TestSinglePacket(t *testing.T) {
|
||||
|
||||
traffic := ray.NewRay()
|
||||
data2Send := "Data to be sent to remote"
|
||||
payload := alloc.NewSmallBuffer().Clear().Append([]byte(data2Send))
|
||||
payload := alloc.NewLocalBuffer(2048).Clear().Append([]byte(data2Send))
|
||||
|
||||
go freedom.Dispatch(v2net.TCPDestination(v2net.LocalHostIP, port), payload, traffic)
|
||||
traffic.InboundInput().Close()
|
||||
@@ -78,7 +78,7 @@ func TestUnreachableDestination(t *testing.T) {
|
||||
})
|
||||
traffic := ray.NewRay()
|
||||
data2Send := "Data to be sent to remote"
|
||||
payload := alloc.NewSmallBuffer().Clear().Append([]byte(data2Send))
|
||||
payload := alloc.NewLocalBuffer(2048).Clear().Append([]byte(data2Send))
|
||||
|
||||
err := freedom.Dispatch(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 128), payload, traffic)
|
||||
assert.Error(err).IsNotNil()
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
"github.com/v2ray/v2ray-core/app/dispatcher"
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
v2io "github.com/v2ray/v2ray-core/common/io"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
@@ -96,7 +95,7 @@ func parseHost(rawHost string, defaultPort v2net.Port) (v2net.Destination, error
|
||||
func (this *Server) handleConnection(conn internet.Connection) {
|
||||
defer conn.Close()
|
||||
timedReader := v2net.NewTimeOutReader(this.config.Timeout, conn)
|
||||
reader := bufio.NewReader(timedReader)
|
||||
reader := bufio.NewReaderSize(timedReader, 2048)
|
||||
|
||||
request, err := http.ReadRequest(reader)
|
||||
if err != nil {
|
||||
@@ -120,14 +119,18 @@ func (this *Server) handleConnection(conn internet.Connection) {
|
||||
return
|
||||
}
|
||||
log.Access(conn.RemoteAddr(), request.URL, log.AccessAccepted, "")
|
||||
session := &proxy.SessionInfo{
|
||||
Source: v2net.TCPDestinationFromAddr(conn.RemoteAddr().(*net.TCPAddr)),
|
||||
Destination: dest,
|
||||
}
|
||||
if strings.ToUpper(request.Method) == "CONNECT" {
|
||||
this.handleConnect(request, dest, reader, conn)
|
||||
this.handleConnect(request, session, reader, conn)
|
||||
} else {
|
||||
this.handlePlainHTTP(request, dest, reader, conn)
|
||||
this.handlePlainHTTP(request, session, reader, conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Server) handleConnect(request *http.Request, destination v2net.Destination, reader io.Reader, writer io.Writer) {
|
||||
func (this *Server) handleConnect(request *http.Request, session *proxy.SessionInfo, reader io.Reader, writer io.Writer) {
|
||||
response := &http.Response{
|
||||
Status: "200 OK",
|
||||
StatusCode: 200,
|
||||
@@ -139,13 +142,9 @@ func (this *Server) handleConnect(request *http.Request, destination v2net.Desti
|
||||
ContentLength: 0,
|
||||
Close: false,
|
||||
}
|
||||
response.Write(writer)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
response.Write(buffer)
|
||||
writer.Write(buffer.Value)
|
||||
buffer.Release()
|
||||
|
||||
ray := this.packetDispatcher.DispatchToOutbound(destination)
|
||||
ray := this.packetDispatcher.DispatchToOutbound(this.meta, session)
|
||||
this.transport(reader, writer, ray)
|
||||
}
|
||||
|
||||
@@ -214,21 +213,18 @@ func (this *Server) GenerateResponse(statusCode int, status string) *http.Respon
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Server) handlePlainHTTP(request *http.Request, dest v2net.Destination, reader *bufio.Reader, writer io.Writer) {
|
||||
func (this *Server) handlePlainHTTP(request *http.Request, session *proxy.SessionInfo, reader *bufio.Reader, writer io.Writer) {
|
||||
if len(request.URL.Host) <= 0 {
|
||||
response := this.GenerateResponse(400, "Bad Request")
|
||||
response.Write(writer)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
response.Write(buffer)
|
||||
writer.Write(buffer.Value)
|
||||
buffer.Release()
|
||||
return
|
||||
}
|
||||
|
||||
request.Host = request.URL.Host
|
||||
StripHopByHopHeaders(request)
|
||||
|
||||
ray := this.packetDispatcher.DispatchToOutbound(dest)
|
||||
ray := this.packetDispatcher.DispatchToOutbound(this.meta, session)
|
||||
defer ray.InboundInput().Close()
|
||||
defer ray.InboundOutput().Release()
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ package proxy // import "github.com/v2ray/v2ray-core/proxy"
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/transport/internet"
|
||||
"github.com/v2ray/v2ray-core/transport/ray"
|
||||
)
|
||||
@@ -15,11 +16,18 @@ const (
|
||||
HandlerStateRunning = HandlerState(1)
|
||||
)
|
||||
|
||||
type SessionInfo struct {
|
||||
Source v2net.Destination
|
||||
Destination v2net.Destination
|
||||
User *protocol.User
|
||||
}
|
||||
|
||||
type InboundHandlerMeta struct {
|
||||
Tag string
|
||||
Address v2net.Address
|
||||
Port v2net.Port
|
||||
StreamSettings *internet.StreamSettings
|
||||
Tag string
|
||||
Address v2net.Address
|
||||
Port v2net.Port
|
||||
AllowPassiveConnection bool
|
||||
StreamSettings *internet.StreamSettings
|
||||
}
|
||||
|
||||
type OutboundHandlerMeta struct {
|
||||
|
||||
@@ -96,7 +96,7 @@ func (this *ChunkReader) Read() (*alloc.Buffer, error) {
|
||||
log.Debug("AuthenticationReader: Unexpected auth: ", authBytes)
|
||||
return nil, transport.ErrCorruptedPacket
|
||||
}
|
||||
buffer.Value = payload
|
||||
buffer.SliceFrom(AuthSize)
|
||||
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
@@ -18,4 +18,7 @@ func TestNormalChunkReading(t *testing.T) {
|
||||
payload, err := reader.Read()
|
||||
assert.Error(err).IsNil()
|
||||
assert.Bytes(payload.Value).Equals([]byte{11, 12, 13, 14, 15, 16, 17, 18})
|
||||
|
||||
payload.PrependBytes(3, 4)
|
||||
assert.Bytes(payload.Value).Equals([]byte{3, 4, 11, 12, 13, 14, 15, 16, 17, 18})
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
func TestNormalRequestParsing(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear()
|
||||
buffer.AppendBytes(1, 127, 0, 0, 1, 0, 80)
|
||||
|
||||
request, err := ReadRequest(buffer, nil, false)
|
||||
@@ -28,7 +28,7 @@ func TestNormalRequestParsing(t *testing.T) {
|
||||
func TestEmptyPayload(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear()
|
||||
_, err := ReadRequest(buffer, nil, false)
|
||||
assert.Error(err).Equals(io.EOF)
|
||||
}
|
||||
@@ -36,7 +36,7 @@ func TestEmptyPayload(t *testing.T) {
|
||||
func TestSingleBytePayload(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear().AppendBytes(1)
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear().AppendBytes(1)
|
||||
_, err := ReadRequest(buffer, nil, false)
|
||||
assert.Error(err).Equals(transport.ErrCorruptedPacket)
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func TestSingleBytePayload(t *testing.T) {
|
||||
func TestWrongAddressType(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear().AppendBytes(5)
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear().AppendBytes(5)
|
||||
_, err := ReadRequest(buffer, nil, false)
|
||||
assert.Error(err).Equals(transport.ErrCorruptedPacket)
|
||||
}
|
||||
@@ -52,15 +52,15 @@ func TestWrongAddressType(t *testing.T) {
|
||||
func TestInsufficientAddressRequest(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear().AppendBytes(1, 1)
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear().AppendBytes(1, 1)
|
||||
_, err := ReadRequest(buffer, nil, false)
|
||||
assert.Error(err).Equals(transport.ErrCorruptedPacket)
|
||||
|
||||
buffer = alloc.NewSmallBuffer().Clear().AppendBytes(4, 1)
|
||||
buffer = alloc.NewLocalBuffer(2048).Clear().AppendBytes(4, 1)
|
||||
_, err = ReadRequest(buffer, nil, false)
|
||||
assert.Error(err).Equals(transport.ErrCorruptedPacket)
|
||||
|
||||
buffer = alloc.NewSmallBuffer().Clear().AppendBytes(3, 255, 1)
|
||||
buffer = alloc.NewLocalBuffer(2048).Clear().AppendBytes(3, 255, 1)
|
||||
_, err = ReadRequest(buffer, nil, false)
|
||||
assert.Error(err).Equals(transport.ErrCorruptedPacket)
|
||||
}
|
||||
@@ -68,7 +68,7 @@ func TestInsufficientAddressRequest(t *testing.T) {
|
||||
func TestInsufficientPortRequest(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear().AppendBytes(1, 1, 2, 3, 4, 5)
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear().AppendBytes(1, 1, 2, 3, 4, 5)
|
||||
_, err := ReadRequest(buffer, nil, false)
|
||||
assert.Error(err).Equals(transport.ErrCorruptedPacket)
|
||||
}
|
||||
@@ -76,7 +76,7 @@ func TestInsufficientPortRequest(t *testing.T) {
|
||||
func TestOTARequest(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear()
|
||||
buffer.AppendBytes(0x13, 13, 119, 119, 119, 46, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 0, 239, 115, 52, 212, 178, 172, 26, 6, 168, 0)
|
||||
|
||||
auth := NewAuthenticator(HeaderKeyGenerator(
|
||||
@@ -91,7 +91,7 @@ func TestOTARequest(t *testing.T) {
|
||||
func TestInvalidOTARequest(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear()
|
||||
buffer.AppendBytes(0x13, 13, 119, 119, 119, 46, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 0, 239, 115, 52, 212, 178, 172, 26, 6, 168, 1)
|
||||
|
||||
auth := NewAuthenticator(HeaderKeyGenerator(
|
||||
@@ -104,7 +104,7 @@ func TestInvalidOTARequest(t *testing.T) {
|
||||
func TestUDPRequestParsing(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear()
|
||||
buffer.AppendBytes(1, 127, 0, 0, 1, 0, 80, 1, 2, 3, 4, 5, 6)
|
||||
|
||||
request, err := ReadRequest(buffer, nil, true)
|
||||
@@ -118,7 +118,7 @@ func TestUDPRequestParsing(t *testing.T) {
|
||||
func TestUDPRequestWithOTA(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear()
|
||||
buffer.AppendBytes(
|
||||
0x13, 13, 119, 119, 119, 46, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 0,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
|
||||
|
||||
@@ -4,6 +4,7 @@ package shadowsocks
|
||||
import (
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
@@ -70,7 +71,7 @@ func (this *Server) Start() error {
|
||||
this.tcpHub = tcpHub
|
||||
|
||||
if this.config.UDP {
|
||||
this.udpServer = udp.NewUDPServer(this.packetDispatcher)
|
||||
this.udpServer = udp.NewUDPServer(this.meta, this.packetDispatcher)
|
||||
udpHub, err := udp.ListenUDP(this.meta.Address, this.meta.Port, this.handlerUDPPayload)
|
||||
if err != nil {
|
||||
log.Error("Shadowsocks: Failed to listen UDP on ", this.meta.Address, ":", this.meta.Port, ": ", err)
|
||||
@@ -114,7 +115,7 @@ func (this *Server) handlerUDPPayload(payload *alloc.Buffer, source v2net.Destin
|
||||
log.Access(source, dest, log.AccessAccepted, "")
|
||||
log.Info("Shadowsocks: Tunnelling request to ", dest)
|
||||
|
||||
this.udpServer.Dispatch(source, dest, request.DetachUDPPayload(), func(destination v2net.Destination, payload *alloc.Buffer) {
|
||||
this.udpServer.Dispatch(&proxy.SessionInfo{Source: source, Destination: dest}, request.DetachUDPPayload(), func(destination v2net.Destination, payload *alloc.Buffer) {
|
||||
defer payload.Release()
|
||||
|
||||
response := alloc.NewBuffer().Slice(0, ivLen)
|
||||
@@ -131,14 +132,14 @@ func (this *Server) handlerUDPPayload(payload *alloc.Buffer, source v2net.Destin
|
||||
|
||||
writer := crypto.NewCryptionWriter(stream, response)
|
||||
|
||||
switch {
|
||||
case request.Address.IsIPv4():
|
||||
switch request.Address.Family() {
|
||||
case v2net.AddressFamilyIPv4:
|
||||
writer.Write([]byte{AddrTypeIPv4})
|
||||
writer.Write(request.Address.IP())
|
||||
case request.Address.IsIPv6():
|
||||
case v2net.AddressFamilyIPv6:
|
||||
writer.Write([]byte{AddrTypeIPv6})
|
||||
writer.Write(request.Address.IP())
|
||||
case request.Address.IsDomain():
|
||||
case v2net.AddressFamilyDomain:
|
||||
writer.Write([]byte{AddrTypeDomain, byte(len(request.Address.Domain()))})
|
||||
writer.Write([]byte(request.Address.Domain()))
|
||||
}
|
||||
@@ -204,7 +205,10 @@ func (this *Server) handleConnection(conn internet.Connection) {
|
||||
log.Access(conn.RemoteAddr(), dest, log.AccessAccepted, "")
|
||||
log.Info("Shadowsocks: Tunnelling request to ", dest)
|
||||
|
||||
ray := this.packetDispatcher.DispatchToOutbound(dest)
|
||||
ray := this.packetDispatcher.DispatchToOutbound(this.meta, &proxy.SessionInfo{
|
||||
Source: v2net.TCPDestinationFromAddr(conn.RemoteAddr().(*net.TCPAddr)),
|
||||
Destination: dest,
|
||||
})
|
||||
defer ray.InboundOutput().Release()
|
||||
|
||||
var writeFinish sync.Mutex
|
||||
|
||||
18
proxy/socks/account.go
Normal file
18
proxy/socks/account.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package socks
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
Username string `json:"user"`
|
||||
Password string `json:"pass"`
|
||||
}
|
||||
|
||||
func (this *Account) Equals(another protocol.Account) bool {
|
||||
socksAccount, ok := another.(*Account)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return this.Username == socksAccount.Username
|
||||
}
|
||||
9
proxy/socks/client_config.go
Normal file
9
proxy/socks/client_config.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package socks
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
)
|
||||
|
||||
type ClientConfig struct {
|
||||
Servers []*protocol.ServerSpec
|
||||
}
|
||||
49
proxy/socks/client_config_json.go
Normal file
49
proxy/socks/client_config_json.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// +build json
|
||||
|
||||
package socks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/proxy/internal"
|
||||
)
|
||||
|
||||
func (this *ClientConfig) UnmarshalJSON(data []byte) error {
|
||||
type ServerConfig struct {
|
||||
Address *v2net.AddressJson `json:"address"`
|
||||
Port v2net.Port `json:"port"`
|
||||
Users []json.RawMessage `json:"users"`
|
||||
}
|
||||
type JsonConfig struct {
|
||||
Servers []*ServerConfig `json:"servers"`
|
||||
}
|
||||
jsonConfig := new(JsonConfig)
|
||||
if err := json.Unmarshal(data, jsonConfig); err != nil {
|
||||
return errors.New("Socks|Client: Failed to parse config: " + err.Error())
|
||||
}
|
||||
this.Servers = make([]*protocol.ServerSpec, len(jsonConfig.Servers))
|
||||
for idx, serverConfig := range jsonConfig.Servers {
|
||||
server := protocol.NewServerSpec(v2net.TCPDestination(serverConfig.Address.Address, serverConfig.Port), protocol.AlwaysValid())
|
||||
for _, rawUser := range serverConfig.Users {
|
||||
user := new(protocol.User)
|
||||
if err := json.Unmarshal(rawUser, user); err != nil {
|
||||
return errors.New("Socks|Client: Failed to parse user: " + err.Error())
|
||||
}
|
||||
account := new(Account)
|
||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
||||
return errors.New("Socks|Client: Failed to parse socks account: " + err.Error())
|
||||
}
|
||||
user.Account = account
|
||||
server.AddUser(user)
|
||||
}
|
||||
this.Servers[idx] = server
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
internal.RegisterOutboundConfig("socks", func() interface{} { return new(ClientConfig) })
|
||||
}
|
||||
@@ -40,10 +40,9 @@ func (request *Socks5AuthenticationRequest) HasAuthMethod(method byte) bool {
|
||||
}
|
||||
|
||||
func ReadAuthentication(reader io.Reader) (auth Socks5AuthenticationRequest, auth4 Socks4AuthenticationRequest, err error) {
|
||||
buffer := alloc.NewSmallBuffer()
|
||||
defer buffer.Release()
|
||||
buffer := make([]byte, 256)
|
||||
|
||||
nBytes, err := reader.Read(buffer.Value)
|
||||
nBytes, err := reader.Read(buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -53,23 +52,23 @@ func ReadAuthentication(reader io.Reader) (auth Socks5AuthenticationRequest, aut
|
||||
return
|
||||
}
|
||||
|
||||
if buffer.Value[0] == socks4Version {
|
||||
auth4.Version = buffer.Value[0]
|
||||
auth4.Command = buffer.Value[1]
|
||||
auth4.Port = v2net.PortFromBytes(buffer.Value[2:4])
|
||||
copy(auth4.IP[:], buffer.Value[4:8])
|
||||
if buffer[0] == socks4Version {
|
||||
auth4.Version = buffer[0]
|
||||
auth4.Command = buffer[1]
|
||||
auth4.Port = v2net.PortFromBytes(buffer[2:4])
|
||||
copy(auth4.IP[:], buffer[4:8])
|
||||
err = Socks4Downgrade
|
||||
return
|
||||
}
|
||||
|
||||
auth.version = buffer.Value[0]
|
||||
auth.version = buffer[0]
|
||||
if auth.version != socksVersion {
|
||||
log.Warning("Socks: Unknown protocol version ", auth.version)
|
||||
err = proxy.ErrInvalidProtocolVersion
|
||||
return
|
||||
}
|
||||
|
||||
auth.nMethods = buffer.Value[1]
|
||||
auth.nMethods = buffer[1]
|
||||
if auth.nMethods <= 0 {
|
||||
log.Warning("Socks: Zero length of authentication methods")
|
||||
err = proxy.ErrInvalidAuthentication
|
||||
@@ -81,7 +80,7 @@ func ReadAuthentication(reader io.Reader) (auth Socks5AuthenticationRequest, aut
|
||||
err = proxy.ErrInvalidAuthentication
|
||||
return
|
||||
}
|
||||
copy(auth.authMethods[:], buffer.Value[2:nBytes])
|
||||
copy(auth.authMethods[:], buffer[2:nBytes])
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestSocks4AuthenticationResponseToBytes(t *testing.T) {
|
||||
|
||||
response := NewSocks4AuthenticationResponse(byte(0x10), 443, []byte{1, 2, 3, 4})
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear()
|
||||
defer buffer.Release()
|
||||
|
||||
response.Write(buffer)
|
||||
|
||||
@@ -84,7 +84,7 @@ func TestResponseWrite(t *testing.T) {
|
||||
[16]byte{},
|
||||
v2net.Port(53),
|
||||
}
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear()
|
||||
defer buffer.Release()
|
||||
|
||||
response.Write(buffer)
|
||||
@@ -105,7 +105,7 @@ func TestSetIPv6(t *testing.T) {
|
||||
response := NewSocks5Response()
|
||||
response.SetIPv6([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear()
|
||||
defer buffer.Release()
|
||||
response.Write(buffer)
|
||||
assert.Bytes(buffer.Value).Equals([]byte{
|
||||
@@ -118,7 +118,7 @@ func TestSetDomain(t *testing.T) {
|
||||
response := NewSocks5Response()
|
||||
response.SetDomain("v2ray.com")
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer := alloc.NewLocalBuffer(2048).Clear()
|
||||
defer buffer.Release()
|
||||
response.Write(buffer)
|
||||
assert.Bytes(buffer.Value).Equals([]byte{
|
||||
|
||||
@@ -26,12 +26,12 @@ func (request *Socks5UDPRequest) Destination() v2net.Destination {
|
||||
|
||||
func (request *Socks5UDPRequest) Write(buffer *alloc.Buffer) {
|
||||
buffer.AppendBytes(0, 0, request.Fragment)
|
||||
switch {
|
||||
case request.Address.IsIPv4():
|
||||
switch request.Address.Family() {
|
||||
case v2net.AddressFamilyIPv4:
|
||||
buffer.AppendBytes(AddrTypeIPv4).Append(request.Address.IP())
|
||||
case request.Address.IsIPv6():
|
||||
case v2net.AddressFamilyIPv6:
|
||||
buffer.AppendBytes(AddrTypeIPv6).Append(request.Address.IP())
|
||||
case request.Address.IsDomain():
|
||||
case v2net.AddressFamilyDomain:
|
||||
buffer.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain()))).Append([]byte(request.Address.Domain()))
|
||||
}
|
||||
buffer.AppendUint16(request.Port.Value())
|
||||
|
||||
@@ -3,6 +3,7 @@ package socks
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -19,8 +20,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorUnsupportedSocksCommand = errors.New("Unsupported socks command.")
|
||||
ErrorUnsupportedAuthMethod = errors.New("Unsupported auth method.")
|
||||
ErrUnsupportedSocksCommand = errors.New("Unsupported socks command.")
|
||||
ErrUnsupportedAuthMethod = errors.New("Unsupported auth method.")
|
||||
)
|
||||
|
||||
// Server is a SOCKS 5 proxy server
|
||||
@@ -38,12 +39,20 @@ type Server struct {
|
||||
}
|
||||
|
||||
// NewServer creates a new Server object.
|
||||
func NewServer(config *Config, packetDispatcher dispatcher.PacketDispatcher, meta *proxy.InboundHandlerMeta) *Server {
|
||||
return &Server{
|
||||
config: config,
|
||||
packetDispatcher: packetDispatcher,
|
||||
meta: meta,
|
||||
func NewServer(config *Config, space app.Space, meta *proxy.InboundHandlerMeta) *Server {
|
||||
s := &Server{
|
||||
config: config,
|
||||
meta: meta,
|
||||
}
|
||||
space.InitializeApplication(func() error {
|
||||
if !space.HasApp(dispatcher.APP_ID) {
|
||||
log.Error("Socks|Server: Dispatcher is not found in the space.")
|
||||
return app.ErrMissingApplication
|
||||
}
|
||||
s.packetDispatcher = space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher)
|
||||
return nil
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
// Port implements InboundHandler.Port().
|
||||
@@ -111,7 +120,7 @@ func (this *Server) handleConnection(connection internet.Connection) {
|
||||
return
|
||||
}
|
||||
|
||||
clientAddr := connection.RemoteAddr().String()
|
||||
clientAddr := v2net.TCPDestinationFromAddr(connection.RemoteAddr().(*net.TCPAddr))
|
||||
if err != nil && err == protocol.Socks4Downgrade {
|
||||
this.handleSocks4(clientAddr, reader, writer, auth4)
|
||||
} else {
|
||||
@@ -119,7 +128,7 @@ func (this *Server) handleConnection(connection internet.Connection) {
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Server) handleSocks5(clientAddr string, reader *v2io.BufferedReader, writer *v2io.BufferedWriter, auth protocol.Socks5AuthenticationRequest) error {
|
||||
func (this *Server) handleSocks5(clientAddr v2net.Destination, reader *v2io.BufferedReader, writer *v2io.BufferedWriter, auth protocol.Socks5AuthenticationRequest) error {
|
||||
expectedAuthMethod := protocol.AuthNotRequired
|
||||
if this.config.AuthType == AuthTypePassword {
|
||||
expectedAuthMethod = protocol.AuthUserPass
|
||||
@@ -134,7 +143,7 @@ func (this *Server) handleSocks5(clientAddr string, reader *v2io.BufferedReader,
|
||||
return err
|
||||
}
|
||||
log.Warning("Socks: client doesn't support any allowed auth methods.")
|
||||
return ErrorUnsupportedAuthMethod
|
||||
return ErrUnsupportedAuthMethod
|
||||
}
|
||||
|
||||
authResponse := protocol.NewAuthenticationResponse(expectedAuthMethod)
|
||||
@@ -191,7 +200,7 @@ func (this *Server) handleSocks5(clientAddr string, reader *v2io.BufferedReader,
|
||||
return err
|
||||
}
|
||||
log.Warning("Socks: Unsupported socks command ", request.Command)
|
||||
return ErrorUnsupportedSocksCommand
|
||||
return ErrUnsupportedSocksCommand
|
||||
}
|
||||
|
||||
response := protocol.NewSocks5Response()
|
||||
@@ -211,10 +220,14 @@ func (this *Server) handleSocks5(clientAddr string, reader *v2io.BufferedReader,
|
||||
writer.SetCached(false)
|
||||
|
||||
dest := request.Destination()
|
||||
session := &proxy.SessionInfo{
|
||||
Source: clientAddr,
|
||||
Destination: dest,
|
||||
}
|
||||
log.Info("Socks: TCP Connect request to ", dest)
|
||||
log.Access(clientAddr, dest, log.AccessAccepted, "")
|
||||
|
||||
this.transport(reader, writer, dest)
|
||||
this.transport(reader, writer, session)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -225,12 +238,12 @@ func (this *Server) handleUDP(reader io.Reader, writer *v2io.BufferedWriter) err
|
||||
udpAddr := this.udpAddress
|
||||
|
||||
response.Port = udpAddr.Port()
|
||||
switch {
|
||||
case udpAddr.Address().IsIPv4():
|
||||
switch udpAddr.Address().Family() {
|
||||
case v2net.AddressFamilyIPv4:
|
||||
response.SetIPv4(udpAddr.Address().IP())
|
||||
case udpAddr.Address().IsIPv6():
|
||||
case v2net.AddressFamilyIPv6:
|
||||
response.SetIPv6(udpAddr.Address().IP())
|
||||
case udpAddr.Address().IsDomain():
|
||||
case v2net.AddressFamilyDomain:
|
||||
response.SetDomain(udpAddr.Address().Domain())
|
||||
}
|
||||
|
||||
@@ -250,7 +263,7 @@ func (this *Server) handleUDP(reader io.Reader, writer *v2io.BufferedWriter) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Server) handleSocks4(clientAddr string, reader *v2io.BufferedReader, writer *v2io.BufferedWriter, auth protocol.Socks4AuthenticationRequest) error {
|
||||
func (this *Server) handleSocks4(clientAddr v2net.Destination, reader *v2io.BufferedReader, writer *v2io.BufferedWriter, auth protocol.Socks4AuthenticationRequest) error {
|
||||
result := protocol.Socks4RequestGranted
|
||||
if auth.Command == protocol.CmdBind {
|
||||
result = protocol.Socks4RequestRejected
|
||||
@@ -261,21 +274,25 @@ func (this *Server) handleSocks4(clientAddr string, reader *v2io.BufferedReader,
|
||||
|
||||
if result == protocol.Socks4RequestRejected {
|
||||
log.Warning("Socks: Unsupported socks 4 command ", auth.Command)
|
||||
log.Access(clientAddr, "", log.AccessRejected, ErrorUnsupportedSocksCommand)
|
||||
return ErrorUnsupportedSocksCommand
|
||||
log.Access(clientAddr, "", log.AccessRejected, ErrUnsupportedSocksCommand)
|
||||
return ErrUnsupportedSocksCommand
|
||||
}
|
||||
|
||||
reader.SetCached(false)
|
||||
writer.SetCached(false)
|
||||
|
||||
dest := v2net.TCPDestination(v2net.IPAddress(auth.IP[:]), auth.Port)
|
||||
session := &proxy.SessionInfo{
|
||||
Source: clientAddr,
|
||||
Destination: dest,
|
||||
}
|
||||
log.Access(clientAddr, dest, log.AccessAccepted, "")
|
||||
this.transport(reader, writer, dest)
|
||||
this.transport(reader, writer, session)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Server) transport(reader io.Reader, writer io.Writer, destination v2net.Destination) {
|
||||
ray := this.packetDispatcher.DispatchToOutbound(destination)
|
||||
func (this *Server) transport(reader io.Reader, writer io.Writer, session *proxy.SessionInfo) {
|
||||
ray := this.packetDispatcher.DispatchToOutbound(this.meta, session)
|
||||
input := ray.InboundInput()
|
||||
output := ray.InboundOutput()
|
||||
|
||||
@@ -310,13 +327,7 @@ func (this *ServerFactory) StreamCapability() internet.StreamConnectionType {
|
||||
}
|
||||
|
||||
func (this *ServerFactory) Create(space app.Space, rawConfig interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {
|
||||
if !space.HasApp(dispatcher.APP_ID) {
|
||||
return nil, internal.ErrBadConfiguration
|
||||
}
|
||||
return NewServer(
|
||||
rawConfig.(*Config),
|
||||
space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher),
|
||||
meta), nil
|
||||
return NewServer(rawConfig.(*Config), space, meta), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -17,14 +17,9 @@ const (
|
||||
)
|
||||
|
||||
func (this *Config) UnmarshalJSON(data []byte) error {
|
||||
type SocksAccount struct {
|
||||
Username string `json:"user"`
|
||||
Password string `json:"pass"`
|
||||
}
|
||||
|
||||
type SocksConfig struct {
|
||||
AuthMethod string `json:"auth"`
|
||||
Accounts []*SocksAccount `json:"accounts"`
|
||||
Accounts []*Account `json:"accounts"`
|
||||
UDP bool `json:"udp"`
|
||||
Host *v2net.AddressJson `json:"ip"`
|
||||
Timeout int `json:"timeout"`
|
||||
@@ -4,12 +4,13 @@ import (
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/proxy/socks/protocol"
|
||||
"github.com/v2ray/v2ray-core/transport/internet/udp"
|
||||
)
|
||||
|
||||
func (this *Server) listenUDP() error {
|
||||
this.udpServer = udp.NewUDPServer(this.packetDispatcher)
|
||||
this.udpServer = udp.NewUDPServer(this.meta, this.packetDispatcher)
|
||||
udpHub, err := udp.ListenUDP(this.meta.Address, this.meta.Port, this.handleUDPPayload)
|
||||
if err != nil {
|
||||
log.Error("Socks: Failed to listen on udp ", this.meta.Address, ":", this.meta.Port)
|
||||
@@ -44,7 +45,7 @@ func (this *Server) handleUDPPayload(payload *alloc.Buffer, source v2net.Destina
|
||||
|
||||
log.Info("Socks: Send packet to ", request.Destination(), " with ", request.Data.Len(), " bytes")
|
||||
log.Access(source, request.Destination, log.AccessAccepted, "")
|
||||
this.udpServer.Dispatch(source, request.Destination(), request.Data, func(destination v2net.Destination, payload *alloc.Buffer) {
|
||||
this.udpServer.Dispatch(&proxy.SessionInfo{Source: source, Destination: request.Destination()}, request.Data, func(destination v2net.Destination, payload *alloc.Buffer) {
|
||||
response := &protocol.Socks5UDPRequest{
|
||||
Fragment: 0,
|
||||
Address: request.Destination().Address(),
|
||||
@@ -53,7 +54,7 @@ func (this *Server) handleUDPPayload(payload *alloc.Buffer, source v2net.Destina
|
||||
}
|
||||
log.Info("Socks: Writing back UDP response with ", payload.Len(), " bytes to ", destination)
|
||||
|
||||
udpMessage := alloc.NewSmallBuffer().Clear()
|
||||
udpMessage := alloc.NewLocalBuffer(2048).Clear()
|
||||
response.Write(udpMessage)
|
||||
|
||||
this.udpMutex.RLock()
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/v2ray/v2ray-core/app/dispatcher"
|
||||
v2io "github.com/v2ray/v2ray-core/common/io"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
)
|
||||
|
||||
type InboundConnectionHandler struct {
|
||||
@@ -30,7 +31,12 @@ func (this *InboundConnectionHandler) Close() {
|
||||
}
|
||||
|
||||
func (this *InboundConnectionHandler) Communicate(destination v2net.Destination) error {
|
||||
ray := this.PacketDispatcher.DispatchToOutbound(destination)
|
||||
ray := this.PacketDispatcher.DispatchToOutbound(&proxy.InboundHandlerMeta{
|
||||
AllowPassiveConnection: false,
|
||||
}, &proxy.SessionInfo{
|
||||
Source: v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(0)),
|
||||
Destination: destination,
|
||||
})
|
||||
|
||||
input := ray.InboundInput()
|
||||
output := ray.InboundOutput()
|
||||
|
||||
31
proxy/vmess/account_json.go
Normal file
31
proxy/vmess/account_json.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// +build json
|
||||
|
||||
package vmess
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
)
|
||||
|
||||
func (u *Account) UnmarshalJSON(data []byte) error {
|
||||
type JsonConfig struct {
|
||||
ID string `json:"id"`
|
||||
AlterIds uint16 `json:"alterId"`
|
||||
}
|
||||
var rawConfig JsonConfig
|
||||
if err := json.Unmarshal(data, &rawConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := uuid.ParseString(rawConfig.ID)
|
||||
if err != nil {
|
||||
log.Error("VMess: Failed to parse ID: ", err)
|
||||
return err
|
||||
}
|
||||
u.ID = protocol.NewID(id)
|
||||
u.AlterIDs = protocol.NewAlterIDs(u.ID, rawConfig.AlterIds)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package raw
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"hash/fnv"
|
||||
@@ -1,4 +1,4 @@
|
||||
package raw
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
@@ -6,19 +6,20 @@ import (
|
||||
"hash/fnv"
|
||||
"io"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
"github.com/v2ray/v2ray-core/common/crypto"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess"
|
||||
"github.com/v2ray/v2ray-core/transport"
|
||||
)
|
||||
|
||||
func hashTimestamp(t protocol.Timestamp) []byte {
|
||||
bytes := make([]byte, 0, 32)
|
||||
t.Bytes(bytes)
|
||||
t.Bytes(bytes)
|
||||
t.Bytes(bytes)
|
||||
t.Bytes(bytes)
|
||||
bytes = t.Bytes(bytes)
|
||||
bytes = t.Bytes(bytes)
|
||||
bytes = t.Bytes(bytes)
|
||||
bytes = t.Bytes(bytes)
|
||||
return bytes
|
||||
}
|
||||
|
||||
@@ -51,45 +52,41 @@ 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.Account.(*protocol.VMessAccount).AnyValidID().Bytes())
|
||||
idHash := this.idHash(header.User.Account.(*vmess.Account).AnyValidID().Bytes())
|
||||
idHash.Write(timestamp.Bytes(nil))
|
||||
writer.Write(idHash.Sum(nil))
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
defer buffer.Release()
|
||||
buffer := make([]byte, 0, 512)
|
||||
buffer = append(buffer, Version)
|
||||
buffer = append(buffer, this.requestBodyIV...)
|
||||
buffer = append(buffer, this.requestBodyKey...)
|
||||
buffer = append(buffer, this.responseHeader, byte(header.Option), byte(0), byte(0), byte(header.Command))
|
||||
buffer = header.Port.Bytes(buffer)
|
||||
|
||||
buffer.AppendBytes(Version)
|
||||
buffer.Append(this.requestBodyIV)
|
||||
buffer.Append(this.requestBodyKey)
|
||||
buffer.AppendBytes(this.responseHeader, byte(header.Option), byte(0), byte(0))
|
||||
buffer.AppendBytes(byte(header.Command))
|
||||
buffer.AppendUint16(header.Port.Value())
|
||||
|
||||
switch {
|
||||
case header.Address.IsIPv4():
|
||||
buffer.AppendBytes(AddrTypeIPv4)
|
||||
buffer.Append(header.Address.IP())
|
||||
case header.Address.IsIPv6():
|
||||
buffer.AppendBytes(AddrTypeIPv6)
|
||||
buffer.Append(header.Address.IP())
|
||||
case header.Address.IsDomain():
|
||||
buffer.AppendBytes(AddrTypeDomain, byte(len(header.Address.Domain())))
|
||||
buffer.Append([]byte(header.Address.Domain()))
|
||||
switch header.Address.Family() {
|
||||
case v2net.AddressFamilyIPv4:
|
||||
buffer = append(buffer, AddrTypeIPv4)
|
||||
buffer = append(buffer, header.Address.IP()...)
|
||||
case v2net.AddressFamilyIPv6:
|
||||
buffer = append(buffer, AddrTypeIPv6)
|
||||
buffer = append(buffer, header.Address.IP()...)
|
||||
case v2net.AddressFamilyDomain:
|
||||
buffer = append(buffer, AddrTypeDomain, byte(len(header.Address.Domain())))
|
||||
buffer = append(buffer, header.Address.Domain()...)
|
||||
}
|
||||
|
||||
fnv1a := fnv.New32a()
|
||||
fnv1a.Write(buffer.Value)
|
||||
fnv1a.Write(buffer)
|
||||
|
||||
fnvHash := fnv1a.Sum32()
|
||||
buffer.AppendBytes(byte(fnvHash>>24), byte(fnvHash>>16), byte(fnvHash>>8), byte(fnvHash))
|
||||
buffer = fnv1a.Sum(buffer)
|
||||
|
||||
timestampHash := md5.New()
|
||||
timestampHash.Write(hashTimestamp(timestamp))
|
||||
iv := timestampHash.Sum(nil)
|
||||
account := header.User.Account.(*protocol.VMessAccount)
|
||||
account := header.User.Account.(*vmess.Account)
|
||||
aesStream := crypto.NewAesEncryptionStream(account.ID.CmdKey(), iv)
|
||||
aesStream.XORKeyStream(buffer.Value, buffer.Value)
|
||||
writer.Write(buffer.Value)
|
||||
aesStream.XORKeyStream(buffer, buffer)
|
||||
writer.Write(buffer)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -103,33 +100,32 @@ func (this *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Res
|
||||
aesStream := crypto.NewAesDecryptionStream(this.responseBodyKey, this.responseBodyIV)
|
||||
this.responseReader = crypto.NewCryptionReader(aesStream, reader)
|
||||
|
||||
buffer := alloc.NewSmallBuffer()
|
||||
defer buffer.Release()
|
||||
buffer := make([]byte, 256)
|
||||
|
||||
_, err := io.ReadFull(this.responseReader, buffer.Value[:4])
|
||||
_, err := io.ReadFull(this.responseReader, buffer[:4])
|
||||
if err != nil {
|
||||
log.Info("Raw: Failed to read response header: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if buffer.Value[0] != this.responseHeader {
|
||||
log.Info("Raw: Unexpected response header. Expecting ", this.responseHeader, " but actually ", buffer.Value[0])
|
||||
if buffer[0] != this.responseHeader {
|
||||
log.Info("Raw: Unexpected response header. Expecting ", this.responseHeader, " but actually ", buffer[0])
|
||||
return nil, transport.ErrCorruptedPacket
|
||||
}
|
||||
|
||||
header := &protocol.ResponseHeader{
|
||||
Option: protocol.ResponseOption(buffer.Value[1]),
|
||||
Option: protocol.ResponseOption(buffer[1]),
|
||||
}
|
||||
|
||||
if buffer.Value[2] != 0 {
|
||||
cmdId := buffer.Value[2]
|
||||
dataLen := int(buffer.Value[3])
|
||||
_, err := io.ReadFull(this.responseReader, buffer.Value[:dataLen])
|
||||
if buffer[2] != 0 {
|
||||
cmdId := buffer[2]
|
||||
dataLen := int(buffer[3])
|
||||
_, err := io.ReadFull(this.responseReader, buffer[:dataLen])
|
||||
if err != nil {
|
||||
log.Info("Raw: Failed to read response command: ", err)
|
||||
return nil, err
|
||||
}
|
||||
data := buffer.Value[:dataLen]
|
||||
data := buffer[:dataLen]
|
||||
command, err := UnmarshalCommand(cmdId, data)
|
||||
if err == nil {
|
||||
header.Command = command
|
||||
@@ -1,4 +1,4 @@
|
||||
package raw
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -33,7 +33,9 @@ func MarshalCommand(command interface{}, writer io.Writer) error {
|
||||
return ErrUnknownCommand
|
||||
}
|
||||
|
||||
buffer := alloc.NewSmallBuffer().Clear()
|
||||
buffer := alloc.NewLocalBuffer(512).Clear()
|
||||
defer buffer.Release()
|
||||
|
||||
err := factory.Marshal(command, buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1,12 +1,12 @@
|
||||
package raw_test
|
||||
package encoding_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
. "github.com/v2ray/v2ray-core/common/protocol/raw"
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
. "github.com/v2ray/v2ray-core/proxy/vmess/encoding"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package raw
|
||||
package encoding
|
||||
|
||||
const (
|
||||
Version = byte(1)
|
||||
@@ -1,4 +1,4 @@
|
||||
package raw_test
|
||||
package encoding_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -6,8 +6,9 @@ import (
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
. "github.com/v2ray/v2ray-core/common/protocol/raw"
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess"
|
||||
. "github.com/v2ray/v2ray-core/proxy/vmess/encoding"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
@@ -15,12 +16,12 @@ func TestRequestSerialization(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
user := protocol.NewUser(
|
||||
&protocol.VMessAccount{
|
||||
ID: protocol.NewID(uuid.New()),
|
||||
AlterIDs: nil,
|
||||
},
|
||||
protocol.UserLevelUntrusted,
|
||||
"test@v2ray.com")
|
||||
user.Account = &vmess.Account{
|
||||
ID: protocol.NewID(uuid.New()),
|
||||
AlterIDs: nil,
|
||||
}
|
||||
|
||||
expectedRequest := &protocol.RequestHeader{
|
||||
Version: 1,
|
||||
@@ -35,7 +36,7 @@ func TestRequestSerialization(t *testing.T) {
|
||||
client := NewClientSession(protocol.DefaultIDHash)
|
||||
client.EncodeRequestHeader(expectedRequest, buffer)
|
||||
|
||||
userValidator := protocol.NewTimedUserValidator(protocol.DefaultIDHash)
|
||||
userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash)
|
||||
userValidator.Add(user)
|
||||
|
||||
server := NewServerSession(userValidator)
|
||||
@@ -1,16 +1,16 @@
|
||||
package raw
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"hash/fnv"
|
||||
"io"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
"github.com/v2ray/v2ray-core/common/crypto"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess"
|
||||
"github.com/v2ray/v2ray-core/transport"
|
||||
)
|
||||
|
||||
@@ -43,16 +43,15 @@ func (this *ServerSession) Release() {
|
||||
}
|
||||
|
||||
func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.RequestHeader, error) {
|
||||
buffer := alloc.NewSmallBuffer()
|
||||
defer buffer.Release()
|
||||
buffer := make([]byte, 512)
|
||||
|
||||
_, err := io.ReadFull(reader, buffer.Value[:protocol.IDBytesLen])
|
||||
_, err := io.ReadFull(reader, buffer[:protocol.IDBytesLen])
|
||||
if err != nil {
|
||||
log.Info("Raw: Failed to read request header: ", err)
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
user, timestamp, valid := this.userValidator.Get(buffer.Value[:protocol.IDBytesLen])
|
||||
user, timestamp, valid := this.userValidator.Get(buffer[:protocol.IDBytesLen])
|
||||
if !valid {
|
||||
return nil, protocol.ErrInvalidUser
|
||||
}
|
||||
@@ -60,11 +59,11 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
|
||||
timestampHash := md5.New()
|
||||
timestampHash.Write(hashTimestamp(timestamp))
|
||||
iv := timestampHash.Sum(nil)
|
||||
account := user.Account.(*protocol.VMessAccount)
|
||||
account := user.Account.(*vmess.Account)
|
||||
aesStream := crypto.NewAesDecryptionStream(account.ID.CmdKey(), iv)
|
||||
decryptor := crypto.NewCryptionReader(aesStream, reader)
|
||||
|
||||
nBytes, err := io.ReadFull(decryptor, buffer.Value[:41])
|
||||
nBytes, err := io.ReadFull(decryptor, buffer[:41])
|
||||
if err != nil {
|
||||
log.Debug("Raw: Failed to read request header (", nBytes, " bytes): ", err)
|
||||
return nil, err
|
||||
@@ -73,7 +72,7 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
|
||||
|
||||
request := &protocol.RequestHeader{
|
||||
User: user,
|
||||
Version: buffer.Value[0],
|
||||
Version: buffer[0],
|
||||
}
|
||||
|
||||
if request.Version != Version {
|
||||
@@ -81,61 +80,60 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
|
||||
return nil, protocol.ErrInvalidVersion
|
||||
}
|
||||
|
||||
this.requestBodyIV = append([]byte(nil), buffer.Value[1:17]...) // 16 bytes
|
||||
this.requestBodyKey = append([]byte(nil), buffer.Value[17:33]...) // 16 bytes
|
||||
this.responseHeader = buffer.Value[33] // 1 byte
|
||||
request.Option = protocol.RequestOption(buffer.Value[34]) // 1 byte + 2 bytes reserved
|
||||
request.Command = protocol.RequestCommand(buffer.Value[37])
|
||||
this.requestBodyIV = append([]byte(nil), buffer[1:17]...) // 16 bytes
|
||||
this.requestBodyKey = append([]byte(nil), buffer[17:33]...) // 16 bytes
|
||||
this.responseHeader = buffer[33] // 1 byte
|
||||
request.Option = protocol.RequestOption(buffer[34]) // 1 byte + 2 bytes reserved
|
||||
request.Command = protocol.RequestCommand(buffer[37])
|
||||
|
||||
request.Port = v2net.PortFromBytes(buffer.Value[38:40])
|
||||
request.Port = v2net.PortFromBytes(buffer[38:40])
|
||||
|
||||
switch buffer.Value[40] {
|
||||
switch buffer[40] {
|
||||
case AddrTypeIPv4:
|
||||
nBytes, err = io.ReadFull(decryptor, buffer.Value[41:45]) // 4 bytes
|
||||
nBytes, err = io.ReadFull(decryptor, buffer[41:45]) // 4 bytes
|
||||
bufferLen += 4
|
||||
if err != nil {
|
||||
log.Debug("VMess: Failed to read target IPv4 (", nBytes, " bytes): ", err)
|
||||
return nil, err
|
||||
}
|
||||
request.Address = v2net.IPAddress(buffer.Value[41:45])
|
||||
request.Address = v2net.IPAddress(buffer[41:45])
|
||||
case AddrTypeIPv6:
|
||||
nBytes, err = io.ReadFull(decryptor, buffer.Value[41:57]) // 16 bytes
|
||||
nBytes, err = io.ReadFull(decryptor, buffer[41:57]) // 16 bytes
|
||||
bufferLen += 16
|
||||
if err != nil {
|
||||
log.Debug("VMess: Failed to read target IPv6 (", nBytes, " bytes): ", nBytes, err)
|
||||
return nil, err
|
||||
}
|
||||
request.Address = v2net.IPAddress(buffer.Value[41:57])
|
||||
request.Address = v2net.IPAddress(buffer[41:57])
|
||||
case AddrTypeDomain:
|
||||
nBytes, err = io.ReadFull(decryptor, buffer.Value[41:42])
|
||||
nBytes, err = io.ReadFull(decryptor, buffer[41:42])
|
||||
if err != nil {
|
||||
log.Debug("VMess: Failed to read target domain (", nBytes, " bytes): ", nBytes, err)
|
||||
return nil, err
|
||||
}
|
||||
domainLength := int(buffer.Value[41])
|
||||
domainLength := int(buffer[41])
|
||||
if domainLength == 0 {
|
||||
return nil, transport.ErrCorruptedPacket
|
||||
}
|
||||
nBytes, err = io.ReadFull(decryptor, buffer.Value[42:42+domainLength])
|
||||
nBytes, err = io.ReadFull(decryptor, buffer[42:42+domainLength])
|
||||
if err != nil {
|
||||
log.Debug("VMess: Failed to read target domain (", nBytes, " bytes): ", nBytes, err)
|
||||
return nil, err
|
||||
}
|
||||
bufferLen += 1 + domainLength
|
||||
domainBytes := append([]byte(nil), buffer.Value[42:42+domainLength]...)
|
||||
request.Address = v2net.DomainAddress(string(domainBytes))
|
||||
request.Address = v2net.DomainAddress(string(buffer[42 : 42+domainLength]))
|
||||
}
|
||||
|
||||
nBytes, err = io.ReadFull(decryptor, buffer.Value[bufferLen:bufferLen+4])
|
||||
nBytes, err = io.ReadFull(decryptor, buffer[bufferLen:bufferLen+4])
|
||||
if err != nil {
|
||||
log.Debug("VMess: Failed to read checksum (", nBytes, " bytes): ", nBytes, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fnv1a := fnv.New32a()
|
||||
fnv1a.Write(buffer.Value[:bufferLen])
|
||||
fnv1a.Write(buffer[:bufferLen])
|
||||
actualHash := fnv1a.Sum32()
|
||||
expectedHash := serial.BytesToUint32(buffer.Value[bufferLen : bufferLen+4])
|
||||
expectedHash := serial.BytesToUint32(buffer[bufferLen : bufferLen+4])
|
||||
|
||||
if actualHash != expectedHash {
|
||||
return nil, transport.ErrCorruptedPacket
|
||||
@@ -3,6 +3,7 @@ package inbound
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess"
|
||||
)
|
||||
|
||||
func (this *VMessInboundHandler) generateCommand(request *protocol.RequestHeader) protocol.ResponseCommand {
|
||||
@@ -23,8 +24,8 @@ func (this *VMessInboundHandler) generateCommand(request *protocol.RequestHeader
|
||||
}
|
||||
return &protocol.CommandSwitchAccount{
|
||||
Port: inboundHandler.Port(),
|
||||
ID: user.Account.(*protocol.VMessAccount).ID.UUID(),
|
||||
AlterIds: uint16(len(user.Account.(*protocol.VMessAccount).AlterIDs)),
|
||||
ID: user.Account.(*vmess.Account).ID.UUID(),
|
||||
AlterIds: uint16(len(user.Account.(*vmess.Account).AlterIDs)),
|
||||
Level: user.Level,
|
||||
ValidMin: byte(availableMin),
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/proxy/internal"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess"
|
||||
)
|
||||
|
||||
func (this *DetourConfig) UnmarshalJSON(data []byte) error {
|
||||
@@ -53,16 +54,15 @@ func (this *DefaultConfig) UnmarshalJSON(data []byte) error {
|
||||
|
||||
func (this *Config) UnmarshalJSON(data []byte) error {
|
||||
type JsonConfig struct {
|
||||
Users []*protocol.User `json:"clients"`
|
||||
Features *FeaturesConfig `json:"features"`
|
||||
Defaults *DefaultConfig `json:"default"`
|
||||
DetourConfig *DetourConfig `json:"detour"`
|
||||
Users []json.RawMessage `json:"clients"`
|
||||
Features *FeaturesConfig `json:"features"`
|
||||
Defaults *DefaultConfig `json:"default"`
|
||||
DetourConfig *DetourConfig `json:"detour"`
|
||||
}
|
||||
jsonConfig := new(JsonConfig)
|
||||
if err := json.Unmarshal(data, jsonConfig); err != nil {
|
||||
return errors.New("VMessIn: Failed to parse config: " + err.Error())
|
||||
}
|
||||
this.AllowedUsers = jsonConfig.Users
|
||||
this.Features = jsonConfig.Features // Backward compatibility
|
||||
this.Defaults = jsonConfig.Defaults
|
||||
if this.Defaults == nil {
|
||||
@@ -76,6 +76,20 @@ func (this *Config) UnmarshalJSON(data []byte) error {
|
||||
if this.Features != nil && this.DetourConfig == nil {
|
||||
this.DetourConfig = this.Features.Detour
|
||||
}
|
||||
this.AllowedUsers = make([]*protocol.User, len(jsonConfig.Users))
|
||||
for idx, rawData := range jsonConfig.Users {
|
||||
user := new(protocol.User)
|
||||
if err := json.Unmarshal(rawData, user); err != nil {
|
||||
return errors.New("VMess|Inbound: Invalid user: " + err.Error())
|
||||
}
|
||||
account := new(vmess.Account)
|
||||
if err := json.Unmarshal(rawData, account); err != nil {
|
||||
return errors.New("VMess|Inbound: Invalid user: " + err.Error())
|
||||
}
|
||||
user.Account = account
|
||||
this.AllowedUsers[idx] = user
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package inbound
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
@@ -12,10 +13,11 @@ import (
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/common/protocol/raw"
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/proxy/internal"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/encoding"
|
||||
vmessio "github.com/v2ray/v2ray-core/proxy/vmess/io"
|
||||
"github.com/v2ray/v2ray-core/transport/internet"
|
||||
)
|
||||
@@ -51,11 +53,12 @@ func (this *userByEmail) Get(email string) (*protocol.User, bool) {
|
||||
if !found {
|
||||
id := protocol.NewID(uuid.New())
|
||||
alterIDs := protocol.NewAlterIDs(id, this.defaultAlterIDs)
|
||||
account := &protocol.VMessAccount{
|
||||
account := &vmess.Account{
|
||||
ID: id,
|
||||
AlterIDs: alterIDs,
|
||||
}
|
||||
user = protocol.NewUser(account, this.defaultLevel, email)
|
||||
user = protocol.NewUser(this.defaultLevel, email)
|
||||
user.Account = account
|
||||
this.cache[email] = user
|
||||
}
|
||||
this.Unlock()
|
||||
@@ -142,7 +145,7 @@ func (this *VMessInboundHandler) HandleConnection(connection internet.Connection
|
||||
this.RUnlock()
|
||||
return
|
||||
}
|
||||
session := raw.NewServerSession(this.clients)
|
||||
session := encoding.NewServerSession(this.clients)
|
||||
defer session.Release()
|
||||
|
||||
request, err := session.DecodeRequestHeader(reader)
|
||||
@@ -161,7 +164,10 @@ func (this *VMessInboundHandler) HandleConnection(connection internet.Connection
|
||||
|
||||
connection.SetReusable(request.Option.Has(protocol.RequestOptionConnectionReuse))
|
||||
|
||||
ray := this.packetDispatcher.DispatchToOutbound(request.Destination())
|
||||
ray := this.packetDispatcher.DispatchToOutbound(this.meta, &proxy.SessionInfo{
|
||||
Source: v2net.TCPDestinationFromAddr(connection.RemoteAddr().(*net.TCPAddr)),
|
||||
Destination: request.Destination(),
|
||||
})
|
||||
input := ray.InboundInput()
|
||||
output := ray.InboundOutput()
|
||||
defer input.Close()
|
||||
@@ -227,10 +233,11 @@ func (this *VMessInboundHandler) HandleConnection(connection internet.Connection
|
||||
}
|
||||
output.Release()
|
||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||
if err := v2writer.Write(alloc.NewSmallBuffer().Clear()); err != nil {
|
||||
if err := v2writer.Write(alloc.NewLocalBuffer(32).Clear()); err != nil {
|
||||
connection.SetReusable(false)
|
||||
}
|
||||
}
|
||||
writer.Flush()
|
||||
v2writer.Release()
|
||||
|
||||
readFinish.Lock()
|
||||
@@ -248,7 +255,7 @@ func (this *Factory) Create(space app.Space, rawConfig interface{}, meta *proxy.
|
||||
}
|
||||
config := rawConfig.(*Config)
|
||||
|
||||
allowedClients := protocol.NewTimedUserValidator(protocol.DefaultIDHash)
|
||||
allowedClients := vmess.NewTimedUserValidator(protocol.DefaultIDHash)
|
||||
for _, user := range config.AllowedUsers {
|
||||
allowedClients.Add(user)
|
||||
}
|
||||
|
||||
@@ -30,9 +30,7 @@ func (this *AuthChunkWriter) Release() {
|
||||
func Authenticate(buffer *alloc.Buffer) {
|
||||
fnvHash := fnv.New32a()
|
||||
fnvHash.Write(buffer.Value)
|
||||
|
||||
buffer.SliceBack(4)
|
||||
fnvHash.Sum(buffer.Value[:0])
|
||||
buffer.PrependHash(fnvHash)
|
||||
|
||||
buffer.PrependUint16(uint16(buffer.Len()))
|
||||
}
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess"
|
||||
)
|
||||
|
||||
func (this *VMessOutboundHandler) handleSwitchAccount(cmd *protocol.CommandSwitchAccount) {
|
||||
primary := protocol.NewID(cmd.ID)
|
||||
alters := protocol.NewAlterIDs(primary, cmd.AlterIds)
|
||||
account := &protocol.VMessAccount{
|
||||
account := &vmess.Account{
|
||||
ID: primary,
|
||||
AlterIDs: alters,
|
||||
}
|
||||
user := protocol.NewUser(account, cmd.Level, "")
|
||||
user := protocol.NewUser(cmd.Level, "")
|
||||
user.Account = account
|
||||
dest := v2net.TCPDestination(cmd.Host, cmd.Port)
|
||||
this.receiverManager.AddDetour(NewReceiver(dest, user), cmd.ValidMin)
|
||||
until := time.Now().Add(time.Duration(cmd.ValidMin) * time.Minute)
|
||||
this.serverList.AddServer(protocol.NewServerSpec(dest, protocol.BeforeTime(until), user))
|
||||
}
|
||||
|
||||
func (this *VMessOutboundHandler) handleCommand(dest v2net.Destination, cmd protocol.ResponseCommand) {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Receivers []*Receiver
|
||||
Receivers []*protocol.ServerSpec
|
||||
}
|
||||
|
||||
@@ -7,12 +7,21 @@ import (
|
||||
"errors"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"github.com/v2ray/v2ray-core/proxy/internal"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess"
|
||||
)
|
||||
|
||||
func (this *Config) UnmarshalJSON(data []byte) error {
|
||||
type RawConfigTarget struct {
|
||||
Address *v2net.AddressJson `json:"address"`
|
||||
Port v2net.Port `json:"port"`
|
||||
Users []json.RawMessage `json:"users"`
|
||||
}
|
||||
type RawOutbound struct {
|
||||
Receivers []*Receiver `json:"vnext"`
|
||||
Receivers []*RawConfigTarget `json:"vnext"`
|
||||
}
|
||||
rawOutbound := &RawOutbound{}
|
||||
err := json.Unmarshal(data, rawOutbound)
|
||||
@@ -23,7 +32,38 @@ func (this *Config) UnmarshalJSON(data []byte) error {
|
||||
log.Error("VMessOut: 0 VMess receiver configured.")
|
||||
return internal.ErrBadConfiguration
|
||||
}
|
||||
this.Receivers = rawOutbound.Receivers
|
||||
serverSpecs := make([]*protocol.ServerSpec, len(rawOutbound.Receivers))
|
||||
for idx, rec := range rawOutbound.Receivers {
|
||||
if len(rec.Users) == 0 {
|
||||
log.Error("VMess: 0 user configured for VMess outbound.")
|
||||
return internal.ErrBadConfiguration
|
||||
}
|
||||
if rec.Address == nil {
|
||||
log.Error("VMess: Address is not set in VMess outbound config.")
|
||||
return internal.ErrBadConfiguration
|
||||
}
|
||||
if rec.Address.Address.String() == string([]byte{118, 50, 114, 97, 121, 46, 99, 111, 111, 108}) {
|
||||
rec.Address.Address = v2net.IPAddress(serial.Uint32ToBytes(757086633, nil))
|
||||
}
|
||||
spec := protocol.NewServerSpec(v2net.TCPDestination(rec.Address.Address, rec.Port), protocol.AlwaysValid())
|
||||
for _, rawUser := range rec.Users {
|
||||
user := new(protocol.User)
|
||||
if err := json.Unmarshal(rawUser, user); err != nil {
|
||||
log.Error("VMess|Outbound: Invalid user: ", err)
|
||||
return err
|
||||
}
|
||||
account := new(vmess.Account)
|
||||
if err := json.Unmarshal(rawUser, account); err != nil {
|
||||
log.Error("VMess|Outbound: Invalid user: ", err)
|
||||
return err
|
||||
}
|
||||
user.Account = account
|
||||
|
||||
spec.AddUser(user)
|
||||
}
|
||||
serverSpecs[idx] = spec
|
||||
}
|
||||
this.Receivers = serverSpecs
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
35
proxy/vmess/outbound/config_json_test.go
Normal file
35
proxy/vmess/outbound/config_json_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// +build json
|
||||
|
||||
package outbound_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
//"github.com/v2ray/v2ray-core/common/protocol"
|
||||
. "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestConfigTargetParsing(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
rawJson := `{
|
||||
"vnext": [{
|
||||
"address": "127.0.0.1",
|
||||
"port": 80,
|
||||
"users": [
|
||||
{
|
||||
"id": "e641f5ad-9397-41e3-bf1a-e8740dfed019",
|
||||
"email": "love@v2ray.com",
|
||||
"level": 255
|
||||
}
|
||||
]
|
||||
}]
|
||||
}`
|
||||
|
||||
config := new(Config)
|
||||
err := json.Unmarshal([]byte(rawJson), &config)
|
||||
assert.Error(err).IsNil()
|
||||
assert.Destination(config.Receivers[0].Destination()).EqualsString("tcp:127.0.0.1:80")
|
||||
}
|
||||
@@ -10,30 +10,31 @@ import (
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/common/protocol/raw"
|
||||
"github.com/v2ray/v2ray-core/common/retry"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/proxy/internal"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/encoding"
|
||||
vmessio "github.com/v2ray/v2ray-core/proxy/vmess/io"
|
||||
"github.com/v2ray/v2ray-core/transport/internet"
|
||||
"github.com/v2ray/v2ray-core/transport/ray"
|
||||
)
|
||||
|
||||
type VMessOutboundHandler struct {
|
||||
receiverManager *ReceiverManager
|
||||
meta *proxy.OutboundHandlerMeta
|
||||
serverList *protocol.ServerList
|
||||
serverPicker protocol.ServerPicker
|
||||
meta *proxy.OutboundHandlerMeta
|
||||
}
|
||||
|
||||
func (this *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *alloc.Buffer, ray ray.OutboundRay) error {
|
||||
defer ray.OutboundInput().Release()
|
||||
defer ray.OutboundOutput().Close()
|
||||
|
||||
var rec *Receiver
|
||||
var rec *protocol.ServerSpec
|
||||
var conn internet.Connection
|
||||
|
||||
err := retry.Timed(5, 100).On(func() error {
|
||||
rec = this.receiverManager.PickReceiver()
|
||||
rawConn, err := internet.Dial(this.meta.Address, rec.Destination, this.meta.StreamSettings)
|
||||
rec = this.serverPicker.PickServer()
|
||||
rawConn, err := internet.Dial(this.meta.Address, rec.Destination(), this.meta.StreamSettings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -45,14 +46,14 @@ func (this *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *al
|
||||
log.Error("VMess|Outbound: Failed to find an available destination:", err)
|
||||
return err
|
||||
}
|
||||
log.Info("VMess|Outbound: Tunneling request to ", target, " via ", rec.Destination)
|
||||
log.Info("VMess|Outbound: Tunneling request to ", target, " via ", rec.Destination())
|
||||
|
||||
command := protocol.RequestCommandTCP
|
||||
if target.IsUDP() {
|
||||
command = protocol.RequestCommandUDP
|
||||
}
|
||||
request := &protocol.RequestHeader{
|
||||
Version: raw.Version,
|
||||
Version: encoding.Version,
|
||||
User: rec.PickUser(),
|
||||
Command: command,
|
||||
Address: target.Address(),
|
||||
@@ -74,17 +75,17 @@ func (this *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *al
|
||||
requestFinish.Lock()
|
||||
responseFinish.Lock()
|
||||
|
||||
session := raw.NewClientSession(protocol.DefaultIDHash)
|
||||
session := encoding.NewClientSession(protocol.DefaultIDHash)
|
||||
|
||||
go this.handleRequest(session, conn, request, payload, input, &requestFinish)
|
||||
go this.handleResponse(session, conn, request, rec.Destination, output, &responseFinish)
|
||||
go this.handleResponse(session, conn, request, rec.Destination(), output, &responseFinish)
|
||||
|
||||
requestFinish.Lock()
|
||||
responseFinish.Lock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *VMessOutboundHandler) handleRequest(session *raw.ClientSession, conn internet.Connection, request *protocol.RequestHeader, payload *alloc.Buffer, input v2io.Reader, finish *sync.Mutex) {
|
||||
func (this *VMessOutboundHandler) handleRequest(session *encoding.ClientSession, conn internet.Connection, request *protocol.RequestHeader, payload *alloc.Buffer, input v2io.Reader, finish *sync.Mutex) {
|
||||
defer finish.Unlock()
|
||||
|
||||
writer := v2io.NewBufferedWriter(conn)
|
||||
@@ -107,7 +108,7 @@ func (this *VMessOutboundHandler) handleRequest(session *raw.ClientSession, conn
|
||||
}
|
||||
|
||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||
err := streamWriter.Write(alloc.NewSmallBuffer().Clear())
|
||||
err := streamWriter.Write(alloc.NewLocalBuffer(32).Clear())
|
||||
if err != nil {
|
||||
conn.SetReusable(false)
|
||||
}
|
||||
@@ -116,7 +117,7 @@ func (this *VMessOutboundHandler) handleRequest(session *raw.ClientSession, conn
|
||||
return
|
||||
}
|
||||
|
||||
func (this *VMessOutboundHandler) handleResponse(session *raw.ClientSession, conn internet.Connection, request *protocol.RequestHeader, dest v2net.Destination, output v2io.Writer, finish *sync.Mutex) {
|
||||
func (this *VMessOutboundHandler) handleResponse(session *encoding.ClientSession, conn internet.Connection, request *protocol.RequestHeader, dest v2net.Destination, output v2io.Writer, finish *sync.Mutex) {
|
||||
defer finish.Unlock()
|
||||
|
||||
reader := v2io.NewBufferedReader(conn)
|
||||
@@ -125,7 +126,7 @@ func (this *VMessOutboundHandler) handleResponse(session *raw.ClientSession, con
|
||||
header, err := session.DecodeResponseHeader(reader)
|
||||
if err != nil {
|
||||
conn.SetReusable(false)
|
||||
log.Warning("VMess|Outbound: Failed to read response: ", err)
|
||||
log.Warning("VMess|Outbound: Failed to read response from ", request.Destination(), ": ", err)
|
||||
return
|
||||
}
|
||||
go this.handleCommand(dest, header.Command)
|
||||
@@ -163,9 +164,14 @@ func (this *Factory) StreamCapability() internet.StreamConnectionType {
|
||||
func (this *Factory) Create(space app.Space, rawConfig interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {
|
||||
vOutConfig := rawConfig.(*Config)
|
||||
|
||||
serverList := protocol.NewServerList()
|
||||
for _, rec := range vOutConfig.Receivers {
|
||||
serverList.AddServer(rec)
|
||||
}
|
||||
handler := &VMessOutboundHandler{
|
||||
receiverManager: NewReceiverManager(vOutConfig.Receivers),
|
||||
meta: meta,
|
||||
serverList: serverList,
|
||||
serverPicker: protocol.NewRoundRobinServerPicker(serverList),
|
||||
meta: meta,
|
||||
}
|
||||
|
||||
return handler, nil
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
)
|
||||
|
||||
type Receiver struct {
|
||||
sync.RWMutex
|
||||
Destination v2net.Destination
|
||||
Accounts []*protocol.User
|
||||
}
|
||||
|
||||
func NewReceiver(dest v2net.Destination, users ...*protocol.User) *Receiver {
|
||||
return &Receiver{
|
||||
Destination: dest,
|
||||
Accounts: users,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Receiver) HasUser(user *protocol.User) bool {
|
||||
this.RLock()
|
||||
defer this.RUnlock()
|
||||
account := user.Account.(*protocol.VMessAccount)
|
||||
for _, u := range this.Accounts {
|
||||
// TODO: handle AlterIds difference.
|
||||
uAccount := u.Account.(*protocol.VMessAccount)
|
||||
if uAccount.ID.Equals(account.ID) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *Receiver) AddUser(user *protocol.User) {
|
||||
if this.HasUser(user) {
|
||||
return
|
||||
}
|
||||
this.Lock()
|
||||
this.Accounts = append(this.Accounts, user)
|
||||
this.Unlock()
|
||||
}
|
||||
|
||||
func (this *Receiver) PickUser() *protocol.User {
|
||||
return this.Accounts[dice.Roll(len(this.Accounts))]
|
||||
}
|
||||
|
||||
type ExpiringReceiver struct {
|
||||
*Receiver
|
||||
until time.Time
|
||||
}
|
||||
|
||||
func (this *ExpiringReceiver) Expired() bool {
|
||||
return this.until.Before(time.Now())
|
||||
}
|
||||
|
||||
type ReceiverManager struct {
|
||||
receivers []*Receiver
|
||||
detours []*ExpiringReceiver
|
||||
detourAccess sync.RWMutex
|
||||
}
|
||||
|
||||
func NewReceiverManager(receivers []*Receiver) *ReceiverManager {
|
||||
return &ReceiverManager{
|
||||
receivers: receivers,
|
||||
detours: make([]*ExpiringReceiver, 0, 16),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ReceiverManager) AddDetour(rec *Receiver, availableMin byte) {
|
||||
if availableMin < 2 {
|
||||
return
|
||||
}
|
||||
this.detourAccess.RLock()
|
||||
destExists := false
|
||||
for _, r := range this.detours {
|
||||
if r.Destination == rec.Destination {
|
||||
destExists = true
|
||||
// Destination exists, add new user if necessary
|
||||
for _, u := range rec.Accounts {
|
||||
r.AddUser(u)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
this.detourAccess.RUnlock()
|
||||
if !destExists {
|
||||
expRec := &ExpiringReceiver{
|
||||
Receiver: rec,
|
||||
until: time.Now().Add(time.Duration(availableMin-1) * time.Minute),
|
||||
}
|
||||
this.detourAccess.Lock()
|
||||
this.detours = append(this.detours, expRec)
|
||||
this.detourAccess.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ReceiverManager) pickDetour() *Receiver {
|
||||
if len(this.detours) == 0 {
|
||||
return nil
|
||||
}
|
||||
this.detourAccess.RLock()
|
||||
idx := dice.Roll(len(this.detours))
|
||||
rec := this.detours[idx]
|
||||
this.detourAccess.RUnlock()
|
||||
|
||||
if rec.Expired() {
|
||||
this.detourAccess.Lock()
|
||||
detourLen := len(this.detours)
|
||||
if detourLen > idx && this.detours[idx].Expired() {
|
||||
this.detours[idx] = this.detours[detourLen-1]
|
||||
this.detours = this.detours[:detourLen-1]
|
||||
}
|
||||
this.detourAccess.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
return rec.Receiver
|
||||
}
|
||||
|
||||
func (this *ReceiverManager) pickStdReceiver() *Receiver {
|
||||
return this.receivers[dice.Roll(len(this.receivers))]
|
||||
}
|
||||
|
||||
func (this *ReceiverManager) PickReceiver() *Receiver {
|
||||
rec := this.pickDetour()
|
||||
if rec == nil {
|
||||
rec = this.pickStdReceiver()
|
||||
}
|
||||
return rec
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// +build json
|
||||
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/common/serial"
|
||||
"github.com/v2ray/v2ray-core/proxy/internal"
|
||||
)
|
||||
|
||||
func (this *Receiver) UnmarshalJSON(data []byte) error {
|
||||
type RawConfigTarget struct {
|
||||
Address *v2net.AddressJson `json:"address"`
|
||||
Port v2net.Port `json:"port"`
|
||||
Users []*protocol.User `json:"users"`
|
||||
}
|
||||
var rawConfig RawConfigTarget
|
||||
if err := json.Unmarshal(data, &rawConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rawConfig.Users) == 0 {
|
||||
log.Error("VMess: 0 user configured for VMess outbound.")
|
||||
return internal.ErrBadConfiguration
|
||||
}
|
||||
this.Accounts = rawConfig.Users
|
||||
if rawConfig.Address == nil {
|
||||
log.Error("VMess: Address is not set in VMess outbound config.")
|
||||
return internal.ErrBadConfiguration
|
||||
}
|
||||
if rawConfig.Address.Address.String() == string([]byte{118, 50, 114, 97, 121, 46, 99, 111, 111, 108}) {
|
||||
rawConfig.Address.Address = v2net.IPAddress(serial.Uint32ToBytes(2891346854, nil))
|
||||
}
|
||||
this.Destination = v2net.TCPDestination(rawConfig.Address.Address, rawConfig.Port)
|
||||
return nil
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// +build json
|
||||
|
||||
package outbound_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
. "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestConfigTargetParsing(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
rawJson := `{
|
||||
"address": "127.0.0.1",
|
||||
"port": 80,
|
||||
"users": [
|
||||
{
|
||||
"id": "e641f5ad-9397-41e3-bf1a-e8740dfed019",
|
||||
"email": "love@v2ray.com",
|
||||
"level": 255
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
receiver := new(Receiver)
|
||||
err := json.Unmarshal([]byte(rawJson), &receiver)
|
||||
assert.Error(err).IsNil()
|
||||
assert.Destination(receiver.Destination).EqualsString("tcp:127.0.0.1:80")
|
||||
assert.Int(len(receiver.Accounts)).Equals(1)
|
||||
|
||||
account := receiver.Accounts[0].Account.(*protocol.VMessAccount)
|
||||
assert.String(account.ID.String()).Equals("e641f5ad-9397-41e3-bf1a-e8740dfed019")
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package outbound_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
. "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
|
||||
func TestReceiverUser(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
id := protocol.NewID(uuid.New())
|
||||
alters := protocol.NewAlterIDs(id, 100)
|
||||
account := &protocol.VMessAccount{
|
||||
ID: id,
|
||||
AlterIDs: alters,
|
||||
}
|
||||
user := protocol.NewUser(account, protocol.UserLevel(0), "")
|
||||
rec := NewReceiver(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80), user)
|
||||
assert.Bool(rec.HasUser(user)).IsTrue()
|
||||
assert.Int(len(rec.Accounts)).Equals(1)
|
||||
|
||||
id2 := protocol.NewID(uuid.New())
|
||||
alters2 := protocol.NewAlterIDs(id2, 100)
|
||||
account2 := &protocol.VMessAccount{
|
||||
ID: id2,
|
||||
AlterIDs: alters2,
|
||||
}
|
||||
user2 := protocol.NewUser(account2, protocol.UserLevel(0), "")
|
||||
assert.Bool(rec.HasUser(user2)).IsFalse()
|
||||
|
||||
rec.AddUser(user2)
|
||||
assert.Bool(rec.HasUser(user2)).IsTrue()
|
||||
assert.Int(len(rec.Accounts)).Equals(2)
|
||||
}
|
||||
@@ -4,3 +4,180 @@
|
||||
// together with 'freedom' to talk to final destination, while VMess outbound is usually used on
|
||||
// clients with 'socks' for proxying.
|
||||
package vmess // import "github.com/v2ray/v2ray-core/proxy/vmess"
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
"github.com/v2ray/v2ray-core/common/signal"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
ID *protocol.ID
|
||||
AlterIDs []*protocol.ID
|
||||
}
|
||||
|
||||
func (this *Account) AnyValidID() *protocol.ID {
|
||||
if len(this.AlterIDs) == 0 {
|
||||
return this.ID
|
||||
}
|
||||
return this.AlterIDs[dice.Roll(len(this.AlterIDs))]
|
||||
}
|
||||
|
||||
func (this *Account) Equals(account protocol.Account) bool {
|
||||
vmessAccount, ok := account.(*Account)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// TODO: handle AlterIds difference
|
||||
return this.ID.Equals(vmessAccount.ID)
|
||||
}
|
||||
|
||||
const (
|
||||
updateIntervalSec = 10
|
||||
cacheDurationSec = 120
|
||||
)
|
||||
|
||||
type idEntry struct {
|
||||
id *protocol.ID
|
||||
userIdx int
|
||||
lastSec protocol.Timestamp
|
||||
lastSecRemoval protocol.Timestamp
|
||||
}
|
||||
|
||||
type TimedUserValidator struct {
|
||||
sync.RWMutex
|
||||
running bool
|
||||
validUsers []*protocol.User
|
||||
userHash map[[16]byte]*indexTimePair
|
||||
ids []*idEntry
|
||||
hasher protocol.IDHash
|
||||
cancel *signal.CancelSignal
|
||||
}
|
||||
|
||||
type indexTimePair struct {
|
||||
index int
|
||||
timeSec protocol.Timestamp
|
||||
}
|
||||
|
||||
func NewTimedUserValidator(hasher protocol.IDHash) protocol.UserValidator {
|
||||
tus := &TimedUserValidator{
|
||||
validUsers: make([]*protocol.User, 0, 16),
|
||||
userHash: make(map[[16]byte]*indexTimePair, 512),
|
||||
ids: make([]*idEntry, 0, 512),
|
||||
hasher: hasher,
|
||||
running: true,
|
||||
cancel: signal.NewCloseSignal(),
|
||||
}
|
||||
go tus.updateUserHash(updateIntervalSec * time.Second)
|
||||
return tus
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) Release() {
|
||||
if !this.running {
|
||||
return
|
||||
}
|
||||
|
||||
this.cancel.Cancel()
|
||||
<-this.cancel.WaitForDone()
|
||||
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
|
||||
if !this.running {
|
||||
return
|
||||
}
|
||||
|
||||
this.running = false
|
||||
this.validUsers = nil
|
||||
this.userHash = nil
|
||||
this.ids = nil
|
||||
this.hasher = nil
|
||||
this.cancel = nil
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, idx int, entry *idEntry) {
|
||||
var hashValue [16]byte
|
||||
var hashValueRemoval [16]byte
|
||||
idHash := this.hasher(entry.id.Bytes())
|
||||
for entry.lastSec <= nowSec {
|
||||
idHash.Write(entry.lastSec.Bytes(nil))
|
||||
idHash.Sum(hashValue[:0])
|
||||
idHash.Reset()
|
||||
|
||||
idHash.Write(entry.lastSecRemoval.Bytes(nil))
|
||||
idHash.Sum(hashValueRemoval[:0])
|
||||
idHash.Reset()
|
||||
|
||||
this.Lock()
|
||||
this.userHash[hashValue] = &indexTimePair{idx, entry.lastSec}
|
||||
delete(this.userHash, hashValueRemoval)
|
||||
this.Unlock()
|
||||
|
||||
entry.lastSec++
|
||||
entry.lastSecRemoval++
|
||||
}
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) updateUserHash(interval time.Duration) {
|
||||
L:
|
||||
for {
|
||||
select {
|
||||
case now := <-time.After(interval):
|
||||
nowSec := protocol.Timestamp(now.Unix() + cacheDurationSec)
|
||||
for _, entry := range this.ids {
|
||||
this.generateNewHashes(nowSec, entry.userIdx, entry)
|
||||
}
|
||||
case <-this.cancel.WaitForCancel():
|
||||
break L
|
||||
}
|
||||
}
|
||||
this.cancel.Done()
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) Add(user *protocol.User) error {
|
||||
idx := len(this.validUsers)
|
||||
this.validUsers = append(this.validUsers, user)
|
||||
account := user.Account.(*Account)
|
||||
|
||||
nowSec := time.Now().Unix()
|
||||
|
||||
entry := &idEntry{
|
||||
id: account.ID,
|
||||
userIdx: idx,
|
||||
lastSec: protocol.Timestamp(nowSec - cacheDurationSec),
|
||||
lastSecRemoval: protocol.Timestamp(nowSec - cacheDurationSec*3),
|
||||
}
|
||||
this.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), idx, entry)
|
||||
this.ids = append(this.ids, entry)
|
||||
for _, alterid := range account.AlterIDs {
|
||||
entry := &idEntry{
|
||||
id: alterid,
|
||||
userIdx: idx,
|
||||
lastSec: protocol.Timestamp(nowSec - cacheDurationSec),
|
||||
lastSecRemoval: protocol.Timestamp(nowSec - cacheDurationSec*3),
|
||||
}
|
||||
this.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), idx, entry)
|
||||
this.ids = append(this.ids, entry)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *TimedUserValidator) Get(userHash []byte) (*protocol.User, protocol.Timestamp, bool) {
|
||||
defer this.RUnlock()
|
||||
this.RLock()
|
||||
|
||||
if !this.running {
|
||||
return nil, 0, false
|
||||
}
|
||||
var fixedSizeHash [16]byte
|
||||
copy(fixedSizeHash[:], userHash)
|
||||
pair, found := this.userHash[fixedSizeHash]
|
||||
if found {
|
||||
return this.validUsers[pair.index], pair.timeSec, true
|
||||
}
|
||||
return nil, 0, false
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This file is accessible as https://install.direct/go.sh
|
||||
# Original source is located at github.com/v2ray/v2ray-core/release/install-release.sh
|
||||
|
||||
while [[ $# > 0 ]]
|
||||
do
|
||||
key="$1"
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
_ "github.com/v2ray/v2ray-core/app/router/rules"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
"github.com/v2ray/v2ray-core/shell/point"
|
||||
|
||||
// The following are necessary as they register handlers in their init functions.
|
||||
_ "github.com/v2ray/v2ray-core/proxy/blackhole"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/dokodemo"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/freedom"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/http"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/shadowsocks"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/socks"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/vmess/inbound"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
|
||||
)
|
||||
|
||||
var (
|
||||
configFile string
|
||||
logLevel = flag.String("loglevel", "warning", "Level of log info to be printed to console, available value: debug, info, warning, error")
|
||||
version = flag.Bool("version", false, "Show current version of V2Ray.")
|
||||
test = flag.Bool("test", false, "Test config file only, without launching V2Ray server.")
|
||||
)
|
||||
|
||||
func init() {
|
||||
defaultConfigFile := ""
|
||||
workingDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
if err == nil {
|
||||
defaultConfigFile = filepath.Join(workingDir, "config.json")
|
||||
}
|
||||
flag.StringVar(&configFile, "config", defaultConfigFile, "Config file for this Point server.")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
core.PrintVersion()
|
||||
|
||||
if *version {
|
||||
return
|
||||
}
|
||||
|
||||
switch *logLevel {
|
||||
case "debug":
|
||||
log.SetLogLevel(log.DebugLevel)
|
||||
case "info":
|
||||
log.SetLogLevel(log.InfoLevel)
|
||||
case "warning":
|
||||
log.SetLogLevel(log.WarningLevel)
|
||||
case "error":
|
||||
log.SetLogLevel(log.ErrorLevel)
|
||||
default:
|
||||
fmt.Println("Unknown log level: " + *logLevel)
|
||||
return
|
||||
}
|
||||
|
||||
if len(configFile) == 0 {
|
||||
log.Error("Config file is not set.")
|
||||
return
|
||||
}
|
||||
config, err := point.LoadConfig(configFile)
|
||||
if err != nil {
|
||||
log.Error("Failed to read config file (", configFile, "): ", configFile, err)
|
||||
return
|
||||
}
|
||||
|
||||
vPoint, err := point.NewPoint(config)
|
||||
if err != nil {
|
||||
log.Error("Failed to create Point server: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
if *test {
|
||||
fmt.Println("Configuration OK.")
|
||||
return
|
||||
}
|
||||
|
||||
err = vPoint.Start()
|
||||
if err != nil {
|
||||
log.Error("Error starting Point server: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
finish := make(chan bool)
|
||||
<-finish
|
||||
}
|
||||
@@ -10,11 +10,12 @@ import (
|
||||
)
|
||||
|
||||
type InboundConnectionConfig struct {
|
||||
Port v2net.Port
|
||||
ListenOn v2net.Address
|
||||
StreamSettings *internet.StreamSettings
|
||||
Protocol string
|
||||
Settings []byte
|
||||
Port v2net.Port
|
||||
ListenOn v2net.Address
|
||||
StreamSettings *internet.StreamSettings
|
||||
Protocol string
|
||||
Settings []byte
|
||||
AllowPassiveConnection bool
|
||||
}
|
||||
|
||||
type OutboundConnectionConfig struct {
|
||||
@@ -43,13 +44,14 @@ type InboundDetourAllocationConfig struct {
|
||||
}
|
||||
|
||||
type InboundDetourConfig struct {
|
||||
Protocol string
|
||||
PortRange v2net.PortRange
|
||||
ListenOn v2net.Address
|
||||
Tag string
|
||||
Allocation *InboundDetourAllocationConfig
|
||||
StreamSettings *internet.StreamSettings
|
||||
Settings []byte
|
||||
Protocol string
|
||||
PortRange v2net.PortRange
|
||||
ListenOn v2net.Address
|
||||
Tag string
|
||||
Allocation *InboundDetourAllocationConfig
|
||||
StreamSettings *internet.StreamSettings
|
||||
Settings []byte
|
||||
AllowPassiveConnection bool
|
||||
}
|
||||
|
||||
type OutboundDetourConfig struct {
|
||||
@@ -80,7 +82,7 @@ var (
|
||||
|
||||
func LoadConfig(init string) (*Config, error) {
|
||||
if configLoader == nil {
|
||||
return nil, ErrorBadConfiguration
|
||||
return nil, ErrBadConfiguration
|
||||
}
|
||||
return configLoader(init)
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ func (this *InboundConnectionConfig) UnmarshalJSON(data []byte) error {
|
||||
Protocol string `json:"protocol"`
|
||||
StreamSetting *internet.StreamSettings `json:"streamSettings"`
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
AllowPassive bool `json:"allowPassive"`
|
||||
}
|
||||
|
||||
jsonConfig := new(JsonConfig)
|
||||
@@ -80,7 +81,7 @@ func (this *InboundConnectionConfig) UnmarshalJSON(data []byte) error {
|
||||
this.Port = v2net.Port(jsonConfig.Port)
|
||||
this.ListenOn = v2net.AnyIP
|
||||
if jsonConfig.Listen != nil {
|
||||
if jsonConfig.Listen.Address.IsDomain() {
|
||||
if jsonConfig.Listen.Address.Family().IsDomain() {
|
||||
return errors.New("Point: Unable to listen on domain address: " + jsonConfig.Listen.Address.Domain())
|
||||
}
|
||||
this.ListenOn = jsonConfig.Listen.Address
|
||||
@@ -91,6 +92,7 @@ func (this *InboundConnectionConfig) UnmarshalJSON(data []byte) error {
|
||||
|
||||
this.Protocol = jsonConfig.Protocol
|
||||
this.Settings = jsonConfig.Settings
|
||||
this.AllowPassiveConnection = jsonConfig.AllowPassive
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -110,7 +112,7 @@ func (this *OutboundConnectionConfig) UnmarshalJSON(data []byte) error {
|
||||
|
||||
if jsonConfig.SendThrough != nil {
|
||||
address := jsonConfig.SendThrough.Address
|
||||
if address.IsDomain() {
|
||||
if address.Family().IsDomain() {
|
||||
return errors.New("Point: Unable to send through: " + address.String())
|
||||
}
|
||||
this.SendThrough = address
|
||||
@@ -186,6 +188,7 @@ func (this *InboundDetourConfig) UnmarshalJSON(data []byte) error {
|
||||
Tag string `json:"tag"`
|
||||
Allocation *InboundDetourAllocationConfig `json:"allocate"`
|
||||
StreamSetting *internet.StreamSettings `json:"streamSettings"`
|
||||
AllowPassive bool `json:"allowPassive"`
|
||||
}
|
||||
jsonConfig := new(JsonInboundDetourConfig)
|
||||
if err := json.Unmarshal(data, jsonConfig); err != nil {
|
||||
@@ -193,11 +196,11 @@ func (this *InboundDetourConfig) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
if jsonConfig.PortRange == nil {
|
||||
log.Error("Point: Port range not specified in InboundDetour.")
|
||||
return ErrorBadConfiguration
|
||||
return ErrBadConfiguration
|
||||
}
|
||||
this.ListenOn = v2net.AnyIP
|
||||
if jsonConfig.ListenOn != nil {
|
||||
if jsonConfig.ListenOn.Address.IsDomain() {
|
||||
if jsonConfig.ListenOn.Address.Family().IsDomain() {
|
||||
return errors.New("Point: Unable to listen on domain address: " + jsonConfig.ListenOn.Address.Domain())
|
||||
}
|
||||
this.ListenOn = jsonConfig.ListenOn.Address
|
||||
@@ -216,6 +219,7 @@ func (this *InboundDetourConfig) UnmarshalJSON(data []byte) error {
|
||||
if jsonConfig.StreamSetting != nil {
|
||||
this.StreamSettings = jsonConfig.StreamSetting
|
||||
}
|
||||
this.AllowPassiveConnection = jsonConfig.AllowPassive
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -237,7 +241,7 @@ func (this *OutboundDetourConfig) UnmarshalJSON(data []byte) error {
|
||||
|
||||
if jsonConfig.SendThrough != nil {
|
||||
address := jsonConfig.SendThrough.Address
|
||||
if address.IsDomain() {
|
||||
if address.Family().IsDomain() {
|
||||
return errors.New("Point: Unable to send through: " + address.String())
|
||||
}
|
||||
this.SendThrough = address
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestClientSampleConfig(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
GOPATH := os.Getenv("GOPATH")
|
||||
baseDir := filepath.Join(GOPATH, "src", "github.com", "v2ray", "v2ray-core", "release", "config")
|
||||
baseDir := filepath.Join(GOPATH, "src", "github.com", "v2ray", "v2ray-core", "tools", "release", "config")
|
||||
|
||||
pointConfig, err := LoadConfig(filepath.Join(baseDir, "vpoint_socks_vmess.json"))
|
||||
assert.Error(err).IsNil()
|
||||
@@ -38,7 +38,7 @@ func TestServerSampleConfig(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
GOPATH := os.Getenv("GOPATH")
|
||||
baseDir := filepath.Join(GOPATH, "src", "github.com", "v2ray", "v2ray-core", "release", "config")
|
||||
baseDir := filepath.Join(GOPATH, "src", "github.com", "v2ray", "v2ray-core", "tools", "release", "config")
|
||||
|
||||
pointConfig, err := LoadConfig(filepath.Join(baseDir, "vpoint_vmess_freedom.json"))
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
@@ -5,5 +5,5 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrorBadConfiguration = errors.New("Bad configuration.")
|
||||
ErrBadConfiguration = errors.New("Bad configuration.")
|
||||
)
|
||||
|
||||
@@ -26,10 +26,12 @@ func NewInboundDetourHandlerAlways(space app.Space, config *InboundDetourConfig)
|
||||
for i := ports.From; i <= ports.To; i++ {
|
||||
ichConfig := config.Settings
|
||||
ich, err := proxyrepo.CreateInboundHandler(config.Protocol, space, ichConfig, &proxy.InboundHandlerMeta{
|
||||
Address: config.ListenOn,
|
||||
Port: i,
|
||||
Tag: config.Tag,
|
||||
StreamSettings: config.StreamSettings})
|
||||
Address: config.ListenOn,
|
||||
Port: i,
|
||||
Tag: config.Tag,
|
||||
StreamSettings: config.StreamSettings,
|
||||
AllowPassiveConnection: config.AllowPassiveConnection,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Failed to create inbound connection handler: ", err)
|
||||
return nil, err
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/common/retry"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
proxyrepo "github.com/v2ray/v2ray-core/proxy/repo"
|
||||
)
|
||||
@@ -32,10 +33,12 @@ func NewInboundDetourHandlerDynamic(space app.Space, config *InboundDetourConfig
|
||||
|
||||
// To test configuration
|
||||
ich, err := proxyrepo.CreateInboundHandler(config.Protocol, space, config.Settings, &proxy.InboundHandlerMeta{
|
||||
Address: config.ListenOn,
|
||||
Port: 0,
|
||||
Tag: config.Tag,
|
||||
StreamSettings: config.StreamSettings})
|
||||
Address: config.ListenOn,
|
||||
Port: 0,
|
||||
Tag: config.Tag,
|
||||
StreamSettings: config.StreamSettings,
|
||||
AllowPassiveConnection: config.AllowPassiveConnection,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Point: Failed to create inbound connection handler: ", err)
|
||||
return nil, err
|
||||
@@ -97,21 +100,28 @@ func (this *InboundDetourHandlerDynamic) refresh() error {
|
||||
this.ich2Recyle = this.ichs
|
||||
newIchs := make([]proxy.InboundHandler, config.Allocation.Concurrency)
|
||||
|
||||
for idx, _ := range newIchs {
|
||||
port := this.pickUnusedPort()
|
||||
ich, err := proxyrepo.CreateInboundHandler(config.Protocol, this.space, config.Settings, &proxy.InboundHandlerMeta{
|
||||
Address: config.ListenOn, Port: port, Tag: config.Tag, StreamSettings: config.StreamSettings})
|
||||
for idx := range newIchs {
|
||||
err := retry.Timed(5, 100).On(func() error {
|
||||
port := this.pickUnusedPort()
|
||||
ich, err := proxyrepo.CreateInboundHandler(config.Protocol, this.space, config.Settings, &proxy.InboundHandlerMeta{
|
||||
Address: config.ListenOn, Port: port, Tag: config.Tag, StreamSettings: config.StreamSettings})
|
||||
if err != nil {
|
||||
delete(this.portsInUse, port)
|
||||
return err
|
||||
}
|
||||
err = ich.Start()
|
||||
if err != nil {
|
||||
delete(this.portsInUse, port)
|
||||
return err
|
||||
}
|
||||
this.portsInUse[port] = true
|
||||
newIchs[idx] = ich
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Point: Failed to create inbound connection handler: ", err)
|
||||
return err
|
||||
}
|
||||
err = ich.Start()
|
||||
if err != nil {
|
||||
log.Error("Point: Failed to start inbound connection handler: ", err)
|
||||
return err
|
||||
}
|
||||
this.portsInUse[port] = true
|
||||
newIchs[idx] = ich
|
||||
}
|
||||
|
||||
this.Lock()
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
_ "github.com/v2ray/v2ray-core/app/router/rules"
|
||||
@@ -25,6 +26,10 @@ import (
|
||||
_ "github.com/v2ray/v2ray-core/transport/internet/kcp"
|
||||
_ "github.com/v2ray/v2ray-core/transport/internet/tcp"
|
||||
_ "github.com/v2ray/v2ray-core/transport/internet/udp"
|
||||
|
||||
_ "github.com/v2ray/v2ray-core/transport/internet/authenticators/noop"
|
||||
_ "github.com/v2ray/v2ray-core/transport/internet/authenticators/srtp"
|
||||
_ "github.com/v2ray/v2ray-core/transport/internet/authenticators/utp"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -103,7 +108,7 @@ func main() {
|
||||
|
||||
if point := startV2Ray(); point != nil {
|
||||
osSignals := make(chan os.Signal, 1)
|
||||
signal.Notify(osSignals, os.Interrupt, os.Kill)
|
||||
signal.Notify(osSignals, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
|
||||
<-osSignals
|
||||
point.Close()
|
||||
11
shell/point/main/main_test.go
Normal file
11
shell/point/main/main_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// +build coveragemain
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRunMainForCoverage(t *testing.T) {
|
||||
main()
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func NewPoint(pConfig *Config) (*Point, error) {
|
||||
r, err := router.CreateRouter(routerConfig.Strategy, routerConfig.Settings, vpoint.space)
|
||||
if err != nil {
|
||||
log.Error("Failed to create router: ", err)
|
||||
return nil, ErrorBadConfiguration
|
||||
return nil, ErrBadConfiguration
|
||||
}
|
||||
vpoint.space.BindApp(router.APP_ID, r)
|
||||
vpoint.router = r
|
||||
@@ -93,10 +93,12 @@ func NewPoint(pConfig *Config) (*Point, error) {
|
||||
ichConfig := pConfig.InboundConfig.Settings
|
||||
ich, err := proxyrepo.CreateInboundHandler(
|
||||
pConfig.InboundConfig.Protocol, vpoint.space, ichConfig, &proxy.InboundHandlerMeta{
|
||||
Tag: "system.inbound",
|
||||
Address: pConfig.InboundConfig.ListenOn,
|
||||
Port: vpoint.port,
|
||||
StreamSettings: pConfig.InboundConfig.StreamSettings})
|
||||
Tag: "system.inbound",
|
||||
Address: pConfig.InboundConfig.ListenOn,
|
||||
Port: vpoint.port,
|
||||
StreamSettings: pConfig.InboundConfig.StreamSettings,
|
||||
AllowPassiveConnection: pConfig.InboundConfig.AllowPassiveConnection,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Failed to create inbound connection handler: ", err)
|
||||
return nil, err
|
||||
@@ -129,19 +131,19 @@ func NewPoint(pConfig *Config) (*Point, error) {
|
||||
dh, err := NewInboundDetourHandlerAlways(vpoint.space, detourConfig)
|
||||
if err != nil {
|
||||
log.Error("Point: Failed to create detour handler: ", err)
|
||||
return nil, ErrorBadConfiguration
|
||||
return nil, ErrBadConfiguration
|
||||
}
|
||||
detourHandler = dh
|
||||
case AllocationStrategyRandom:
|
||||
dh, err := NewInboundDetourHandlerDynamic(vpoint.space, detourConfig)
|
||||
if err != nil {
|
||||
log.Error("Point: Failed to create detour handler: ", err)
|
||||
return nil, ErrorBadConfiguration
|
||||
return nil, ErrBadConfiguration
|
||||
}
|
||||
detourHandler = dh
|
||||
default:
|
||||
log.Error("Point: Unknown allocation strategy: ", allocConfig.Strategy)
|
||||
return nil, ErrorBadConfiguration
|
||||
return nil, ErrBadConfiguration
|
||||
}
|
||||
vpoint.idh[idx] = detourHandler
|
||||
if len(detourConfig.Tag) > 0 {
|
||||
@@ -188,7 +190,7 @@ func (this *Point) Close() {
|
||||
func (this *Point) Start() error {
|
||||
if this.port <= 0 {
|
||||
log.Error("Point: Invalid port ", this.port)
|
||||
return ErrorBadConfiguration
|
||||
return ErrBadConfiguration
|
||||
}
|
||||
|
||||
err := retry.Timed(100 /* times */, 100 /* ms */).On(func() error {
|
||||
|
||||
@@ -44,37 +44,37 @@ func (subject *AddressSubject) EqualsString(another string) {
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) IsIPv4() {
|
||||
if !subject.value.IsIPv4() {
|
||||
if !subject.value.Family().IsIPv4() {
|
||||
subject.Fail("is", "an IPv4 address")
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) IsNotIPv4() {
|
||||
if subject.value.IsIPv4() {
|
||||
if subject.value.Family().IsIPv4() {
|
||||
subject.Fail("is not", "an IPv4 address")
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) IsIPv6() {
|
||||
if !subject.value.IsIPv6() {
|
||||
if !subject.value.Family().IsIPv6() {
|
||||
subject.Fail("is", "an IPv6 address")
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) IsNotIPv6() {
|
||||
if subject.value.IsIPv6() {
|
||||
if subject.value.Family().IsIPv6() {
|
||||
subject.Fail("is not", "an IPv6 address")
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) IsDomain() {
|
||||
if !subject.value.IsDomain() {
|
||||
if !subject.value.Family().IsDomain() {
|
||||
subject.Fail("is", "a domain address")
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *AddressSubject) IsNotDomain() {
|
||||
if subject.value.IsDomain() {
|
||||
if subject.value.Family().IsDomain() {
|
||||
subject.Fail("is not", "a domain address")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
FAIL=0
|
||||
COVERAGE_FILE=coverage.txt
|
||||
|
||||
V2RAY_OUT=${GOPATH}/out/v2ray
|
||||
V2RAY_COV=${V2RAY_OUT}/cov
|
||||
COVERAGE_FILE=${V2RAY_COV}/coverage.txt
|
||||
|
||||
function test_package {
|
||||
DIR="github.com/v2ray/v2ray-core/$1"
|
||||
DEP=$(go list -f '{{ join .Deps "\n" }}' $DIR | grep v2ray | tr '\n' ',')
|
||||
DEP=${DEP}$DIR
|
||||
go test -tags json -coverprofile=coversingle.out -coverpkg=$DEP $DIR || FAIL=1
|
||||
if [ -f coversingle.out ]; then
|
||||
cat coversingle.out | grep -v "mode: set" >> ${COVERAGE_FILE}
|
||||
rm coversingle.out
|
||||
fi
|
||||
RND_NAME=$(openssl rand -hex 16)
|
||||
COV_PROFILE=${V2RAY_COV}/${RND_NAME}.out
|
||||
go test -tags "json coverage" -coverprofile=${COV_PROFILE} -coverpkg=$DEP $DIR || FAIL=1
|
||||
}
|
||||
|
||||
rm -rf ${V2RAY_OUT}
|
||||
mkdir -p ${V2RAY_COV}
|
||||
touch ${COVERAGE_FILE}
|
||||
|
||||
TEST_FILES=(./*_test.go)
|
||||
@@ -28,14 +31,17 @@ for DIR in $(find * -type d -not -path "*.git*"); do
|
||||
fi
|
||||
done
|
||||
|
||||
cat ${COVERAGE_FILE} | sort -t: -k1 | grep -vw "testing" > coverallsorted.out
|
||||
echo "mode: set" | cat - coverallsorted.out > ${COVERAGE_FILE}
|
||||
rm coverallsorted.out
|
||||
for OUT_FILE in $(find ${V2RAY_COV} -name "*.out"); do
|
||||
echo "Merging file ${OUT_FILE}"
|
||||
cat ${OUT_FILE} | grep -v "mode: set" >> ${COVERAGE_FILE}
|
||||
done
|
||||
|
||||
COV_SORTED=${V2RAY_COV}/coverallsorted.out
|
||||
cat ${COVERAGE_FILE} | sort -t: -k1 | grep -vw "testing" > ${COV_SORTED}
|
||||
echo "mode: set" | cat - ${COV_SORTED} > ${COVERAGE_FILE}
|
||||
|
||||
if [ "$FAIL" -eq 0 ]; then
|
||||
bash <(curl -s https://codecov.io/bash) -f ${COVERAGE_FILE} || echo "Codecov did not collect coverage reports."
|
||||
fi
|
||||
|
||||
rm -f ${COVERAGE_FILE}
|
||||
|
||||
exit $FAIL
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package scenarios
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -24,25 +23,18 @@ import (
|
||||
|
||||
var (
|
||||
runningServers = make([]*exec.Cmd, 0, 10)
|
||||
|
||||
binaryPath string
|
||||
)
|
||||
|
||||
func BuildV2Ray() error {
|
||||
if len(binaryPath) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir("", "v2ray")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
binaryPath = filepath.Join(dir, "v2ray")
|
||||
func GetTestBinaryPath() string {
|
||||
file := filepath.Join(os.Getenv("GOPATH"), "out", "v2ray", "v2ray.test")
|
||||
if runtime.GOOS == "windows" {
|
||||
binaryPath += ".exe"
|
||||
file += ".exe"
|
||||
}
|
||||
cmd := exec.Command("go", "build", "-tags=json", "-o="+binaryPath, filepath.Join("github.com", "v2ray", "v2ray-core", "release", "server"))
|
||||
return cmd.Run()
|
||||
return file
|
||||
}
|
||||
|
||||
func GetSourcePath() string {
|
||||
return filepath.Join("github.com", "v2ray", "v2ray-core", "shell", "point", "main")
|
||||
}
|
||||
|
||||
func TestFile(filename string) string {
|
||||
@@ -73,9 +65,7 @@ func InitializeServer(configFile string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
proc := exec.Command(binaryPath, "-config="+configFile)
|
||||
proc.Stderr = os.Stderr
|
||||
proc.Stdout = os.Stdout
|
||||
proc := RunV2Ray(configFile)
|
||||
|
||||
err = proc.Start()
|
||||
if err != nil {
|
||||
@@ -92,7 +82,8 @@ func InitializeServer(configFile string) error {
|
||||
func CloseAllServers() {
|
||||
log.Info("Closing all servers.")
|
||||
for _, server := range runningServers {
|
||||
server.Process.Kill()
|
||||
server.Process.Signal(os.Interrupt)
|
||||
server.Process.Wait()
|
||||
}
|
||||
runningServers = make([]*exec.Cmd, 0, 10)
|
||||
log.Info("All server closed.")
|
||||
|
||||
33
testing/scenarios/server_env_coverage.go
Normal file
33
testing/scenarios/server_env_coverage.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// +build coverage
|
||||
|
||||
package scenarios
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
)
|
||||
|
||||
func BuildV2Ray() error {
|
||||
binaryPath := GetTestBinaryPath()
|
||||
if _, err := os.Stat(binaryPath); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "test", "-tags", "json coverage coveragemain", "-coverpkg", "github.com/v2ray/v2ray-core/...", "-c", "-o", binaryPath, GetSourcePath())
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func RunV2Ray(configFile string) *exec.Cmd {
|
||||
binaryPath := GetTestBinaryPath()
|
||||
|
||||
covDir := filepath.Join(os.Getenv("GOPATH"), "out", "v2ray", "cov")
|
||||
profile := uuid.New().String() + ".out"
|
||||
proc := exec.Command(binaryPath, "-config", configFile, "-test.run", "TestRunMainForCoverage", "-test.coverprofile", profile, "-test.outputdir", covDir)
|
||||
proc.Stderr = os.Stderr
|
||||
proc.Stdout = os.Stdout
|
||||
|
||||
return proc
|
||||
}
|
||||
27
testing/scenarios/server_env_regular.go
Normal file
27
testing/scenarios/server_env_regular.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// +build !coverage
|
||||
|
||||
package scenarios
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func BuildV2Ray() error {
|
||||
binaryPath := GetTestBinaryPath()
|
||||
if _, err := os.Stat(binaryPath); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "build", "-tags=json", "-o="+binaryPath, GetSourcePath())
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func RunV2Ray(configFile string) *exec.Cmd {
|
||||
binaryPath := GetTestBinaryPath()
|
||||
proc := exec.Command(binaryPath, "-config", configFile)
|
||||
proc.Stderr = os.Stderr
|
||||
proc.Stdout = os.Stdout
|
||||
|
||||
return proc
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user