You've already forked v2ray-core
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb16047cf0 | ||
|
|
823cbf1509 | ||
|
|
b319704282 | ||
|
|
5d5102de4c | ||
|
|
ad613a9e3e | ||
|
|
b22f306a3d | ||
|
|
521db6bdb4 | ||
|
|
07d4495b27 | ||
|
|
a887fd01b8 | ||
|
|
d03cac9670 | ||
|
|
b40be74b86 | ||
|
|
075753c030 | ||
|
|
5eee1b97aa | ||
|
|
86315633e1 | ||
|
|
2c0096c20a | ||
|
|
76200e2764 | ||
|
|
ab723fa0b5 | ||
|
|
46abe9de5e | ||
|
|
3734a25cf2 | ||
|
|
5b63edbdbc | ||
|
|
fe719cb03d | ||
|
|
450fef27f6 | ||
|
|
8be5d695c8 | ||
|
|
729312f3b5 | ||
|
|
76cb92f918 | ||
|
|
c09ca34ada | ||
|
|
e2d4d44c77 | ||
|
|
1b24f2f9ae | ||
|
|
0c2fd04f0e | ||
|
|
f39d6077b6 | ||
|
|
33fa7dbd3c | ||
|
|
f27e8308cb |
@@ -2,7 +2,6 @@ language: go
|
||||
|
||||
go:
|
||||
- 1.5
|
||||
- tip
|
||||
|
||||
before_install:
|
||||
- go get github.com/axw/gocov/gocov
|
||||
@@ -15,12 +14,13 @@ script:
|
||||
env:
|
||||
secure: "nnEiGTuS8SsSFRpxUsPh6hmx/OfPDA6AO3OX7IpjPCbkQnjuFEsm3DfPEZOHTiYra00Ea3bNw9ePLPqyWdLAPmcLoUBhWRyfevt9m3jrbyrT8O9MCXNDY6i1ll3E43nx8kxRDF1QbYaSK4jppa8Vu2vyZH5+GiS0JVcw/7CfX8TyUMbDuyaQXYQ1kwixlThQZH43xxQ7u5CFF1pBFdwpkuDm2/wKUE+0CGZOeWzUYI8h1in4eY0Vse4Zt4GqS2eywf4rw12bBysZ21UnT/xu990m5nzAScbUGjloplJNpdZGtD8SBTGujZBMMUzKi/E0zJkdEf37MZ9rsarfv6v9khdwTp/8VoAiZxpG1IDISIu0TClAjw8NYO5hmuF01/oJexMuxqRhMPwbk1nPsustlBKEG3zjvW5yILMkiaRl67Pj+9h21zMGYmwPGka7Ollh9ki2h17RFNuj9Yxm5XX2xvZDIvtxL1a8S50vv7VZTT9rrrFMhGP7ty9gj2opjJ7QBgwi2W/eVGhfshfnZJjJ7Bqf+gl1MDOYcTlqmFe5JvKdIO9mN7gskJizmeFxh31LrZ2sCY71/iicGj7gZJ4e/u2/dI2OVfFWTTbHoiIyCLvvrA18zBF9Sky1Y3QDj85GdRtC0SI6TXJLAIa7jNgF6Nr2mAg044DaX9VW0VH0VnE="
|
||||
|
||||
before_deploy: "./release/release.sh"
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: "Nbno1ibFpq0DC6y0mkN6NFTbDQinRAEQo2Jl5lvsbiaX9m4CK59xG8XOcmZ3mqqdeioj6m9X5BVe2UtCZjmXmnyPq0FYNFHAPTfd2VrcOpi0fWLD9ndTYAsqjxrqPGK62XVjPbG8U2pGdq2yCOb0qQ9DlahDhVIMzQ/GYuRDps25fPGftepdPwlrwOfYCIvAHoi3GEozuS67WNEx/YtmZTce/2Qebdmwf44hUNaQibeXEspu9wQcp9M/i7aTuX/tUbn7rVCQ4TfoIJU1C1qKedUw0QYMjqP08Wn6UjwwhbblsJKFv8Kc0nx+i2K9CvvLyaBBIVY2Ggxj4Niy+X4VbyWWzdKx+0FXlmKh3U/hJdMZU3OrnwliBns2X3tCHSq7/+7ynLs3UZ4wCkCl7rQYucJ2mlvGquohQV6MX2vjl73y+pYljf+PKYbSDBb7iUOjllisRD+HmDmGQl81BvdEpN/K/BlfsTwHoHxsALSTjRljSngH51SUvjnaVbi/JolJ3lomTr1vCHl4WUiQVLnh7O2Cg7QLNMNieoblnh9yDQG9wC4xuzOJ/AaxRd/pRZMeIooQ1zsHOe+47C1YAep88b+9xtRqVMozAjk/XgNzWjuT2R8G7lZDq0mL9VajD/NUjM3Yrswoft9A3DRmR+lIFJR+fJb8tNf+k1OczE4lesg="
|
||||
file: "$GOPATH/bin/v2ray-latest.zip"
|
||||
file: "$GOPATH/bin/v2ray.zip"
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
before_deploy: "./release/release.sh"
|
||||
|
||||
30
README.md
30
README.md
@@ -3,6 +3,9 @@
|
||||
[](https://travis-ci.org/v2ray/v2ray-core)
|
||||
[](https://coveralls.io/github/v2ray/v2ray-core?branch=master)
|
||||
[](https://godoc.org/github.com/v2ray/v2ray-core)
|
||||

|
||||
|
||||
[中文](https://github.com/V2Ray/v2ray-core/blob/master/README.md) | [English](https://github.com/V2Ray/v2ray-core/blob/master/spec/en/README.md)
|
||||
|
||||
V2Ray 是一个翻墙工具包,用于简化和复用其它翻墙工具,加速二次开发。
|
||||
|
||||
@@ -13,29 +16,20 @@ V2Ray 是一个翻墙工具包,用于简化和复用其它翻墙工具,加
|
||||
* 支持多用户
|
||||
* 开放协议支持,兼容 ShadowSocks 和 GoAgent
|
||||
|
||||
## 简明教程
|
||||
[链接](https://github.com/V2Ray/v2ray-core/blob/master/spec/guide.md)
|
||||
## 使用说明
|
||||
* [简明教程](https://github.com/V2Ray/v2ray-core/blob/master/spec/guide.md)
|
||||
* [建议或意见](https://github.com/v2ray/v2ray-core/issues)
|
||||
* [Issue 指引](https://github.com/V2Ray/v2ray-core/blob/master/spec/issue.md)
|
||||
* [当前状态](https://github.com/V2Ray/v2ray-core/blob/master/spec/status.md)
|
||||
|
||||
## 概要设计
|
||||
[链接](https://github.com/V2Ray/v2ray-core/blob/master/spec/design.md)
|
||||
## 开发人员相关
|
||||
* [概要设计](https://github.com/V2Ray/v2ray-core/blob/master/spec/design.md)
|
||||
* [开发计划](https://github.com/V2Ray/v2ray-core/blob/master/spec/roadmap.md)
|
||||
* [Help Wanted](https://github.com/v2ray/v2ray-core/labels/help%20wanted):所有被标记为“Help Wanted”的 Issue 都接受 Pull Request,如果你对本项目感兴趣并想做点贡献,请挑选其中之一完善之,不甚感激。
|
||||
|
||||
## 联系方式
|
||||
公开的建议或意见请发 [Issue](https://github.com/v2ray/v2ray-core/issues),发 Issue 之前请先阅读 [Issue 指引](https://github.com/V2Ray/v2ray-core/blob/master/spec/issue.md)。
|
||||
|
||||
私下联系:love@v2ray.com
|
||||
|
||||
## 合作机会
|
||||
所有被标记为“[Help Wanted](https://github.com/v2ray/v2ray-core/labels/help%20wanted)”的 Issue 都接受 Pull Request,如果你对本项目感兴趣并想做点贡献,请挑选其中之一完善之,不甚感激。
|
||||
|
||||
## 开发日程
|
||||
|
||||
* 2015.11 **1.0** 完成,单服务器模式,可独立运行
|
||||
* 2016.01 **1.5** 完成,兼容 ShadowSocks 协议
|
||||
* 2016.04 **2.0** 完成,多服务器模式
|
||||
|
||||
## 进展
|
||||
[当前状态](https://github.com/V2Ray/v2ray-core/blob/master/spec/status.md):Version 1.0 Alpha,[安装方式](https://github.com/V2Ray/v2ray-core/blob/master/spec/install.md)。理论上可用,简单测试通过,还未进行稳定性和性能测试。
|
||||
|
||||
## 捐赠
|
||||
目前 V2Ray 还在早期开发阶段,暂时没什么可用性,也谈不上捐赠。如果你执意想捐赠,请发送 Amazon Gift Card 至 donate@v2ray.com
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"crypto/cipher"
|
||||
"io"
|
||||
|
||||
"github.com/v2ray/v2ray-core/log"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
)
|
||||
|
||||
// CryptionReader is a general purpose reader that applies
|
||||
144
common/net/address.go
Normal file
144
common/net/address.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
)
|
||||
|
||||
type Address interface {
|
||||
IP() net.IP
|
||||
Domain() string
|
||||
Port() uint16
|
||||
PortBytes() []byte
|
||||
|
||||
IsIPv4() bool
|
||||
IsIPv6() bool
|
||||
IsDomain() bool
|
||||
|
||||
String() string
|
||||
}
|
||||
|
||||
func IPAddress(ip []byte, port uint16) Address {
|
||||
switch len(ip) {
|
||||
case net.IPv4len:
|
||||
return IPv4Address{
|
||||
PortAddress: PortAddress{port: port},
|
||||
ip: [4]byte{ip[0], ip[1], ip[2], ip[3]},
|
||||
}
|
||||
case net.IPv6len:
|
||||
return IPv6Address{
|
||||
PortAddress: PortAddress{port: port},
|
||||
ip: [16]byte{ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15]},
|
||||
}
|
||||
default:
|
||||
panic(log.Error("Unknown IP format: %v", ip))
|
||||
}
|
||||
}
|
||||
|
||||
func DomainAddress(domain string, port uint16) Address {
|
||||
return DomainAddressImpl{
|
||||
domain: domain,
|
||||
PortAddress: PortAddress{port: port},
|
||||
}
|
||||
}
|
||||
|
||||
type PortAddress struct {
|
||||
port uint16
|
||||
}
|
||||
|
||||
func (addr PortAddress) Port() uint16 {
|
||||
return addr.port
|
||||
}
|
||||
|
||||
func (addr PortAddress) PortBytes() []byte {
|
||||
return []byte{byte(addr.port >> 8), byte(addr.port)}
|
||||
}
|
||||
|
||||
type IPv4Address struct {
|
||||
PortAddress
|
||||
ip [4]byte
|
||||
}
|
||||
|
||||
func (addr IPv4Address) IP() net.IP {
|
||||
return net.IP(addr.ip[:])
|
||||
}
|
||||
|
||||
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) String() string {
|
||||
return addr.IP().String() + ":" + strconv.Itoa(int(addr.PortAddress.port))
|
||||
}
|
||||
|
||||
type IPv6Address struct {
|
||||
PortAddress
|
||||
ip [16]byte
|
||||
}
|
||||
|
||||
func (addr IPv6Address) IP() net.IP {
|
||||
return net.IP(addr.ip[:])
|
||||
}
|
||||
|
||||
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 (addr IPv6Address) String() string {
|
||||
return "[" + addr.IP().String() + "]:" + strconv.Itoa(int(addr.PortAddress.port))
|
||||
}
|
||||
|
||||
type DomainAddressImpl struct {
|
||||
PortAddress
|
||||
domain string
|
||||
}
|
||||
|
||||
func (addr DomainAddressImpl) IP() net.IP {
|
||||
panic("Calling IP() on a DomainAddress.")
|
||||
}
|
||||
|
||||
func (addr DomainAddressImpl) Domain() string {
|
||||
return addr.domain
|
||||
}
|
||||
|
||||
func (addr DomainAddressImpl) IsIPv4() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (addr DomainAddressImpl) IsIPv6() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (addr DomainAddressImpl) IsDomain() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (addr DomainAddressImpl) String() string {
|
||||
return addr.domain + ":" + strconv.Itoa(int(addr.PortAddress.port))
|
||||
}
|
||||
@@ -13,10 +13,9 @@ func TestIPv4Address(t *testing.T) {
|
||||
port := uint16(80)
|
||||
addr := IPAddress(ip, port)
|
||||
|
||||
assert.Byte(addr.Type).Equals(AddrTypeIP)
|
||||
assert.Bool(addr.IsIPv4()).IsTrue()
|
||||
assert.Bytes(addr.IP).Equals(ip)
|
||||
assert.Uint16(addr.Port).Equals(port)
|
||||
assert.Bytes(addr.IP()).Equals(ip)
|
||||
assert.Uint16(addr.Port()).Equals(port)
|
||||
assert.String(addr.String()).Equals("1.2.3.4:80")
|
||||
}
|
||||
|
||||
@@ -32,10 +31,9 @@ func TestIPv6Address(t *testing.T) {
|
||||
port := uint16(443)
|
||||
addr := IPAddress(ip, port)
|
||||
|
||||
assert.Byte(addr.Type).Equals(AddrTypeIP)
|
||||
assert.Bool(addr.IsIPv6()).IsTrue()
|
||||
assert.Bytes(addr.IP).Equals(ip)
|
||||
assert.Uint16(addr.Port).Equals(port)
|
||||
assert.Bytes(addr.IP()).Equals(ip)
|
||||
assert.Uint16(addr.Port()).Equals(port)
|
||||
assert.String(addr.String()).Equals("[102:304:102:304:102:304:102:304]:443")
|
||||
}
|
||||
|
||||
@@ -46,9 +44,8 @@ func TestDomainAddress(t *testing.T) {
|
||||
port := uint16(443)
|
||||
addr := DomainAddress(domain, port)
|
||||
|
||||
assert.Byte(addr.Type).Equals(AddrTypeDomain)
|
||||
assert.Bool(addr.IsDomain()).IsTrue()
|
||||
assert.String(addr.Domain).Equals(domain)
|
||||
assert.Uint16(addr.Port).Equals(port)
|
||||
assert.String(addr.Domain()).Equals(domain)
|
||||
assert.Uint16(addr.Port()).Equals(port)
|
||||
assert.String(addr.String()).Equals("v2ray.com:443")
|
||||
}
|
||||
66
common/net/destination.go
Normal file
66
common/net/destination.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package net
|
||||
|
||||
type Destination interface {
|
||||
Network() string
|
||||
Address() Address
|
||||
String() string
|
||||
|
||||
IsTCP() bool
|
||||
IsUDP() bool
|
||||
}
|
||||
|
||||
func NewTCPDestination(address Address) Destination {
|
||||
return TCPDestination{address: address}
|
||||
}
|
||||
|
||||
func NewUDPDestination(address Address) Destination {
|
||||
return UDPDestination{address: address}
|
||||
}
|
||||
|
||||
type TCPDestination struct {
|
||||
address Address
|
||||
}
|
||||
|
||||
func (dest TCPDestination) Network() string {
|
||||
return "tcp"
|
||||
}
|
||||
|
||||
func (dest TCPDestination) Address() Address {
|
||||
return dest.address
|
||||
}
|
||||
|
||||
func (dest TCPDestination) String() string {
|
||||
return "tcp:" + dest.address.String()
|
||||
}
|
||||
|
||||
func (dest TCPDestination) IsTCP() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (dest TCPDestination) IsUDP() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type UDPDestination struct {
|
||||
address Address
|
||||
}
|
||||
|
||||
func (dest UDPDestination) Network() string {
|
||||
return "udp"
|
||||
}
|
||||
|
||||
func (dest UDPDestination) Address() Address {
|
||||
return dest.address
|
||||
}
|
||||
|
||||
func (dest UDPDestination) String() string {
|
||||
return "udp:" + dest.address.String()
|
||||
}
|
||||
|
||||
func (dest UDPDestination) IsTCP() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (dest UDPDestination) IsUDP() bool {
|
||||
return true
|
||||
}
|
||||
@@ -19,7 +19,6 @@ func ReaderToChan(stream chan<- []byte, reader io.Reader) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ChanToWriter(writer io.Writer, stream <-chan []byte) error {
|
||||
30
config.go
30
config.go
@@ -1,28 +1,12 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// User is the user account that is used for connection to a Point
|
||||
type User struct {
|
||||
Id ID `json:"id"` // The ID of this User.
|
||||
type ConnectionConfig interface {
|
||||
Protocol() string
|
||||
Content() []byte
|
||||
}
|
||||
|
||||
type ConnectionConfig struct {
|
||||
Protocol string `json:"protocol"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
|
||||
// Config is the config for Point server.
|
||||
type Config struct {
|
||||
Port uint16 `json:"port"` // Port of this Point server.
|
||||
InboundConfig ConnectionConfig `json:"inbound"`
|
||||
OutboundConfig ConnectionConfig `json:"outbound"`
|
||||
}
|
||||
|
||||
func LoadConfig(rawConfig []byte) (Config, error) {
|
||||
config := Config{}
|
||||
err := json.Unmarshal(rawConfig, &config)
|
||||
return config, err
|
||||
type PointConfig interface {
|
||||
Port() uint16
|
||||
InboundConfig() ConnectionConfig
|
||||
OutboundConfig() ConnectionConfig
|
||||
}
|
||||
|
||||
72
config/json/json.go
Normal file
72
config/json/json.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
)
|
||||
|
||||
type ConnectionConfig struct {
|
||||
ProtocolString string `json:"protocol"`
|
||||
File string `json:"file"`
|
||||
}
|
||||
|
||||
func (config *ConnectionConfig) Protocol() string {
|
||||
return config.ProtocolString
|
||||
}
|
||||
|
||||
func (config *ConnectionConfig) Content() []byte {
|
||||
if len(config.File) == 0 {
|
||||
return nil
|
||||
}
|
||||
content, err := ioutil.ReadFile(config.File)
|
||||
if err != nil {
|
||||
panic(log.Error("Failed to read config file (%s): %v", config.File, err))
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
// Config is the config for Point server.
|
||||
type Config struct {
|
||||
PortValue uint16 `json:"port"` // Port of this Point server.
|
||||
InboundConfigValue *ConnectionConfig `json:"inbound"`
|
||||
OutboundConfigValue *ConnectionConfig `json:"outbound"`
|
||||
}
|
||||
|
||||
func (config *Config) Port() uint16 {
|
||||
return config.PortValue
|
||||
}
|
||||
|
||||
func (config *Config) InboundConfig() core.ConnectionConfig {
|
||||
return config.InboundConfigValue
|
||||
}
|
||||
|
||||
func (config *Config) OutboundConfig() core.ConnectionConfig {
|
||||
return config.OutboundConfigValue
|
||||
}
|
||||
|
||||
func LoadConfig(file string) (*Config, error) {
|
||||
fixedFile := os.ExpandEnv(file)
|
||||
rawConfig, err := ioutil.ReadFile(fixedFile)
|
||||
if err != nil {
|
||||
log.Error("Failed to read point config file (%s): %v", file, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := &Config{}
|
||||
err = json.Unmarshal(rawConfig, config)
|
||||
|
||||
if !filepath.IsAbs(config.InboundConfigValue.File) && len(config.InboundConfigValue.File) > 0 {
|
||||
config.InboundConfigValue.File = filepath.Join(filepath.Dir(fixedFile), config.InboundConfigValue.File)
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(config.OutboundConfigValue.File) && len(config.OutboundConfigValue.File) > 0 {
|
||||
config.OutboundConfigValue.File = filepath.Join(filepath.Dir(fixedFile), config.OutboundConfigValue.File)
|
||||
}
|
||||
|
||||
return config, err
|
||||
}
|
||||
48
config/json/json_test.go
Normal file
48
config/json/json_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core/testing/unit"
|
||||
)
|
||||
|
||||
func TestClientSampleConfig(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
|
||||
// TODO: fix for Windows
|
||||
baseDir := "$GOPATH/src/github.com/v2ray/v2ray-core/release/config"
|
||||
|
||||
config, err := LoadConfig(filepath.Join(baseDir, "vpoint_socks_vmess.json"))
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
assert.Uint16(config.Port()).Positive()
|
||||
assert.Pointer(config.InboundConfig()).IsNotNil()
|
||||
assert.Pointer(config.OutboundConfig()).IsNotNil()
|
||||
|
||||
assert.String(config.InboundConfig().Protocol()).Equals("socks")
|
||||
assert.Int(len(config.InboundConfig().Content())).GreaterThan(0)
|
||||
|
||||
assert.String(config.OutboundConfig().Protocol()).Equals("vmess")
|
||||
assert.Int(len(config.OutboundConfig().Content())).GreaterThan(0)
|
||||
}
|
||||
|
||||
func TestServerSampleConfig(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
|
||||
// TODO: fix for Windows
|
||||
baseDir := "$GOPATH/src/github.com/v2ray/v2ray-core/release/config"
|
||||
|
||||
config, err := LoadConfig(filepath.Join(baseDir, "vpoint_vmess_freedom.json"))
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
assert.Uint16(config.Port()).Positive()
|
||||
assert.Pointer(config.InboundConfig()).IsNotNil()
|
||||
assert.Pointer(config.OutboundConfig()).IsNotNil()
|
||||
|
||||
assert.String(config.InboundConfig().Protocol()).Equals("vmess")
|
||||
assert.Int(len(config.InboundConfig().Content())).GreaterThan(0)
|
||||
|
||||
assert.String(config.OutboundConfig().Protocol()).Equals("freedom")
|
||||
assert.Int(len(config.OutboundConfig().Content())).Equals(0)
|
||||
}
|
||||
4
core.go
4
core.go
@@ -2,7 +2,7 @@
|
||||
package core
|
||||
|
||||
const (
|
||||
Version = "0.1"
|
||||
Version = "0.5"
|
||||
Codename = "Post Apocalypse"
|
||||
Intro = "A stable and unbreakable connection for every one."
|
||||
Intro = "A stable and unbreakable connection for everyone."
|
||||
)
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
AddrTypeIP = byte(0x01)
|
||||
AddrTypeDomain = byte(0x03)
|
||||
)
|
||||
|
||||
type Address struct {
|
||||
Type byte
|
||||
IP net.IP
|
||||
Domain string
|
||||
Port uint16
|
||||
}
|
||||
|
||||
func IPAddress(ip []byte, port uint16) Address {
|
||||
ipCopy := make([]byte, len(ip))
|
||||
copy(ipCopy, ip)
|
||||
// TODO: check IP length
|
||||
return Address{
|
||||
Type: AddrTypeIP,
|
||||
IP: net.IP(ipCopy),
|
||||
Domain: "",
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
func DomainAddress(domain string, port uint16) Address {
|
||||
return Address{
|
||||
Type: AddrTypeDomain,
|
||||
IP: nil,
|
||||
Domain: domain,
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
func (addr Address) IsIPv4() bool {
|
||||
return addr.Type == AddrTypeIP && len(addr.IP) == net.IPv4len
|
||||
}
|
||||
|
||||
func (addr Address) IsIPv6() bool {
|
||||
return addr.Type == AddrTypeIP && len(addr.IP) == net.IPv6len
|
||||
}
|
||||
|
||||
func (addr Address) IsDomain() bool {
|
||||
return addr.Type == AddrTypeDomain
|
||||
}
|
||||
|
||||
func (addr Address) String() string {
|
||||
var host string
|
||||
switch addr.Type {
|
||||
case AddrTypeIP:
|
||||
host = addr.IP.String()
|
||||
if len(addr.IP) == net.IPv6len {
|
||||
host = "[" + host + "]"
|
||||
}
|
||||
|
||||
case AddrTypeDomain:
|
||||
host = addr.Domain
|
||||
default:
|
||||
panic("Unknown Address Type " + strconv.Itoa(int(addr.Type)))
|
||||
}
|
||||
return host + ":" + strconv.Itoa(int(addr.Port))
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package socks
|
||||
|
||||
import (
|
||||
// "bytes"
|
||||
"testing"
|
||||
|
||||
// "github.com/v2ray/v2ray-core"
|
||||
// "github.com/v2ray/v2ray-core/testing/mocks"
|
||||
// "github.com/v2ray/v2ray-core/testing/unit"
|
||||
)
|
||||
|
||||
func TestSocksTcpConnect(t *testing.T) {
|
||||
t.Skip("Not ready yet.")
|
||||
/*
|
||||
assert := unit.Assert(t)
|
||||
|
||||
port := uint16(12384)
|
||||
|
||||
uuid := "2418d087-648d-4990-86e8-19dca1d006d3"
|
||||
id, err := core.UUIDToID(uuid)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
config := core.VConfig{
|
||||
port,
|
||||
[]core.User{core.User{id}},
|
||||
"",
|
||||
[]core.VNext{}}
|
||||
|
||||
och := new(mocks.FakeOutboundConnectionHandler)
|
||||
och.Data2Send = bytes.NewBuffer(make([]byte, 1024))
|
||||
och.Data2Return = []byte("The data to be returned to socks server.")
|
||||
|
||||
vpoint, err := core.NewPoint(&config, SocksServerFactory{}, och)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = vpoint.Start()
|
||||
assert.Error(err).IsNil()
|
||||
*/
|
||||
}
|
||||
38
point.go
38
point.go
@@ -1,10 +1,8 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/v2ray/v2ray-core/log"
|
||||
v2net "github.com/v2ray/v2ray-core/net"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -35,36 +33,24 @@ type Point struct {
|
||||
|
||||
// NewPoint returns a new Point server based on given configuration.
|
||||
// The server is not started at this point.
|
||||
func NewPoint(config Config) (*Point, error) {
|
||||
func NewPoint(config PointConfig) (*Point, error) {
|
||||
var vpoint = new(Point)
|
||||
vpoint.port = config.Port
|
||||
vpoint.port = config.Port()
|
||||
|
||||
ichFactory, ok := inboundFactories[config.InboundConfig.Protocol]
|
||||
ichFactory, ok := inboundFactories[config.InboundConfig().Protocol()]
|
||||
if !ok {
|
||||
panic(log.Error("Unknown inbound connection handler factory %s", config.InboundConfig.Protocol))
|
||||
panic(log.Error("Unknown inbound connection handler factory %s", config.InboundConfig().Protocol()))
|
||||
}
|
||||
vpoint.ichFactory = ichFactory
|
||||
if len(config.InboundConfig.File) > 0 {
|
||||
ichConfig, err := ioutil.ReadFile(config.InboundConfig.File)
|
||||
if err != nil {
|
||||
panic(log.Error("Unable to read config file %v", err))
|
||||
}
|
||||
vpoint.ichConfig = ichConfig
|
||||
}
|
||||
vpoint.ichConfig = config.InboundConfig().Content()
|
||||
|
||||
ochFactory, ok := outboundFactories[config.OutboundConfig.Protocol]
|
||||
ochFactory, ok := outboundFactories[config.OutboundConfig().Protocol()]
|
||||
if !ok {
|
||||
panic(log.Error("Unknown outbound connection handler factory %s", config.OutboundConfig.Protocol))
|
||||
panic(log.Error("Unknown outbound connection handler factory %s", config.OutboundConfig().Protocol))
|
||||
}
|
||||
|
||||
vpoint.ochFactory = ochFactory
|
||||
if len(config.OutboundConfig.File) > 0 {
|
||||
ochConfig, err := ioutil.ReadFile(config.OutboundConfig.File)
|
||||
if err != nil {
|
||||
panic(log.Error("Unable to read config file %v", err))
|
||||
}
|
||||
vpoint.ochConfig = ochConfig
|
||||
}
|
||||
vpoint.ochConfig = config.OutboundConfig().Content()
|
||||
|
||||
return vpoint, nil
|
||||
}
|
||||
@@ -78,7 +64,7 @@ type InboundConnectionHandler interface {
|
||||
}
|
||||
|
||||
type OutboundConnectionHandlerFactory interface {
|
||||
Create(VP *Point, config []byte, dest v2net.Address) (OutboundConnectionHandler, error)
|
||||
Create(VP *Point, config []byte, dest v2net.Destination) (OutboundConnectionHandler, error)
|
||||
}
|
||||
|
||||
type OutboundConnectionHandler interface {
|
||||
@@ -99,7 +85,7 @@ func (vp *Point) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vp *Point) NewInboundConnectionAccepted(destination v2net.Address) InboundRay {
|
||||
func (vp *Point) NewInboundConnectionAccepted(destination v2net.Destination) InboundRay {
|
||||
ray := NewRay()
|
||||
// TODO: handle error
|
||||
och, _ := vp.ochFactory.Create(vp, vp.ochConfig, destination)
|
||||
|
||||
@@ -4,15 +4,15 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/log"
|
||||
v2net "github.com/v2ray/v2ray-core/net"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
type FreedomConnection struct {
|
||||
dest v2net.Address
|
||||
dest v2net.Destination
|
||||
}
|
||||
|
||||
func NewFreedomConnection(dest v2net.Address) *FreedomConnection {
|
||||
func NewFreedomConnection(dest v2net.Destination) *FreedomConnection {
|
||||
return &FreedomConnection{
|
||||
dest: dest,
|
||||
}
|
||||
@@ -21,12 +21,12 @@ func NewFreedomConnection(dest v2net.Address) *FreedomConnection {
|
||||
func (vconn *FreedomConnection) Start(ray core.OutboundRay) error {
|
||||
input := ray.OutboundInput()
|
||||
output := ray.OutboundOutput()
|
||||
conn, err := net.Dial("tcp", vconn.dest.String())
|
||||
conn, err := net.Dial(vconn.dest.Network(), vconn.dest.Address().String())
|
||||
log.Info("Freedom: Opening connection to %s", vconn.dest.String())
|
||||
if err != nil {
|
||||
close(output)
|
||||
return log.Error("Freedom: Failed to open tcp connection: %s : %v", vconn.dest.String(), err)
|
||||
return log.Error("Freedom: Failed to open connection: %s : %v", vconn.dest.String(), err)
|
||||
}
|
||||
log.Info("Freedom: Sending outbound tcp: %s", vconn.dest.String())
|
||||
|
||||
readFinish := make(chan bool)
|
||||
writeFinish := make(chan bool)
|
||||
@@ -2,13 +2,13 @@ package freedom
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core"
|
||||
v2net "github.com/v2ray/v2ray-core/net"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
type FreedomFactory struct {
|
||||
}
|
||||
|
||||
func (factory FreedomFactory) Create(vp *core.Point, config []byte, dest v2net.Address) (core.OutboundConnectionHandler, error) {
|
||||
func (factory FreedomFactory) Create(vp *core.Point, config []byte, dest v2net.Destination) (core.OutboundConnectionHandler, error) {
|
||||
return NewFreedomConnection(dest), nil
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Package socks contains protocol definition and io lib for SOCKS5 protocol
|
||||
package socks
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
@@ -7,8 +6,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/v2ray/v2ray-core/log"
|
||||
v2net "github.com/v2ray/v2ray-core/net"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -279,17 +278,19 @@ func ReadRequest(reader io.Reader) (request *Socks5Request, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (request *Socks5Request) Destination() v2net.Address {
|
||||
func (request *Socks5Request) Destination() v2net.Destination {
|
||||
var address v2net.Address
|
||||
switch request.AddrType {
|
||||
case AddrTypeIPv4:
|
||||
return v2net.IPAddress(request.IPv4[:], request.Port)
|
||||
address = v2net.IPAddress(request.IPv4[:], request.Port)
|
||||
case AddrTypeIPv6:
|
||||
return v2net.IPAddress(request.IPv6[:], request.Port)
|
||||
address = v2net.IPAddress(request.IPv6[:], request.Port)
|
||||
case AddrTypeDomain:
|
||||
return v2net.DomainAddress(request.Domain, request.Port)
|
||||
address = v2net.DomainAddress(request.Domain, request.Port)
|
||||
default:
|
||||
panic("Unknown address type")
|
||||
}
|
||||
return v2net.NewTCPDestination(address)
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -1,4 +1,4 @@
|
||||
package socks
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -1,9 +1,9 @@
|
||||
package socks
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
v2net "github.com/v2ray/v2ray-core/net"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
type Socks5UDPRequest struct {
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
socksio "github.com/v2ray/v2ray-core/io/socks"
|
||||
"github.com/v2ray/v2ray-core/log"
|
||||
v2net "github.com/v2ray/v2ray-core/net"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
protocol "github.com/v2ray/v2ray-core/proxy/socks/protocol"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -64,37 +64,37 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
|
||||
|
||||
reader := connection.(io.Reader)
|
||||
|
||||
auth, auth4, err := socksio.ReadAuthentication(reader)
|
||||
if err != nil && err != socksio.ErrorSocksVersion4 {
|
||||
auth, auth4, err := protocol.ReadAuthentication(reader)
|
||||
if err != nil && err != protocol.ErrorSocksVersion4 {
|
||||
log.Error("Error on reading authentication: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
var dest v2net.Address
|
||||
var dest v2net.Destination
|
||||
|
||||
// TODO refactor this part
|
||||
if err == socksio.ErrorSocksVersion4 {
|
||||
result := socksio.Socks4RequestGranted
|
||||
if auth4.Command == socksio.CmdBind {
|
||||
result = socksio.Socks4RequestRejected
|
||||
if err == protocol.ErrorSocksVersion4 {
|
||||
result := protocol.Socks4RequestGranted
|
||||
if auth4.Command == protocol.CmdBind {
|
||||
result = protocol.Socks4RequestRejected
|
||||
}
|
||||
socks4Response := socksio.NewSocks4AuthenticationResponse(result, auth4.Port, auth4.IP[:])
|
||||
socksio.WriteSocks4AuthenticationResponse(connection, socks4Response)
|
||||
socks4Response := protocol.NewSocks4AuthenticationResponse(result, auth4.Port, auth4.IP[:])
|
||||
protocol.WriteSocks4AuthenticationResponse(connection, socks4Response)
|
||||
|
||||
if result == socksio.Socks4RequestRejected {
|
||||
if result == protocol.Socks4RequestRejected {
|
||||
return ErrorCommandNotSupported
|
||||
}
|
||||
|
||||
dest = v2net.IPAddress(auth4.IP[:], auth4.Port)
|
||||
dest = v2net.NewTCPDestination(v2net.IPAddress(auth4.IP[:], auth4.Port))
|
||||
} else {
|
||||
expectedAuthMethod := socksio.AuthNotRequired
|
||||
expectedAuthMethod := protocol.AuthNotRequired
|
||||
if server.config.AuthMethod == JsonAuthMethodUserPass {
|
||||
expectedAuthMethod = socksio.AuthUserPass
|
||||
expectedAuthMethod = protocol.AuthUserPass
|
||||
}
|
||||
|
||||
if !auth.HasAuthMethod(expectedAuthMethod) {
|
||||
authResponse := socksio.NewAuthenticationResponse(socksio.AuthNoMatchingMethod)
|
||||
err = socksio.WriteAuthentication(connection, authResponse)
|
||||
authResponse := protocol.NewAuthenticationResponse(protocol.AuthNoMatchingMethod)
|
||||
err = protocol.WriteAuthentication(connection, authResponse)
|
||||
if err != nil {
|
||||
log.Error("Error on socksio write authentication: %v", err)
|
||||
return err
|
||||
@@ -103,14 +103,14 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
|
||||
return ErrorAuthenticationFailed
|
||||
}
|
||||
|
||||
authResponse := socksio.NewAuthenticationResponse(expectedAuthMethod)
|
||||
err = socksio.WriteAuthentication(connection, authResponse)
|
||||
authResponse := protocol.NewAuthenticationResponse(expectedAuthMethod)
|
||||
err = protocol.WriteAuthentication(connection, authResponse)
|
||||
if err != nil {
|
||||
log.Error("Error on socksio write authentication: %v", err)
|
||||
return err
|
||||
}
|
||||
if server.config.AuthMethod == JsonAuthMethodUserPass {
|
||||
upRequest, err := socksio.ReadUserPassRequest(reader)
|
||||
upRequest, err := protocol.ReadUserPassRequest(reader)
|
||||
if err != nil {
|
||||
log.Error("Failed to read username and password: %v", err)
|
||||
return err
|
||||
@@ -119,8 +119,8 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
|
||||
if !upRequest.IsValid(server.config.Username, server.config.Password) {
|
||||
status = byte(0xFF)
|
||||
}
|
||||
upResponse := socksio.NewSocks5UserPassResponse(status)
|
||||
err = socksio.WriteUserPassResponse(connection, upResponse)
|
||||
upResponse := protocol.NewSocks5UserPassResponse(status)
|
||||
err = protocol.WriteUserPassResponse(connection, upResponse)
|
||||
if err != nil {
|
||||
log.Error("Error on socksio write user pass response: %v", err)
|
||||
return err
|
||||
@@ -130,18 +130,18 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
|
||||
}
|
||||
}
|
||||
|
||||
request, err := socksio.ReadRequest(reader)
|
||||
request, err := protocol.ReadRequest(reader)
|
||||
if err != nil {
|
||||
log.Error("Error on reading socks request: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
response := socksio.NewSocks5Response()
|
||||
response := protocol.NewSocks5Response()
|
||||
|
||||
if request.Command == socksio.CmdBind || request.Command == socksio.CmdUdpAssociate {
|
||||
response := socksio.NewSocks5Response()
|
||||
response.Error = socksio.ErrorCommandNotSupported
|
||||
err = socksio.WriteResponse(connection, response)
|
||||
if request.Command == protocol.CmdBind || request.Command == protocol.CmdUdpAssociate {
|
||||
response := protocol.NewSocks5Response()
|
||||
response.Error = protocol.ErrorCommandNotSupported
|
||||
err = protocol.WriteResponse(connection, response)
|
||||
if err != nil {
|
||||
log.Error("Error on socksio write response: %v", err)
|
||||
return err
|
||||
@@ -150,18 +150,18 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
|
||||
return ErrorCommandNotSupported
|
||||
}
|
||||
|
||||
response.Error = socksio.ErrorSuccess
|
||||
response.Error = protocol.ErrorSuccess
|
||||
response.Port = request.Port
|
||||
response.AddrType = request.AddrType
|
||||
switch response.AddrType {
|
||||
case socksio.AddrTypeIPv4:
|
||||
case protocol.AddrTypeIPv4:
|
||||
copy(response.IPv4[:], request.IPv4[:])
|
||||
case socksio.AddrTypeIPv6:
|
||||
case protocol.AddrTypeIPv6:
|
||||
copy(response.IPv6[:], request.IPv6[:])
|
||||
case socksio.AddrTypeDomain:
|
||||
case protocol.AddrTypeDomain:
|
||||
response.Domain = request.Domain
|
||||
}
|
||||
err = socksio.WriteResponse(connection, response)
|
||||
err = protocol.WriteResponse(connection, response)
|
||||
if err != nil {
|
||||
log.Error("Error on socksio write response: %v", err)
|
||||
return err
|
||||
116
proxy/socks/socks_test.go
Normal file
116
proxy/socks/socks_test.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package socks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/testing/mocks"
|
||||
"github.com/v2ray/v2ray-core/testing/unit"
|
||||
)
|
||||
|
||||
func TestSocksTcpConnect(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
port := uint16(12385)
|
||||
|
||||
och := &mocks.OutboundConnectionHandler{
|
||||
Data2Send: bytes.NewBuffer(make([]byte, 0, 1024)),
|
||||
Data2Return: []byte("The data to be returned to socks server."),
|
||||
}
|
||||
|
||||
core.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
|
||||
config := mocks.Config{
|
||||
PortValue: port,
|
||||
InboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "socks",
|
||||
ContentValue: []byte("{\"auth\": \"noauth\"}"),
|
||||
},
|
||||
OutboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "mock_och",
|
||||
ContentValue: nil,
|
||||
},
|
||||
}
|
||||
|
||||
point, err := core.NewPoint(&config)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = point.Start()
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
socks5Client, err := proxy.SOCKS5("tcp", "127.0.0.1:12385", nil, proxy.Direct)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
targetServer := "google.com:80"
|
||||
conn, err := socks5Client.Dial("tcp", targetServer)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
data2Send := "The data to be sent to remote server."
|
||||
conn.Write([]byte(data2Send))
|
||||
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||
tcpConn.CloseWrite()
|
||||
}
|
||||
|
||||
dataReturned, err := ioutil.ReadAll(conn)
|
||||
assert.Error(err).IsNil()
|
||||
conn.Close()
|
||||
|
||||
assert.Bytes([]byte(data2Send)).Equals(och.Data2Send.Bytes())
|
||||
assert.Bytes(dataReturned).Equals(och.Data2Return)
|
||||
assert.String(targetServer).Equals(och.Destination.Address().String())
|
||||
}
|
||||
|
||||
func TestSocksTcpConnectWithUserPass(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
port := uint16(12386)
|
||||
|
||||
och := &mocks.OutboundConnectionHandler{
|
||||
Data2Send: bytes.NewBuffer(make([]byte, 0, 1024)),
|
||||
Data2Return: []byte("The data to be returned to socks server."),
|
||||
}
|
||||
|
||||
core.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
|
||||
config := mocks.Config{
|
||||
PortValue: port,
|
||||
InboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "socks",
|
||||
ContentValue: []byte("{\"auth\": \"password\",\"user\": \"userx\",\"pass\": \"passy\"}"),
|
||||
},
|
||||
OutboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "mock_och",
|
||||
ContentValue: nil,
|
||||
},
|
||||
}
|
||||
|
||||
point, err := core.NewPoint(&config)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = point.Start()
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
socks5Client, err := proxy.SOCKS5("tcp", "127.0.0.1:12386", &proxy.Auth{"userx", "passy"}, proxy.Direct)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
targetServer := "1.2.3.4:443"
|
||||
conn, err := socks5Client.Dial("tcp", targetServer)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
data2Send := "The data to be sent to remote server."
|
||||
conn.Write([]byte(data2Send))
|
||||
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||
tcpConn.CloseWrite()
|
||||
}
|
||||
|
||||
dataReturned, err := ioutil.ReadAll(conn)
|
||||
assert.Error(err).IsNil()
|
||||
conn.Close()
|
||||
|
||||
assert.Bytes([]byte(data2Send)).Equals(och.Data2Send.Bytes())
|
||||
assert.Bytes(dataReturned).Equals(och.Data2Return)
|
||||
assert.String(targetServer).Equals(och.Destination.Address().String())
|
||||
}
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/log"
|
||||
v2net "github.com/v2ray/v2ray-core/net"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
|
||||
)
|
||||
|
||||
type VMessUser struct {
|
||||
@@ -14,9 +14,9 @@ type VMessUser struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
func (u *VMessUser) ToUser() (core.User, error) {
|
||||
id, err := core.NewID(u.Id)
|
||||
return core.User{
|
||||
func (u *VMessUser) ToUser() (user.User, error) {
|
||||
id, err := user.NewID(u.Id)
|
||||
return user.User{
|
||||
Id: id,
|
||||
}, err
|
||||
}
|
||||
@@ -35,10 +35,11 @@ type VNextConfig struct {
|
||||
Address string `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
Users []VMessUser `json:"users"`
|
||||
Network string `json:"network"`
|
||||
}
|
||||
|
||||
func (config VNextConfig) ToVNextServer() VNextServer {
|
||||
users := make([]core.User, 0, len(config.Users))
|
||||
users := make([]user.User, 0, len(config.Users))
|
||||
for _, user := range config.Users {
|
||||
vuser, err := user.ToUser()
|
||||
if err != nil {
|
||||
@@ -50,9 +51,14 @@ func (config VNextConfig) ToVNextServer() VNextServer {
|
||||
if ip == nil {
|
||||
panic(log.Error("Unable to parse VNext IP: %s", config.Address))
|
||||
}
|
||||
address := v2net.IPAddress(ip, config.Port)
|
||||
dest := v2net.NewTCPDestination(address)
|
||||
if config.Network == "udp" {
|
||||
dest = v2net.NewUDPDestination(address)
|
||||
}
|
||||
return VNextServer{
|
||||
Address: v2net.IPAddress(ip, config.Port),
|
||||
Users: users,
|
||||
Destination: dest,
|
||||
Users: users,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package core
|
||||
package user
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/v2ray/v2ray-core/log"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -1,4 +1,4 @@
|
||||
package core
|
||||
package user
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -1,4 +1,4 @@
|
||||
package hash
|
||||
package user
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
@@ -1,4 +1,4 @@
|
||||
package hash
|
||||
package user
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
@@ -1,4 +1,4 @@
|
||||
package math
|
||||
package user
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
@@ -1,4 +1,4 @@
|
||||
package math
|
||||
package user
|
||||
|
||||
import (
|
||||
"testing"
|
||||
6
proxy/vmess/protocol/user/user.go
Normal file
6
proxy/vmess/protocol/user/user.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package user
|
||||
|
||||
// User is the user account that is used for connection to a Point
|
||||
type User struct {
|
||||
Id ID `json:"id"` // The ID of this User.
|
||||
}
|
||||
129
proxy/vmess/protocol/user/userset.go
Normal file
129
proxy/vmess/protocol/user/userset.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
)
|
||||
|
||||
const (
|
||||
updateIntervalSec = 10
|
||||
cacheDurationSec = 120
|
||||
)
|
||||
|
||||
type UserSet interface {
|
||||
AddUser(user User) error
|
||||
GetUser(timeHash []byte) (*ID, int64, bool)
|
||||
}
|
||||
|
||||
type TimedUserSet struct {
|
||||
validUserIds []ID
|
||||
userHashes map[string]indexTimePair
|
||||
hash2Remove hashEntrySet
|
||||
}
|
||||
|
||||
type indexTimePair struct {
|
||||
index int
|
||||
timeSec int64
|
||||
}
|
||||
|
||||
type hashEntry struct {
|
||||
hash string
|
||||
timeSec int64
|
||||
}
|
||||
|
||||
type hashEntrySet []*hashEntry
|
||||
|
||||
func (set hashEntrySet) Len() int {
|
||||
return len(set)
|
||||
}
|
||||
|
||||
func (set hashEntrySet) Less(i, j int) bool {
|
||||
return set[i].timeSec < set[j].timeSec
|
||||
}
|
||||
|
||||
func (set hashEntrySet) Swap(i, j int) {
|
||||
tmp := set[i]
|
||||
set[i] = set[j]
|
||||
set[j] = tmp
|
||||
}
|
||||
|
||||
func (set *hashEntrySet) Push(value interface{}) {
|
||||
entry := value.(*hashEntry)
|
||||
*set = append(*set, entry)
|
||||
}
|
||||
|
||||
func (set *hashEntrySet) Pop() interface{} {
|
||||
old := *set
|
||||
n := len(old)
|
||||
v := old[n-1]
|
||||
*set = old[:n-1]
|
||||
return v
|
||||
}
|
||||
|
||||
func NewTimedUserSet() UserSet {
|
||||
vuSet := new(TimedUserSet)
|
||||
vuSet.validUserIds = make([]ID, 0, 16)
|
||||
vuSet.userHashes = make(map[string]indexTimePair)
|
||||
vuSet.hash2Remove = make(hashEntrySet, 0, cacheDurationSec*10)
|
||||
|
||||
go vuSet.updateUserHash(time.Tick(updateIntervalSec * time.Second))
|
||||
return vuSet
|
||||
}
|
||||
|
||||
func (us *TimedUserSet) generateNewHashes(lastSec, nowSec int64, idx int, id ID) {
|
||||
idHash := NewTimeHash(HMACHash{})
|
||||
for lastSec < nowSec+cacheDurationSec {
|
||||
|
||||
idHash := idHash.Hash(id.Bytes, lastSec)
|
||||
log.Debug("Valid User Hash: %v", idHash)
|
||||
heap.Push(&us.hash2Remove, &hashEntry{string(idHash), lastSec})
|
||||
us.userHashes[string(idHash)] = indexTimePair{idx, lastSec}
|
||||
lastSec++
|
||||
}
|
||||
}
|
||||
|
||||
func (us *TimedUserSet) updateUserHash(tick <-chan time.Time) {
|
||||
now := time.Now().UTC()
|
||||
lastSec := now.Unix()
|
||||
lastSec2Remove := now.Unix()
|
||||
|
||||
for {
|
||||
now := <-tick
|
||||
nowSec := now.UTC().Unix()
|
||||
|
||||
remove2Sec := nowSec - cacheDurationSec
|
||||
if remove2Sec > lastSec2Remove {
|
||||
for lastSec2Remove+1 < remove2Sec {
|
||||
front := heap.Pop(&us.hash2Remove)
|
||||
entry := front.(*hashEntry)
|
||||
lastSec2Remove = entry.timeSec
|
||||
delete(us.userHashes, entry.hash)
|
||||
}
|
||||
}
|
||||
for idx, id := range us.validUserIds {
|
||||
us.generateNewHashes(lastSec, nowSec, idx, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (us *TimedUserSet) AddUser(user User) error {
|
||||
id := user.Id
|
||||
idx := len(us.validUserIds)
|
||||
us.validUserIds = append(us.validUserIds, id)
|
||||
|
||||
nowSec := time.Now().UTC().Unix()
|
||||
lastSec := nowSec - cacheDurationSec
|
||||
us.generateNewHashes(lastSec, nowSec, idx, id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (us TimedUserSet) GetUser(userHash []byte) (*ID, int64, bool) {
|
||||
pair, found := us.userHashes[string(userHash)]
|
||||
if found {
|
||||
return &us.validUserIds[pair.index], pair.timeSec, true
|
||||
}
|
||||
return nil, 0, false
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Package vmess contains protocol definition, io lib for VMess.
|
||||
package vmess
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
@@ -12,12 +12,10 @@ import (
|
||||
mrand "math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
v2hash "github.com/v2ray/v2ray-core/hash"
|
||||
v2io "github.com/v2ray/v2ray-core/io"
|
||||
"github.com/v2ray/v2ray-core/log"
|
||||
v2math "github.com/v2ray/v2ray-core/math"
|
||||
v2net "github.com/v2ray/v2ray-core/net"
|
||||
v2io "github.com/v2ray/v2ray-core/common/io"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -25,6 +23,9 @@ const (
|
||||
addrTypeIPv6 = byte(0x03)
|
||||
addrTypeDomain = byte(0x02)
|
||||
|
||||
CmdTCP = byte(0x01)
|
||||
CmdUDP = byte(0x02)
|
||||
|
||||
Version = byte(0x01)
|
||||
|
||||
blockSize = 16
|
||||
@@ -41,7 +42,7 @@ var (
|
||||
|
||||
type VMessRequest struct {
|
||||
Version byte
|
||||
UserId core.ID
|
||||
UserId user.ID
|
||||
RequestIV [16]byte
|
||||
RequestKey [16]byte
|
||||
ResponseHeader [4]byte
|
||||
@@ -49,11 +50,19 @@ type VMessRequest struct {
|
||||
Address v2net.Address
|
||||
}
|
||||
|
||||
type VMessRequestReader struct {
|
||||
vUserSet core.UserSet
|
||||
func (request *VMessRequest) Destination() v2net.Destination {
|
||||
if request.Command == CmdTCP {
|
||||
return v2net.NewTCPDestination(request.Address)
|
||||
} else {
|
||||
return v2net.NewUDPDestination(request.Address)
|
||||
}
|
||||
}
|
||||
|
||||
func NewVMessRequestReader(vUserSet core.UserSet) *VMessRequestReader {
|
||||
type VMessRequestReader struct {
|
||||
vUserSet user.UserSet
|
||||
}
|
||||
|
||||
func NewVMessRequestReader(vUserSet user.UserSet) *VMessRequestReader {
|
||||
return &VMessRequestReader{
|
||||
vUserSet: vUserSet,
|
||||
}
|
||||
@@ -62,7 +71,7 @@ func NewVMessRequestReader(vUserSet core.UserSet) *VMessRequestReader {
|
||||
func (r *VMessRequestReader) Read(reader io.Reader) (*VMessRequest, error) {
|
||||
buffer := make([]byte, 256)
|
||||
|
||||
nBytes, err := reader.Read(buffer[:core.IDBytesLen])
|
||||
nBytes, err := reader.Read(buffer[:user.IDBytesLen])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -78,7 +87,7 @@ func (r *VMessRequestReader) Read(reader io.Reader) (*VMessRequest, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aesStream := cipher.NewCFBDecrypter(aesCipher, v2hash.Int64Hash(timeSec))
|
||||
aesStream := cipher.NewCFBDecrypter(aesCipher, user.Int64Hash(timeSec))
|
||||
decryptor := v2io.NewCryptionReader(aesStream, reader)
|
||||
|
||||
if err != nil {
|
||||
@@ -181,7 +190,7 @@ func (r *VMessRequestReader) Read(reader io.Reader) (*VMessRequest, error) {
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (request *VMessRequest) ToBytes(idHash v2hash.CounterHash, randomRangeInt64 v2math.RandomInt64InRange) ([]byte, error) {
|
||||
func (request *VMessRequest) ToBytes(idHash user.CounterHash, randomRangeInt64 user.RandomInt64InRange) ([]byte, error) {
|
||||
buffer := make([]byte, 0, 300)
|
||||
|
||||
counter := randomRangeInt64(time.Now().UTC().Unix(), 30)
|
||||
@@ -206,22 +215,19 @@ func (request *VMessRequest) ToBytes(idHash v2hash.CounterHash, randomRangeInt64
|
||||
buffer = append(buffer, request.RequestKey[:]...)
|
||||
buffer = append(buffer, request.ResponseHeader[:]...)
|
||||
buffer = append(buffer, request.Command)
|
||||
|
||||
portBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(portBytes, request.Address.Port)
|
||||
buffer = append(buffer, portBytes...)
|
||||
buffer = append(buffer, request.Address.PortBytes()...)
|
||||
|
||||
switch {
|
||||
case request.Address.IsIPv4():
|
||||
buffer = append(buffer, addrTypeIPv4)
|
||||
buffer = append(buffer, request.Address.IP...)
|
||||
buffer = append(buffer, request.Address.IP()...)
|
||||
case request.Address.IsIPv6():
|
||||
buffer = append(buffer, addrTypeIPv6)
|
||||
buffer = append(buffer, request.Address.IP...)
|
||||
buffer = append(buffer, request.Address.IP()...)
|
||||
case request.Address.IsDomain():
|
||||
buffer = append(buffer, addrTypeDomain)
|
||||
buffer = append(buffer, byte(len(request.Address.Domain)))
|
||||
buffer = append(buffer, []byte(request.Address.Domain)...)
|
||||
buffer = append(buffer, byte(len(request.Address.Domain())))
|
||||
buffer = append(buffer, []byte(request.Address.Domain())...)
|
||||
}
|
||||
|
||||
paddingLength := mrand.Intn(32) + 1
|
||||
@@ -238,7 +244,7 @@ func (request *VMessRequest) ToBytes(idHash v2hash.CounterHash, randomRangeInt64
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aesStream := cipher.NewCFBEncrypter(aesCipher, v2hash.Int64Hash(counter))
|
||||
aesStream := cipher.NewCFBEncrypter(aesCipher, user.Int64Hash(counter))
|
||||
aesStream.XORKeyStream(buffer[encryptionBegin:encryptionEnd], buffer[encryptionBegin:encryptionEnd])
|
||||
|
||||
return buffer, nil
|
||||
@@ -1,14 +1,12 @@
|
||||
package vmess
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
v2hash "github.com/v2ray/v2ray-core/hash"
|
||||
v2math "github.com/v2ray/v2ray-core/math"
|
||||
v2net "github.com/v2ray/v2ray-core/net"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
|
||||
"github.com/v2ray/v2ray-core/testing/mocks"
|
||||
"github.com/v2ray/v2ray-core/testing/unit"
|
||||
)
|
||||
@@ -16,13 +14,13 @@ import (
|
||||
func TestVMessSerialization(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
|
||||
userId, err := core.NewID("2b2966ac-16aa-4fbf-8d81-c5f172a3da51")
|
||||
userId, err := user.NewID("2b2966ac-16aa-4fbf-8d81-c5f172a3da51")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
userSet := mocks.MockUserSet{[]core.ID{}, make(map[string]int), make(map[string]int64)}
|
||||
userSet.AddUser(core.User{userId})
|
||||
userSet := mocks.MockUserSet{[]user.ID{}, make(map[string]int), make(map[string]int64)}
|
||||
userSet.AddUser(user.User{userId})
|
||||
|
||||
request := new(VMessRequest)
|
||||
request.Version = byte(0x01)
|
||||
@@ -47,7 +45,7 @@ func TestVMessSerialization(t *testing.T) {
|
||||
request.Address = v2net.DomainAddress("v2ray.com", 80)
|
||||
|
||||
mockTime := int64(1823730)
|
||||
buffer, err := request.ToBytes(v2hash.NewTimeHash(v2hash.HMACHash{}), func(base int64, delta int) int64 { return mockTime })
|
||||
buffer, err := request.ToBytes(user.NewTimeHash(user.HMACHash{}), func(base int64, delta int) int64 { return mockTime })
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -71,9 +69,9 @@ func TestVMessSerialization(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkVMessRequestWriting(b *testing.B) {
|
||||
userId, _ := core.NewID("2b2966ac-16aa-4fbf-8d81-c5f172a3da51")
|
||||
userSet := mocks.MockUserSet{[]core.ID{}, make(map[string]int), make(map[string]int64)}
|
||||
userSet.AddUser(core.User{userId})
|
||||
userId, _ := user.NewID("2b2966ac-16aa-4fbf-8d81-c5f172a3da51")
|
||||
userSet := mocks.MockUserSet{[]user.ID{}, make(map[string]int), make(map[string]int64)}
|
||||
userSet.AddUser(user.User{userId})
|
||||
|
||||
request := new(VMessRequest)
|
||||
request.Version = byte(0x01)
|
||||
@@ -87,6 +85,6 @@ func BenchmarkVMessRequestWriting(b *testing.B) {
|
||||
request.Address = v2net.DomainAddress("v2ray.com", 80)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
request.ToBytes(v2hash.NewTimeHash(v2hash.HMACHash{}), v2math.GenerateRandomInt64InRange)
|
||||
request.ToBytes(user.NewTimeHash(user.HMACHash{}), user.GenerateRandomInt64InRange)
|
||||
}
|
||||
}
|
||||
75
proxy/vmess/vmess_test.go
Normal file
75
proxy/vmess/vmess_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package vmess
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/testing/mocks"
|
||||
"github.com/v2ray/v2ray-core/testing/unit"
|
||||
)
|
||||
|
||||
func TestVMessInAndOut(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
|
||||
data2Send := "The data to be send to outbound server."
|
||||
|
||||
portA := uint16(17392)
|
||||
ich := &mocks.InboundConnectionHandler{
|
||||
Data2Send: []byte(data2Send),
|
||||
DataReturned: bytes.NewBuffer(make([]byte, 0, 1024)),
|
||||
}
|
||||
|
||||
core.RegisterInboundConnectionHandlerFactory("mock_ich", ich)
|
||||
|
||||
configA := mocks.Config{
|
||||
PortValue: portA,
|
||||
InboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "mock_ich",
|
||||
ContentValue: nil,
|
||||
},
|
||||
OutboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "vmess",
|
||||
ContentValue: []byte("{\"vnext\":[{\"address\": \"127.0.0.1\", \"port\": 13829, \"users\":[{\"id\": \"ad937d9d-6e23-4a5a-ba23-bce5092a7c51\"}]}]}"),
|
||||
},
|
||||
}
|
||||
|
||||
pointA, err := core.NewPoint(&configA)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = pointA.Start()
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
portB := uint16(13829)
|
||||
|
||||
och := &mocks.OutboundConnectionHandler{
|
||||
Data2Send: bytes.NewBuffer(make([]byte, 0, 1024)),
|
||||
Data2Return: []byte("The data to be returned to inbound server."),
|
||||
}
|
||||
|
||||
core.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
|
||||
configB := mocks.Config{
|
||||
PortValue: portB,
|
||||
InboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "vmess",
|
||||
ContentValue: []byte("{\"clients\": [{\"id\": \"ad937d9d-6e23-4a5a-ba23-bce5092a7c51\"}]}"),
|
||||
},
|
||||
OutboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "mock_och",
|
||||
ContentValue: nil,
|
||||
},
|
||||
}
|
||||
|
||||
pointB, err := core.NewPoint(&configB)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = pointB.Start()
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
dest := v2net.NewTCPDestination(v2net.IPAddress([]byte{1, 2, 3, 4}, 80))
|
||||
ich.Communicate(dest)
|
||||
assert.Bytes([]byte(data2Send)).Equals(och.Data2Send.Bytes())
|
||||
assert.Bytes(ich.DataReturned.Bytes()).Equals(och.Data2Return)
|
||||
}
|
||||
@@ -7,19 +7,20 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
v2io "github.com/v2ray/v2ray-core/io"
|
||||
vmessio "github.com/v2ray/v2ray-core/io/vmess"
|
||||
"github.com/v2ray/v2ray-core/log"
|
||||
v2net "github.com/v2ray/v2ray-core/net"
|
||||
v2io "github.com/v2ray/v2ray-core/common/io"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/protocol"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
|
||||
)
|
||||
|
||||
type VMessInboundHandler struct {
|
||||
vPoint *core.Point
|
||||
clients core.UserSet
|
||||
clients user.UserSet
|
||||
accepting bool
|
||||
}
|
||||
|
||||
func NewVMessInboundHandler(vp *core.Point, clients core.UserSet) *VMessInboundHandler {
|
||||
func NewVMessInboundHandler(vp *core.Point, clients user.UserSet) *VMessInboundHandler {
|
||||
return &VMessInboundHandler{
|
||||
vPoint: vp,
|
||||
clients: clients,
|
||||
@@ -51,7 +52,7 @@ func (handler *VMessInboundHandler) AcceptConnections(listener net.Listener) err
|
||||
func (handler *VMessInboundHandler) HandleConnection(connection net.Conn) error {
|
||||
defer connection.Close()
|
||||
|
||||
reader := vmessio.NewVMessRequestReader(handler.clients)
|
||||
reader := protocol.NewVMessRequestReader(handler.clients)
|
||||
|
||||
request, err := reader.Read(connection)
|
||||
if err != nil {
|
||||
@@ -60,7 +61,7 @@ func (handler *VMessInboundHandler) HandleConnection(connection net.Conn) error
|
||||
}
|
||||
log.Debug("VMessIn: Received request for %s", request.Address.String())
|
||||
|
||||
ray := handler.vPoint.NewInboundConnectionAccepted(request.Address)
|
||||
ray := handler.vPoint.NewInboundConnectionAccepted(request.Destination())
|
||||
input := ray.InboundInput()
|
||||
output := ray.InboundOutput()
|
||||
|
||||
@@ -72,7 +73,7 @@ func (handler *VMessInboundHandler) HandleConnection(connection net.Conn) error
|
||||
responseKey := md5.Sum(request.RequestKey[:])
|
||||
responseIV := md5.Sum(request.RequestIV[:])
|
||||
|
||||
response := vmessio.NewVMessResponse(request)
|
||||
response := protocol.NewVMessResponse(request)
|
||||
responseWriter, err := v2io.NewAesEncryptWriter(responseKey[:], responseIV[:], connection)
|
||||
if err != nil {
|
||||
return log.Error("VMessIn: Failed to create encrypt writer: %v", err)
|
||||
@@ -97,7 +98,7 @@ func (handler *VMessInboundHandler) HandleConnection(connection net.Conn) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleInput(request *vmessio.VMessRequest, reader io.Reader, input chan<- []byte, finish chan<- bool) {
|
||||
func handleInput(request *protocol.VMessRequest, reader io.Reader, input chan<- []byte, finish chan<- bool) {
|
||||
defer close(input)
|
||||
defer close(finish)
|
||||
|
||||
@@ -110,7 +111,7 @@ func handleInput(request *vmessio.VMessRequest, reader io.Reader, input chan<- [
|
||||
v2net.ReaderToChan(input, requestReader)
|
||||
}
|
||||
|
||||
func handleOutput(request *vmessio.VMessRequest, writer io.Writer, output <-chan []byte, finish chan<- bool) {
|
||||
func handleOutput(request *protocol.VMessRequest, writer io.Writer, output <-chan []byte, finish chan<- bool) {
|
||||
v2net.ChanToWriter(writer, output)
|
||||
close(finish)
|
||||
}
|
||||
@@ -123,7 +124,7 @@ func (factory *VMessInboundHandlerFactory) Create(vp *core.Point, rawConfig []by
|
||||
if err != nil {
|
||||
panic(log.Error("VMessIn: Failed to load VMess inbound config: %v", err))
|
||||
}
|
||||
allowedClients := core.NewTimedUserSet()
|
||||
allowedClients := user.NewTimedUserSet()
|
||||
for _, client := range config.AllowedClients {
|
||||
user, err := client.ToUser()
|
||||
if err != nil {
|
||||
@@ -8,27 +8,26 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
v2hash "github.com/v2ray/v2ray-core/hash"
|
||||
v2io "github.com/v2ray/v2ray-core/io"
|
||||
vmessio "github.com/v2ray/v2ray-core/io/vmess"
|
||||
"github.com/v2ray/v2ray-core/log"
|
||||
v2math "github.com/v2ray/v2ray-core/math"
|
||||
v2net "github.com/v2ray/v2ray-core/net"
|
||||
v2io "github.com/v2ray/v2ray-core/common/io"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/protocol"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
|
||||
)
|
||||
|
||||
// VNext is the next Point server in the connection chain.
|
||||
type VNextServer struct {
|
||||
Address v2net.Address // Address of VNext server
|
||||
Users []core.User // User accounts for accessing VNext.
|
||||
Destination v2net.Destination // Address of VNext server
|
||||
Users []user.User // User accounts for accessing VNext.
|
||||
}
|
||||
|
||||
type VMessOutboundHandler struct {
|
||||
vPoint *core.Point
|
||||
dest v2net.Address
|
||||
dest v2net.Destination
|
||||
vNextList []VNextServer
|
||||
}
|
||||
|
||||
func NewVMessOutboundHandler(vp *core.Point, vNextList []VNextServer, dest v2net.Address) *VMessOutboundHandler {
|
||||
func NewVMessOutboundHandler(vp *core.Point, vNextList []VNextServer, dest v2net.Destination) *VMessOutboundHandler {
|
||||
return &VMessOutboundHandler{
|
||||
vPoint: vp,
|
||||
dest: dest,
|
||||
@@ -36,7 +35,7 @@ func NewVMessOutboundHandler(vp *core.Point, vNextList []VNextServer, dest v2net
|
||||
}
|
||||
}
|
||||
|
||||
func (handler *VMessOutboundHandler) pickVNext() (v2net.Address, core.User) {
|
||||
func (handler *VMessOutboundHandler) pickVNext() (v2net.Destination, user.User) {
|
||||
vNextLen := len(handler.vNextList)
|
||||
if vNextLen == 0 {
|
||||
panic("VMessOut: Zero vNext is configured.")
|
||||
@@ -49,17 +48,21 @@ func (handler *VMessOutboundHandler) pickVNext() (v2net.Address, core.User) {
|
||||
}
|
||||
vNextUserIndex := mrand.Intn(vNextUserLen)
|
||||
vNextUser := vNext.Users[vNextUserIndex]
|
||||
return vNext.Address, vNextUser
|
||||
return vNext.Destination, vNextUser
|
||||
}
|
||||
|
||||
func (handler *VMessOutboundHandler) Start(ray core.OutboundRay) error {
|
||||
vNextAddress, vNextUser := handler.pickVNext()
|
||||
|
||||
request := &vmessio.VMessRequest{
|
||||
Version: vmessio.Version,
|
||||
command := protocol.CmdTCP
|
||||
if handler.dest.IsUDP() {
|
||||
command = protocol.CmdUDP
|
||||
}
|
||||
request := &protocol.VMessRequest{
|
||||
Version: protocol.Version,
|
||||
UserId: vNextUser.Id,
|
||||
Command: byte(0x01),
|
||||
Address: handler.dest,
|
||||
Command: command,
|
||||
Address: handler.dest.Address(),
|
||||
}
|
||||
rand.Read(request.RequestIV[:])
|
||||
rand.Read(request.RequestKey[:])
|
||||
@@ -69,11 +72,11 @@ func (handler *VMessOutboundHandler) Start(ray core.OutboundRay) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func startCommunicate(request *vmessio.VMessRequest, dest v2net.Address, ray core.OutboundRay) error {
|
||||
func startCommunicate(request *protocol.VMessRequest, dest v2net.Destination, ray core.OutboundRay) error {
|
||||
input := ray.OutboundInput()
|
||||
output := ray.OutboundOutput()
|
||||
|
||||
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{dest.IP, int(dest.Port), ""})
|
||||
conn, err := net.DialTCP(dest.Network(), nil, &net.TCPAddr{dest.Address().IP(), int(dest.Address().Port()), ""})
|
||||
if err != nil {
|
||||
log.Error("Failed to open tcp (%s): %v", dest.String(), err)
|
||||
close(output)
|
||||
@@ -95,7 +98,7 @@ func startCommunicate(request *vmessio.VMessRequest, dest v2net.Address, ray cor
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleRequest(conn *net.TCPConn, request *vmessio.VMessRequest, input <-chan []byte, finish chan<- bool) {
|
||||
func handleRequest(conn *net.TCPConn, request *protocol.VMessRequest, input <-chan []byte, finish chan<- bool) {
|
||||
defer close(finish)
|
||||
encryptRequestWriter, err := v2io.NewAesEncryptWriter(request.RequestKey[:], request.RequestIV[:], conn)
|
||||
if err != nil {
|
||||
@@ -103,7 +106,7 @@ func handleRequest(conn *net.TCPConn, request *vmessio.VMessRequest, input <-cha
|
||||
return
|
||||
}
|
||||
|
||||
buffer, err := request.ToBytes(v2hash.NewTimeHash(v2hash.HMACHash{}), v2math.GenerateRandomInt64InRange)
|
||||
buffer, err := request.ToBytes(user.NewTimeHash(user.HMACHash{}), user.GenerateRandomInt64InRange)
|
||||
if err != nil {
|
||||
log.Error("VMessOut: Failed to serialize VMess request: %v", err)
|
||||
return
|
||||
@@ -126,7 +129,7 @@ func handleRequest(conn *net.TCPConn, request *vmessio.VMessRequest, input <-cha
|
||||
return
|
||||
}
|
||||
|
||||
func handleResponse(conn *net.TCPConn, request *vmessio.VMessRequest, output chan<- []byte, finish chan<- bool) {
|
||||
func handleResponse(conn *net.TCPConn, request *protocol.VMessRequest, output chan<- []byte, finish chan<- bool) {
|
||||
defer close(finish)
|
||||
defer close(output)
|
||||
responseKey := md5.Sum(request.RequestKey[:])
|
||||
@@ -138,7 +141,7 @@ func handleResponse(conn *net.TCPConn, request *vmessio.VMessRequest, output cha
|
||||
return
|
||||
}
|
||||
|
||||
response := vmessio.VMessResponse{}
|
||||
response := protocol.VMessResponse{}
|
||||
nBytes, err := decryptResponseReader.Read(response[:])
|
||||
if err != nil {
|
||||
log.Error("VMessOut: Failed to read VMess response (%d bytes): %v", nBytes, err)
|
||||
@@ -156,7 +159,7 @@ func handleResponse(conn *net.TCPConn, request *vmessio.VMessRequest, output cha
|
||||
type VMessOutboundHandlerFactory struct {
|
||||
}
|
||||
|
||||
func (factory *VMessOutboundHandlerFactory) Create(vp *core.Point, rawConfig []byte, destination v2net.Address) (core.OutboundConnectionHandler, error) {
|
||||
func (factory *VMessOutboundHandlerFactory) Create(vp *core.Point, rawConfig []byte, destination v2net.Destination) (core.OutboundConnectionHandler, error) {
|
||||
config, err := loadOutboundConfig(rawConfig)
|
||||
if err != nil {
|
||||
panic(log.Error("Failed to load VMess outbound config: %v", err))
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION=$(sed -n 's/.*Version = \"\([^"]*\)\"*/\1/p' $GOPATH/src/github.com/v2ray/v2ray-core/core.go)
|
||||
VERSION=$(sed -n 's/.*Version.*=.*\"\([^"]*\)\".*/\1/p' $GOPATH/src/github.com/v2ray/v2ray-core/core.go)
|
||||
|
||||
REL_PATH=$GOPATH/bin/$VERSION
|
||||
if [ -d "$REL_PATH" ]; then
|
||||
@@ -25,12 +25,11 @@ build "windows" "amd64" "-windows-64.exe"
|
||||
build "linux" "amd64" "-linux-64"
|
||||
build "linux" "386" "-linux-32"
|
||||
|
||||
ZIP_FILE=$GOPATH/bin/v2ray-$VERSION.zip
|
||||
ZIP_FILE=$GOPATH/bin/v2ray.zip
|
||||
if [ -f $ZIP_FILE ]; then
|
||||
rm -f $ZIP_FILE
|
||||
fi
|
||||
|
||||
pushd $REL_PATH
|
||||
zip -r $GOPATH/bin/v2ray-$VERSION.zip *
|
||||
cp $GOPATH/bin/v2ray-$VERSION.zip $GOPATH/bin/v2ray-latest.zip
|
||||
pushd $GOPATH/bin/
|
||||
zip -r $ZIP_FILE ./$VERSION/*
|
||||
popd
|
||||
|
||||
@@ -3,16 +3,15 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/log"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
jsonconf "github.com/v2ray/v2ray-core/config/json"
|
||||
|
||||
// The following are neccesary as they register handlers in their init functions.
|
||||
_ "github.com/v2ray/v2ray-core/net/freedom"
|
||||
_ "github.com/v2ray/v2ray-core/net/socks"
|
||||
_ "github.com/v2ray/v2ray-core/net/vmess"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/freedom"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/socks"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/vmess"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -44,24 +43,12 @@ func main() {
|
||||
if configFile == nil || len(*configFile) == 0 {
|
||||
panic(log.Error("Config file is not set."))
|
||||
}
|
||||
rawVConfig, err := ioutil.ReadFile(*configFile)
|
||||
config, err := jsonconf.LoadConfig(*configFile)
|
||||
if err != nil {
|
||||
panic(log.Error("Failed to read config file (%s): %v", *configFile, err))
|
||||
}
|
||||
vconfig, err := core.LoadConfig(rawVConfig)
|
||||
if err != nil {
|
||||
panic(log.Error("Failed to parse Config: %v", err))
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(vconfig.InboundConfig.File) && len(vconfig.InboundConfig.File) > 0 {
|
||||
vconfig.InboundConfig.File = filepath.Join(filepath.Dir(*configFile), vconfig.InboundConfig.File)
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(vconfig.OutboundConfig.File) && len(vconfig.OutboundConfig.File) > 0 {
|
||||
vconfig.OutboundConfig.File = filepath.Join(filepath.Dir(*configFile), vconfig.OutboundConfig.File)
|
||||
}
|
||||
|
||||
vPoint, err := core.NewPoint(vconfig)
|
||||
vPoint, err := core.NewPoint(config)
|
||||
if err != nil {
|
||||
panic(log.Error("Failed to create Point server: %v", err))
|
||||
}
|
||||
|
||||
12
spec/en/README.md
Normal file
12
spec/en/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Project V2Ray
|
||||
|
||||
[](https://travis-ci.org/v2ray/v2ray-core)
|
||||
[](https://coveralls.io/github/v2ray/v2ray-core?branch=master)
|
||||
[](https://godoc.org/github.com/v2ray/v2ray-core)
|
||||

|
||||
|
||||
[中文](https://github.com/V2Ray/v2ray-core/blob/master/README.md) | [English](https://github.com/V2Ray/v2ray-core/blob/master/spec/en/README.md)
|
||||
|
||||
V2Ray provides building blocks for network proxy, tunnel protocol development. It aims for easy further development of proxy software, to help people fight against internet censorship.
|
||||
|
||||
For now this project focus on defeating [the Great Firewall of China](https://en.wikipedia.org/wiki/Great_Firewall), and thus most documents are provided in Chinese. Internationlization work will be done later.
|
||||
@@ -43,9 +43,9 @@
|
||||
"vnext": [
|
||||
{
|
||||
"address": "127.0.0.1", // Point B 的 IP 地址,IPv4 或 IPv6,不支持域名
|
||||
"port": 27183, // Point B 的监听端口
|
||||
"port": 27183, // Point B 的监听端口,请更换成其它的值
|
||||
"users": [
|
||||
{"id": "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"} // 用户 ID,必须包含在 Point B 的配置文件中
|
||||
{"id": "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"} // 用户 ID,必须包含在 Point B 的配置文件中。此 ID 将被用于通信的认证,请自行更换随机的 ID,可以使用 https://www.uuidgenerator.net/ 来生成新的 ID。
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -56,13 +56,13 @@
|
||||
示例配置保存于 vpoint_vmess_freedom.json 文件中,格式如下:
|
||||
```javascript
|
||||
{
|
||||
"port": 27183, // 监听端口
|
||||
"port": 27183, // 监听端口,必须和 out_vmess.json 中指定的一致
|
||||
"inbound": {
|
||||
"protocol": "vmess", // 中继协议
|
||||
"protocol": "vmess", // 中继协议,不用改
|
||||
"file": "in_vmess.json" // vmess 配置文件
|
||||
},
|
||||
"outbound": {
|
||||
"protocol": "freedom", // 出口协议,暂时只有这一个,不用改
|
||||
"protocol": "freedom", // 出口协议,不用改
|
||||
"file": "" // 暂无配置
|
||||
}
|
||||
}
|
||||
@@ -73,11 +73,15 @@
|
||||
// in_vmess.json
|
||||
{
|
||||
"clients": [
|
||||
{"id": "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"} // 认可的用户 ID
|
||||
{"id": "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"} // 认可的用户 ID,必须包含 out_vmess.json 中的用户 ID
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 其它
|
||||
* V2Ray 的用户验证基于时间,请确保 A 和 B 所在机器的系统时间误差在一分钟以内。
|
||||
* json 配置文件实际上不支持注释(即“//”之后的部分,在使用时请务必删去)。
|
||||
|
||||
## 运行
|
||||
|
||||
Point Server A
|
||||
|
||||
34
spec/roadmap.md
Normal file
34
spec/roadmap.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# V2Ray 开发计划
|
||||
|
||||
## 版本号
|
||||
V2Ray 的版本号形如 X.Y,其中 X 表示 Milestone,Y 表示 Release,如 2.3 表示第二个 Milestone 的第三个 Release。
|
||||
|
||||
## 周期
|
||||
V2Ray 将在每周一发布一个 [Release](https://github.com/v2ray/v2ray-core/releases),每 12 周左右完成一个 Milestone。
|
||||
|
||||
## Milestones
|
||||
|
||||
### Milestone 0
|
||||
**目标:可用**
|
||||
|
||||
M0 将提供一个可用的 V2Ray Point Server,包含 Windows、Mac OS 和 Linux(Debian 为主)的预编译文件。主要功能和限制如下:
|
||||
* SOCKS 4 / 5 协议,仅提供 TCP 代理;
|
||||
* 使用自有 [VMess 协议](https://github.com/V2Ray/v2ray-core/blob/master/spec/vmess.md)做隧道;
|
||||
* 服务器端支持多用户;
|
||||
* 客户端支持多服务器,暂不支持负载平衡,多服务器时随机选择服务器;
|
||||
|
||||
### Milestone 1
|
||||
**目标:兼容**
|
||||
|
||||
M1 将完成在服务器端对兼容 Shadowsocks 和 GoAgent 协议的兼容,为用户提供多种选择。期望的功能如下:
|
||||
* SOCKS 协议的 UDP 代理;
|
||||
* 可选择路由,不必要的网站不使用代理;
|
||||
* WebSocket 代理;
|
||||
|
||||
### Milestone 2
|
||||
**目标:交互**
|
||||
|
||||
M2 将提供必要的 API 供第三方程序调用。主要功能如下:
|
||||
* 可以查询 V2Ray 进程的当前状态;
|
||||
* 可以远程和 V2Ray 进程通信并控制;
|
||||
* 可以动态管理用户和服务器列表;
|
||||
@@ -1,25 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "mode: set" > coverall.out
|
||||
fail=0
|
||||
FAIL=0
|
||||
|
||||
for dir in $(find . -maxdepth 10 -not -path './.git*' -type d);
|
||||
do
|
||||
if ls $dir/*.go &> /dev/null; then
|
||||
go test -coverprofile=coversingle.out $dir || fail=1
|
||||
if [ -f coversingle.out ]
|
||||
then
|
||||
cat coversingle.out | grep -v "mode: set" >> coverall.out
|
||||
rm coversingle.out
|
||||
fi
|
||||
function join { local IFS="$1"; shift; echo "$*"; }
|
||||
|
||||
function test_package {
|
||||
DIR="github.com/v2ray/v2ray-core/$1"
|
||||
DEP=$2
|
||||
IFS=',' read -ra DEPS <<< "$DEP"
|
||||
DEPS=("${DEPS[@]/#/github.com/v2ray/v2ray-core/}")
|
||||
DEP=$(join , "${DEPS[@]}")
|
||||
go test -coverprofile=coversingle.out -coverpkg=$DEP $DIR || FAIL=1
|
||||
if [ -f coversingle.out ]; then
|
||||
cat coversingle.out | grep -v "mode: set" >> coverall.out
|
||||
rm coversingle.out
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
if [ "$fail" -eq 0 ]
|
||||
then
|
||||
touch coverall.out
|
||||
|
||||
test_package "common/net" "common/net"
|
||||
test_package "config/json" "config/json"
|
||||
test_package "proxy/socks" "proxy/socks,proxy/socks/protocol"
|
||||
test_package "proxy/socks/protocol" "proxy/socks/protocol"
|
||||
test_package "proxy/vmess" "common/io,common/net,proxy/vmess,proxy/vmess/protocol,proxy/vmess/protocol/user"
|
||||
test_package "proxy/vmess/protocol" "proxy/vmess/protocol,proxy/vmess/protocol/user"
|
||||
test_package "proxy/vmess/protocol/user" "proxy/vmess/protocol/user"
|
||||
|
||||
cat coverall.out | sort -t: -k1 > coverallsorted.out
|
||||
echo "mode: set" | cat - coverallsorted.out > coverall.out
|
||||
rm coverallsorted.out
|
||||
|
||||
if [ "$FAIL" -eq 0 ]; then
|
||||
$HOME/gopath/bin/goveralls -v -coverprofile=coverall.out -service=travis-ci -repotoken $COVERALLS_TOKEN
|
||||
fi
|
||||
|
||||
rm -f coverall.out
|
||||
|
||||
exit $fail
|
||||
exit $FAIL
|
||||
|
||||
36
testing/mocks/config.go
Normal file
36
testing/mocks/config.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core"
|
||||
)
|
||||
|
||||
type ConnectionConfig struct {
|
||||
ProtocolValue string
|
||||
ContentValue []byte
|
||||
}
|
||||
|
||||
func (config *ConnectionConfig) Protocol() string {
|
||||
return config.ProtocolValue
|
||||
}
|
||||
|
||||
func (config *ConnectionConfig) Content() []byte {
|
||||
return config.ContentValue
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
PortValue uint16
|
||||
InboundConfigValue *ConnectionConfig
|
||||
OutboundConfigValue *ConnectionConfig
|
||||
}
|
||||
|
||||
func (config *Config) Port() uint16 {
|
||||
return config.PortValue
|
||||
}
|
||||
|
||||
func (config *Config) InboundConfig() core.ConnectionConfig {
|
||||
return config.InboundConfigValue
|
||||
}
|
||||
|
||||
func (config *Config) OutboundConfig() core.ConnectionConfig {
|
||||
return config.OutboundConfigValue
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
v2net "github.com/v2ray/v2ray-core/net"
|
||||
)
|
||||
|
||||
type FakeOutboundConnectionHandler struct {
|
||||
Data2Send *bytes.Buffer
|
||||
Data2Return []byte
|
||||
Destination v2net.Address
|
||||
}
|
||||
|
||||
func (handler *FakeOutboundConnectionHandler) Start(ray core.OutboundRay) error {
|
||||
input := ray.OutboundInput()
|
||||
output := ray.OutboundOutput()
|
||||
|
||||
output <- handler.Data2Return
|
||||
for {
|
||||
data, open := <-input
|
||||
if !open {
|
||||
break
|
||||
}
|
||||
handler.Data2Send.Write(data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (handler *FakeOutboundConnectionHandler) Create(point *core.Point, dest v2net.Address) (core.OutboundConnectionHandler, error) {
|
||||
return handler, nil
|
||||
}
|
||||
38
testing/mocks/inboundhandler.go
Normal file
38
testing/mocks/inboundhandler.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
type InboundConnectionHandler struct {
|
||||
Data2Send []byte
|
||||
DataReturned *bytes.Buffer
|
||||
Port uint16
|
||||
Server *core.Point
|
||||
}
|
||||
|
||||
func (handler *InboundConnectionHandler) Listen(port uint16) error {
|
||||
handler.Port = port
|
||||
return nil
|
||||
}
|
||||
|
||||
func (handler *InboundConnectionHandler) Communicate(dest v2net.Destination) error {
|
||||
ray := handler.Server.NewInboundConnectionAccepted(dest)
|
||||
|
||||
input := ray.InboundInput()
|
||||
output := ray.InboundOutput()
|
||||
|
||||
input <- handler.Data2Send
|
||||
close(input)
|
||||
|
||||
v2net.ChanToWriter(handler.DataReturned, output)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (handler *InboundConnectionHandler) Create(point *core.Point, config []byte) (core.InboundConnectionHandler, error) {
|
||||
handler.Server = point
|
||||
return handler, nil
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
|
||||
)
|
||||
|
||||
type MockUserSet struct {
|
||||
UserIds []core.ID
|
||||
UserIds []user.ID
|
||||
UserHashes map[string]int
|
||||
Timestamps map[string]int64
|
||||
}
|
||||
|
||||
func (us *MockUserSet) AddUser(user core.User) error {
|
||||
func (us *MockUserSet) AddUser(user user.User) error {
|
||||
us.UserIds = append(us.UserIds, user.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (us *MockUserSet) GetUser(userhash []byte) (*core.ID, int64, bool) {
|
||||
func (us *MockUserSet) GetUser(userhash []byte) (*user.ID, int64, bool) {
|
||||
idx, found := us.UserHashes[string(userhash)]
|
||||
if found {
|
||||
return &us.UserIds[idx], us.Timestamps[string(userhash)], true
|
||||
|
||||
38
testing/mocks/outboundhandler.go
Normal file
38
testing/mocks/outboundhandler.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
type OutboundConnectionHandler struct {
|
||||
Data2Send *bytes.Buffer
|
||||
Data2Return []byte
|
||||
Destination v2net.Destination
|
||||
}
|
||||
|
||||
func (handler *OutboundConnectionHandler) Start(ray core.OutboundRay) error {
|
||||
input := ray.OutboundInput()
|
||||
output := ray.OutboundOutput()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
data, open := <-input
|
||||
if !open {
|
||||
break
|
||||
}
|
||||
handler.Data2Send.Write(data)
|
||||
}
|
||||
output <- handler.Data2Return
|
||||
close(output)
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (handler *OutboundConnectionHandler) Create(point *core.Point, config []byte, dest v2net.Destination) (core.OutboundConnectionHandler, error) {
|
||||
handler.Destination = dest
|
||||
return handler, nil
|
||||
}
|
||||
@@ -47,3 +47,7 @@ func (a *Assertion) Error(value error) *ErrorSubject {
|
||||
func (a *Assertion) Bool(value bool) *BoolSubject {
|
||||
return NewBoolSubject(NewSubject(a), value)
|
||||
}
|
||||
|
||||
func (a *Assertion) Pointer(value interface{}) *PointerSubject {
|
||||
return NewPointerSubject(NewSubject(a), value)
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ type BytesSubject struct {
|
||||
}
|
||||
|
||||
func NewBytesSubject(base *Subject, value []byte) *BytesSubject {
|
||||
subject := new(BytesSubject)
|
||||
subject.Subject = base
|
||||
subject.value = value
|
||||
return subject
|
||||
return &BytesSubject{
|
||||
Subject: base,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *BytesSubject) Named(name string) *BytesSubject {
|
||||
|
||||
@@ -10,10 +10,10 @@ type ByteSubject struct {
|
||||
}
|
||||
|
||||
func NewByteSubject(base *Subject, value byte) *ByteSubject {
|
||||
subject := new(ByteSubject)
|
||||
subject.Subject = base
|
||||
subject.value = value
|
||||
return subject
|
||||
return &ByteSubject{
|
||||
Subject: base,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *ByteSubject) Named(name string) *ByteSubject {
|
||||
|
||||
@@ -6,10 +6,10 @@ type ErrorSubject struct {
|
||||
}
|
||||
|
||||
func NewErrorSubject(base *Subject, value error) *ErrorSubject {
|
||||
subject := new(ErrorSubject)
|
||||
subject.Subject = base
|
||||
subject.value = value
|
||||
return subject
|
||||
return &ErrorSubject{
|
||||
Subject: base,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *ErrorSubject) Named(name string) *ErrorSubject {
|
||||
|
||||
@@ -10,10 +10,10 @@ type IntSubject struct {
|
||||
}
|
||||
|
||||
func NewIntSubject(base *Subject, value int) *IntSubject {
|
||||
subject := new(IntSubject)
|
||||
subject.Subject = base
|
||||
subject.value = value
|
||||
return subject
|
||||
return &IntSubject{
|
||||
Subject: base,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *IntSubject) Named(name string) *IntSubject {
|
||||
|
||||
48
testing/unit/pointersubject.go
Normal file
48
testing/unit/pointersubject.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package unit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type PointerSubject struct {
|
||||
*Subject
|
||||
value interface{}
|
||||
}
|
||||
|
||||
func NewPointerSubject(base *Subject, value interface{}) *PointerSubject {
|
||||
return &PointerSubject{
|
||||
Subject: base,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *PointerSubject) Named(name string) *PointerSubject {
|
||||
subject.Subject.Named(name)
|
||||
return subject
|
||||
}
|
||||
|
||||
func (subject *PointerSubject) Fail(verb string, other interface{}) {
|
||||
subject.FailWithMessage(fmt.Sprintf("Not true that %s %s <%v>.", subject.DisplayString(), verb, other))
|
||||
}
|
||||
|
||||
func (subject *PointerSubject) DisplayString() string {
|
||||
return subject.Subject.DisplayString(fmt.Sprintf("%v", subject.value))
|
||||
}
|
||||
|
||||
func (subject *PointerSubject) Equals(expectation interface{}) {
|
||||
if subject.value != expectation {
|
||||
subject.Fail("is equal to", expectation)
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *PointerSubject) IsNil() {
|
||||
if subject.value != nil {
|
||||
subject.FailWithMessage("Not true that " + subject.DisplayString() + " is nil.")
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *PointerSubject) IsNotNil() {
|
||||
if subject.value == nil {
|
||||
subject.FailWithMessage("Not true that " + subject.DisplayString() + " is not nil.")
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,10 @@ type StringSubject struct {
|
||||
}
|
||||
|
||||
func NewStringSubject(base *Subject, value string) *StringSubject {
|
||||
subject := new(StringSubject)
|
||||
subject.Subject = base
|
||||
subject.value = value
|
||||
return subject
|
||||
return &StringSubject{
|
||||
Subject: base,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *StringSubject) Named(name string) *StringSubject {
|
||||
|
||||
@@ -6,10 +6,10 @@ type Subject struct {
|
||||
}
|
||||
|
||||
func NewSubject(assert *Assertion) *Subject {
|
||||
subject := new(Subject)
|
||||
subject.assert = assert
|
||||
subject.name = ""
|
||||
return subject
|
||||
return &Subject{
|
||||
assert: assert,
|
||||
name: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *Subject) FailWithMessage(message string) {
|
||||
|
||||
@@ -10,10 +10,10 @@ type Uint16Subject struct {
|
||||
}
|
||||
|
||||
func NewUint16Subject(base *Subject, value uint16) *Uint16Subject {
|
||||
subject := new(Uint16Subject)
|
||||
subject.Subject = base
|
||||
subject.value = value
|
||||
return subject
|
||||
return &Uint16Subject{
|
||||
Subject: base,
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *Uint16Subject) Named(name string) *Uint16Subject {
|
||||
@@ -46,3 +46,9 @@ func (subject *Uint16Subject) LessThan(expectation uint16) {
|
||||
subject.Fail("is less than", expectation)
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *Uint16Subject) Positive() {
|
||||
if subject.value <= 0 {
|
||||
subject.FailWithMessage("Not true that " + subject.DisplayString() + " is positive.")
|
||||
}
|
||||
}
|
||||
|
||||
86
userset.go
86
userset.go
@@ -1,86 +0,0 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
v2hash "github.com/v2ray/v2ray-core/hash"
|
||||
)
|
||||
|
||||
const (
|
||||
updateIntervalSec = 10
|
||||
cacheDurationSec = 120
|
||||
)
|
||||
|
||||
type UserSet interface {
|
||||
AddUser(user User) error
|
||||
GetUser(timeHash []byte) (*ID, int64, bool)
|
||||
}
|
||||
|
||||
type TimedUserSet struct {
|
||||
validUserIds []ID
|
||||
userHashes map[string]indexTimePair
|
||||
}
|
||||
|
||||
type indexTimePair struct {
|
||||
index int
|
||||
timeSec int64
|
||||
}
|
||||
|
||||
type hashEntry struct {
|
||||
hash string
|
||||
timeSec int64
|
||||
}
|
||||
|
||||
func NewTimedUserSet() UserSet {
|
||||
vuSet := new(TimedUserSet)
|
||||
vuSet.validUserIds = make([]ID, 0, 16)
|
||||
vuSet.userHashes = make(map[string]indexTimePair)
|
||||
|
||||
go vuSet.updateUserHash(time.Tick(updateIntervalSec * time.Second))
|
||||
return vuSet
|
||||
}
|
||||
|
||||
func (us *TimedUserSet) updateUserHash(tick <-chan time.Time) {
|
||||
now := time.Now().UTC()
|
||||
lastSec := now.Unix() - cacheDurationSec
|
||||
|
||||
hash2Remove := make(chan hashEntry, cacheDurationSec*3*len(us.validUserIds))
|
||||
lastSec2Remove := now.Unix()
|
||||
idHash := v2hash.NewTimeHash(v2hash.HMACHash{})
|
||||
for {
|
||||
now := <-tick
|
||||
nowSec := now.UTC().Unix()
|
||||
|
||||
remove2Sec := nowSec - cacheDurationSec
|
||||
if remove2Sec > lastSec2Remove {
|
||||
for lastSec2Remove+1 < remove2Sec {
|
||||
entry := <-hash2Remove
|
||||
lastSec2Remove = entry.timeSec
|
||||
delete(us.userHashes, entry.hash)
|
||||
}
|
||||
}
|
||||
|
||||
for lastSec < nowSec+cacheDurationSec {
|
||||
for idx, id := range us.validUserIds {
|
||||
idHash := idHash.Hash(id.Bytes, lastSec)
|
||||
hash2Remove <- hashEntry{string(idHash), lastSec}
|
||||
us.userHashes[string(idHash)] = indexTimePair{idx, lastSec}
|
||||
}
|
||||
lastSec++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (us *TimedUserSet) AddUser(user User) error {
|
||||
id := user.Id
|
||||
us.validUserIds = append(us.validUserIds, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (us TimedUserSet) GetUser(userHash []byte) (*ID, int64, bool) {
|
||||
pair, found := us.userHashes[string(userHash)]
|
||||
if found {
|
||||
return &us.validUserIds[pair.index], pair.timeSec, true
|
||||
}
|
||||
return nil, 0, false
|
||||
}
|
||||
Reference in New Issue
Block a user