Compare commits

..

32 Commits
v0.2 ... v0.5

Author SHA1 Message Date
V2Ray
cb16047cf0 prefer interface over struct 2015-09-20 18:22:29 +02:00
V2Ray
823cbf1509 Split Address struct to save some bytes 2015-09-20 16:55:45 +02:00
V2Ray
b319704282 Add network type to destination to be ready for UDP connection 2015-09-20 16:03:12 +02:00
V2Ray
5d5102de4c code cleanup 2015-09-20 13:08:05 +02:00
V2Ray
ad613a9e3e license badge 2015-09-20 12:18:25 +02:00
V2Ray
b22f306a3d update test code 2015-09-20 11:51:24 +02:00
V2Ray
521db6bdb4 update coverall script 2015-09-20 11:47:39 +02:00
V2Ray
07d4495b27 test cases for json config 2015-09-20 11:45:40 +02:00
V2Ray
a887fd01b8 Move log into common 2015-09-20 00:50:21 +02:00
V2Ray
d03cac9670 Orgnize coverage info 2015-09-20 00:43:42 +02:00
V2Ray
b40be74b86 Fix test break 2015-09-20 00:11:14 +02:00
V2Ray
075753c030 Massive refactoring for better code structure 2015-09-19 23:54:36 +02:00
V2Ray
5eee1b97aa Massive refactoring for better code structure 2015-09-19 23:54:12 +02:00
V2Ray
86315633e1 update coverall script 2015-09-19 22:06:32 +02:00
V2Ray
2c0096c20a Update coverall script 2015-09-19 21:58:52 +02:00
V2Ray
76200e2764 test case for net.vmess 2015-09-19 21:56:09 +02:00
V2Ray
ab723fa0b5 Generate user hash on demand 2015-09-19 21:56:00 +02:00
V2Ray
46abe9de5e Update coverall script 2015-09-19 19:21:17 +02:00
V2Ray
3734a25cf2 Update coverall script 2015-09-19 19:06:41 +02:00
V2Ray
5b63edbdbc Update coverall script to get better coverage info 2015-09-19 18:55:43 +02:00
V2Ray
fe719cb03d test case for user pass auth 2015-09-19 15:42:46 +02:00
V2Ray
450fef27f6 Remove unnecessary tip target. 2015-09-19 15:36:33 +02:00
V2Ray
8be5d695c8 test case for socks.net 2015-09-19 15:35:20 +02:00
V2Ray
729312f3b5 Convert point server config to interface for testibility 2015-09-19 15:07:10 +02:00
V2Ray
76cb92f918 Update user guide 2015-09-19 11:55:59 +02:00
V2Ray
c09ca34ada roadmap 2015-09-19 11:46:06 +02:00
V2Ray
e2d4d44c77 Update guide for time sync 2015-09-18 19:54:01 +02:00
V2Ray
1b24f2f9ae english readme 2015-09-18 15:14:14 +02:00
V2Ray
0c2fd04f0e Typo 2015-09-18 14:59:02 +02:00
V2Ray
f39d6077b6 bump version 2015-09-18 14:40:15 +02:00
V2Ray
33fa7dbd3c Update travis.yml 2015-09-18 14:39:54 +02:00
V2Ray
f27e8308cb Another test for travis 2015-09-18 14:35:50 +02:00
61 changed files with 1154 additions and 528 deletions

View File

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

View File

@@ -3,6 +3,9 @@
[![Build Status](https://travis-ci.org/v2ray/v2ray-core.svg?branch=master)](https://travis-ci.org/v2ray/v2ray-core)
[![Coverage Status](https://coveralls.io/repos/v2ray/v2ray-core/badge.svg?branch=master&service=github)](https://coveralls.io/github/v2ray/v2ray-core?branch=master)
[![GoDoc](https://godoc.org/github.com/v2ray/v2ray-core?status.svg)](https://godoc.org/github.com/v2ray/v2ray-core)
![License](https://img.shields.io/github/license/v2ray/v2ray-core.svg)
[中文](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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
package socks
package protocol
import (
"bytes"

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
package core
package user
import (
"testing"

View File

@@ -1,4 +1,4 @@
package hash
package user
import (
"crypto/hmac"

View File

@@ -1,4 +1,4 @@
package hash
package user
import (
"crypto/md5"

View File

@@ -1,4 +1,4 @@
package math
package user
import (
"math/rand"

View File

@@ -1,4 +1,4 @@
package math
package user
import (
"testing"

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@@ -0,0 +1,12 @@
# Project V2Ray
[![Build Status](https://travis-ci.org/v2ray/v2ray-core.svg?branch=master)](https://travis-ci.org/v2ray/v2ray-core)
[![Coverage Status](https://coveralls.io/repos/v2ray/v2ray-core/badge.svg?branch=master&service=github)](https://coveralls.io/github/v2ray/v2ray-core?branch=master)
[![GoDoc](https://godoc.org/github.com/v2ray/v2ray-core?status.svg)](https://godoc.org/github.com/v2ray/v2ray-core)
![License](https://img.shields.io/github/license/v2ray/v2ray-core.svg)
[中文](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.

View File

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

@@ -0,0 +1,34 @@
# V2Ray 开发计划
## 版本号
V2Ray 的版本号形如 X.Y其中 X 表示 MilestoneY 表示 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 和 LinuxDebian 为主)的预编译文件。主要功能和限制如下:
* 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 进程通信并控制;
* 可以动态管理用户和服务器列表;

View File

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

View File

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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