Compare commits

..

74 Commits

Author SHA1 Message Date
V2Ray
da1b428bb4 Fix default config file 2015-11-09 22:31:19 +01:00
V2Ray
a3a51b7b30 Update coverall 2015-11-06 13:56:33 +01:00
V2Ray
5347673e00 test case for socks udp 2015-11-06 13:45:41 +01:00
V2Ray
6ce1539bca Update coverall script 2015-11-06 13:28:34 +01:00
V2Ray
e60fcba4b3 Test cases for Socks end 2 end. 2015-11-06 13:08:20 +01:00
V2Ray
fdc72ed8c9 Move mocked userset to vmess/protocol/user 2015-11-04 23:04:00 +01:00
V2Ray
1d4b541d2f Move mock config to app/config 2015-11-04 23:01:04 +01:00
V2Ray
346b6125f4 test cases for Destination 2015-11-04 22:48:54 +01:00
V2Ray
31fb65b3d8 Remove VMess UDP 2015-11-04 21:52:48 +01:00
V2Ray
581e6b7104 More test case for building 2015-11-04 21:20:06 +01:00
V2Ray
8204c9923d typo 2015-11-04 17:57:11 +01:00
V2Ray
602afca3e3 Test case for macos building 2015-11-04 17:52:59 +01:00
V2Ray
0a8e283e55 function to create router 2015-11-03 23:24:56 +01:00
V2Ray
0d06561b7e More test case 2015-11-03 22:27:26 +01:00
V2Ray
c144e77eb3 Refactor socks config 2015-11-03 22:23:50 +01:00
V2Ray
654cdf18d9 Refactor socks json config 2015-11-03 22:09:07 +01:00
V2Ray
a46db069fb Refactor AES encryption/decryption 2015-11-03 21:26:16 +01:00
V2Ray
3f884ed924 More test case for stream logger 2015-11-03 20:30:14 +01:00
V2Ray
144141b3c3 test case for stream logger 2015-11-03 20:24:29 +01:00
V2Ray
cb19e4613f Update port range test 2015-11-03 20:07:46 +01:00
V2Ray
6faff6d514 test case for socks udp protocol 2015-11-03 18:33:58 +01:00
V2Ray
d9ebd008d3 Check lenth of the udp packet before parsing 2015-11-03 18:20:28 +01:00
V2Ray
d58384ced0 One more test case for retry 2015-11-03 00:12:25 +01:00
V2Ray
f080f36372 format code 2015-11-03 00:07:19 +01:00
V2Ray
60b116fbde Test case for retry 2015-11-03 00:07:15 +01:00
V2Ray
1edd0e660e Remove redunent code 2015-11-02 23:55:10 +01:00
V2Ray
8fbb9762db typo 2015-11-02 23:54:11 +01:00
V2Ray
1a4405dbe1 Remove redunent code 2015-11-02 23:52:22 +01:00
V2Ray
f2cf4a1f89 Test case for too-short request 2015-11-02 23:48:47 +01:00
V2Ray
dcdce6696b Update version for next release 2015-11-02 00:07:28 +01:00
V2Ray
e70c02f768 Fix test break 2015-11-02 00:06:56 +01:00
V2Ray
bbac647656 Update user id 2015-11-02 00:05:32 +01:00
V2Ray
590699fc7b Update ports 2015-11-02 00:04:11 +01:00
V2Ray
fae78683f9 Update DNS port 2015-11-02 00:02:46 +01:00
V2Ray
1cec5ac643 Test case for inbound detour network 2015-11-01 23:01:15 +01:00
V2Ray
1c6b66eacf Test case for dokodemo 2015-11-01 22:17:49 +01:00
V2Ray
03b8c8f9e8 Port picker 2015-11-01 21:32:08 +01:00
V2Ray
192960b711 Move mocked connection handlers to proxy/testing/mocks 2015-11-01 21:15:08 +01:00
V2Ray
f983d285b8 detour handler 2015-11-01 00:11:41 +01:00
V2Ray
3c43268898 inbound detour 2015-10-31 22:22:43 +01:00
V2Ray
1708c48f5b Port range 2015-10-31 21:43:26 +01:00
V2Ray
1c09b70931 format code 2015-10-31 14:08:13 +01:00
V2Ray
3765826602 Test case for config cache. 2015-10-31 14:08:09 +01:00
V2Ray
46c0d457d9 User settings for trusted user 2015-10-31 09:39:45 +01:00
V2Ray
b44098d752 Introduce user level 2015-10-31 00:38:31 +01:00
V2Ray
d585ec0593 Move config to app/point 2015-10-30 22:50:45 +01:00
V2Ray
2b45e63607 Move config cache to proxy/common 2015-10-30 22:42:24 +01:00
V2Ray
c56e17fff9 Dokodemo proxy 2015-10-30 15:56:46 +01:00
V2Ray
f93b29993b Move connection handler interfaces to proxy/common/connhandler 2015-10-30 00:11:29 +01:00
V2Ray
361a22d74d Move proxy errors to proxy/common/errors 2015-10-29 23:59:57 +01:00
V2Ray
ce07ea9769 Split json config files 2015-10-29 23:41:37 +01:00
V2Ray
6e8425b23a Remove config type in connection settings() 2015-10-29 23:35:54 +01:00
V2Ray
fdb41bbd50 Remove call to UTC() which is not necessary. 2015-10-29 12:04:14 +01:00
V2Ray
d0ae47e2ce Router interface 2015-10-28 23:18:07 +01:00
V2Ray
9b8632d01a BlackHole proxy 2015-10-28 17:41:14 +01:00
V2Ray
fdb952fb01 Merge branch 'master' of https://github.com/v2ray/v2ray-core 2015-10-28 12:13:43 +01:00
V2Ray
08328d23fa beginning of http proxy 2015-10-28 12:13:27 +01:00
V2 Ray
65bc306ba7 Update README.md 2015-10-27 12:50:25 +01:00
V2Ray
294376edc0 Remove unused const 2015-10-25 21:44:08 +01:00
V2Ray
2f721c4006 Bump version for 0.10 release 2015-10-25 21:39:44 +01:00
V2Ray
58dcfd5e84 Reduce timeout for reading VMess header 2015-10-21 23:10:03 +02:00
V2Ray
bbc41c5e63 Increase small buffer pool 2015-10-21 22:39:36 +02:00
V2Ray
f474fc32a7 Remove all []byte allocation 2015-10-21 22:38:53 +02:00
V2Ray
96c5d32d59 Remove []byte allocation in vmess 2015-10-21 22:28:26 +02:00
V2Ray
2c710d6b1c Protect from DoS attack 2015-10-21 21:53:55 +02:00
V2Ray
280c138cb4 Remove Milestone description as plan changes 2015-10-19 15:30:10 +02:00
V2Ray
462aa6f041 Remove failing tests 2015-10-19 13:17:18 +02:00
V2Ray
cf0e0b579b Add IP section in socks doc 2015-10-19 10:33:11 +02:00
V2 Ray
481c367154 Merge pull request #46 from v2ray/revert-45-patch-2
Revert "Update guide.md"
2015-10-19 10:31:15 +02:00
V2 Ray
401d61cb5f Revert "Update guide.md" 2015-10-19 10:30:58 +02:00
V2Ray
6fefaaaf5a Merge branch 'master' of https://github.com/v2ray/v2ray-core 2015-10-19 10:30:15 +02:00
V2 Ray
30d7b39410 Merge pull request #45 from kxjhlele/patch-2
Update guide.md
2015-10-19 10:25:37 +02:00
kxjhlele
f44d270c88 Update guide.md
更新配置文件 vpoint_vmess_freedom.json
2015-10-19 15:01:30 +08:00
V2Ray
c4ab202c11 Update version structure 2015-10-18 20:52:38 +02:00
92 changed files with 2330 additions and 904 deletions

View File

@@ -9,25 +9,7 @@
V2Ray 是一个模块化的代理软件包,它的目标是提供常用的代理软件模块,简化网络代理软件的开发。
## 联系方式
* 聊天室https://gitter.im/v2ray/v2ray-core
* Twitterhttps://twitter.com/projectv2ray
* 私下联系love@v2ray.com
* [捐赠](https://github.com/v2ray/v2ray-core/blob/master/spec/donate.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/errors.md)
* [性能测试](https://github.com/V2Ray/v2ray-core/blob/master/spec/benchmark.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如果你对本项目感兴趣并想做点贡献请挑选其中之一完善之不甚感激。
* [开发指引](https://github.com/V2Ray/v2ray-core/blob/master/spec/develop.md)
[官方网站](https://www.v2ray.com/)
## License
[The MIT License (MIT)](https://raw.githubusercontent.com/v2ray/v2ray-core/master/LICENSE)

View File

@@ -0,0 +1,36 @@
package config
type RouterConfig interface {
Strategy() string
Settings() interface{}
}
type ConnectionTag string
type ConnectionConfig interface {
Protocol() string
Settings() interface{}
}
type LogConfig interface {
AccessLog() string
}
type PortRange interface {
From() uint16
To() uint16
}
type InboundDetourConfig interface {
Protocol() string
PortRange() PortRange
Settings() interface{}
}
type PointConfig interface {
Port() uint16
LogConfig() LogConfig
InboundConfig() ConnectionConfig
OutboundConfig() ConnectionConfig
InboundDetours() []InboundDetourConfig
}

View File

@@ -0,0 +1,36 @@
package json
import (
"encoding/json"
"github.com/v2ray/v2ray-core/common/log"
proxyconfig "github.com/v2ray/v2ray-core/proxy/common/config"
proxyjson "github.com/v2ray/v2ray-core/proxy/common/config/json"
)
type ConnectionConfig struct {
ProtocolString string `json:"protocol"`
SettingsMessage json.RawMessage `json:"settings"`
Type proxyconfig.Type `json:"-"`
}
func (c *ConnectionConfig) Protocol() string {
return c.ProtocolString
}
func (c *ConnectionConfig) Settings() interface{} {
return loadConnectionConfig(c.SettingsMessage, c.Protocol(), c.Type)
}
func loadConnectionConfig(message json.RawMessage, protocol string, cType proxyconfig.Type) interface{} {
configObj := proxyjson.CreateConfig(protocol, cType)
if configObj == nil {
panic("Unknown protocol " + protocol)
}
err := json.Unmarshal(message, configObj)
if err != nil {
log.Error("Unable to parse connection config: %v", err)
panic("Failed to parse connection config.")
}
return configObj
}

View File

@@ -0,0 +1,26 @@
package json
import (
"encoding/json"
"github.com/v2ray/v2ray-core/app/point/config"
proxyconfig "github.com/v2ray/v2ray-core/proxy/common/config"
)
type InboundDetourConfig struct {
ProtocolValue string `json:"protocol"`
PortRangeValue *PortRange `json:"port"`
SettingsValue json.RawMessage `json:"settings"`
}
func (this *InboundDetourConfig) Protocol() string {
return this.ProtocolValue
}
func (this *InboundDetourConfig) PortRange() config.PortRange {
return this.PortRangeValue
}
func (this *InboundDetourConfig) Settings() interface{} {
return loadConnectionConfig(this.SettingsValue, this.ProtocolValue, proxyconfig.TypeInbound)
}

View File

@@ -5,47 +5,18 @@ import (
"io/ioutil"
"os"
"github.com/v2ray/v2ray-core/app/point/config"
"github.com/v2ray/v2ray-core/common/log"
"github.com/v2ray/v2ray-core/config"
proxyconfig "github.com/v2ray/v2ray-core/proxy/common/config"
)
type ConnectionConfig struct {
ProtocolString string `json:"protocol"`
SettingsMessage json.RawMessage `json:"settings"`
}
func (config *ConnectionConfig) Protocol() string {
return config.ProtocolString
}
func (config *ConnectionConfig) Settings(configType config.Type) interface{} {
creator, found := configCache[getConfigKey(config.Protocol(), configType)]
if !found {
panic("Unknown protocol " + config.Protocol())
}
configObj := creator()
err := json.Unmarshal(config.SettingsMessage, configObj)
if err != nil {
log.Error("Unable to parse connection config: %v", err)
panic("Failed to parse connection config.")
}
return configObj
}
type LogConfig struct {
AccessLogValue string `json:"access"`
}
func (config *LogConfig) AccessLog() string {
return config.AccessLogValue
}
// Config is the config for Point server.
type Config struct {
PortValue uint16 `json:"port"` // Port of this Point server.
LogConfigValue *LogConfig `json:"log"`
InboundConfigValue *ConnectionConfig `json:"inbound"`
OutboundConfigValue *ConnectionConfig `json:"outbound"`
PortValue uint16 `json:"port"` // Port of this Point server.
LogConfigValue *LogConfig `json:"log"`
InboundConfigValue *ConnectionConfig `json:"inbound"`
OutboundConfigValue *ConnectionConfig `json:"outbound"`
InboundDetoursValue []*InboundDetourConfig `json:"inboundDetour"`
}
func (config *Config) Port() uint16 {
@@ -73,6 +44,14 @@ func (config *Config) OutboundConfig() config.ConnectionConfig {
return config.OutboundConfigValue
}
func (this *Config) InboundDetours() []config.InboundDetourConfig {
detours := make([]config.InboundDetourConfig, len(this.InboundDetoursValue))
for idx, detour := range this.InboundDetoursValue {
detours[idx] = detour
}
return detours
}
func LoadConfig(file string) (*Config, error) {
fixedFile := os.ExpandEnv(file)
rawConfig, err := ioutil.ReadFile(fixedFile)
@@ -88,5 +67,8 @@ func LoadConfig(file string) (*Config, error) {
return nil, err
}
jsonConfig.InboundConfigValue.Type = proxyconfig.TypeInbound
jsonConfig.OutboundConfigValue.Type = proxyconfig.TypeOutbound
return jsonConfig, err
}

View File

@@ -4,8 +4,8 @@ import (
"path/filepath"
"testing"
"github.com/v2ray/v2ray-core/config"
"github.com/v2ray/v2ray-core/config/json"
"github.com/v2ray/v2ray-core/app/point/config/json"
_ "github.com/v2ray/v2ray-core/proxy/dokodemo/config/json"
_ "github.com/v2ray/v2ray-core/proxy/freedom/config/json"
_ "github.com/v2ray/v2ray-core/proxy/socks/config/json"
_ "github.com/v2ray/v2ray-core/proxy/vmess/config/json"
@@ -27,10 +27,10 @@ func TestClientSampleConfig(t *testing.T) {
assert.Pointer(pointConfig.OutboundConfig()).IsNotNil()
assert.String(pointConfig.InboundConfig().Protocol()).Equals("socks")
assert.Pointer(pointConfig.InboundConfig().Settings(config.TypeInbound)).IsNotNil()
assert.Pointer(pointConfig.InboundConfig().Settings()).IsNotNil()
assert.String(pointConfig.OutboundConfig().Protocol()).Equals("vmess")
assert.Pointer(pointConfig.OutboundConfig().Settings(config.TypeOutbound)).IsNotNil()
assert.Pointer(pointConfig.OutboundConfig().Settings()).IsNotNil()
}
func TestServerSampleConfig(t *testing.T) {
@@ -47,8 +47,27 @@ func TestServerSampleConfig(t *testing.T) {
assert.Pointer(pointConfig.OutboundConfig()).IsNotNil()
assert.String(pointConfig.InboundConfig().Protocol()).Equals("vmess")
assert.Pointer(pointConfig.InboundConfig().Settings(config.TypeInbound)).IsNotNil()
assert.Pointer(pointConfig.InboundConfig().Settings()).IsNotNil()
assert.String(pointConfig.OutboundConfig().Protocol()).Equals("freedom")
assert.Pointer(pointConfig.OutboundConfig().Settings(config.TypeOutbound)).IsNotNil()
assert.Pointer(pointConfig.OutboundConfig().Settings()).IsNotNil()
}
func TestDetourConfig(t *testing.T) {
assert := unit.Assert(t)
// TODO: fix for Windows
baseDir := "$GOPATH/src/github.com/v2ray/v2ray-core/release/config"
pointConfig, err := json.LoadConfig(filepath.Join(baseDir, "vpoint_dns_detour.json"))
assert.Error(err).IsNil()
detours := pointConfig.InboundDetours()
assert.Int(len(detours)).Equals(1)
detour := detours[0]
assert.String(detour.Protocol()).Equals("dokodemo-door")
assert.Uint16(detour.PortRange().From()).Equals(uint16(28394))
assert.Uint16(detour.PortRange().To()).Equals(uint16(28394))
assert.Pointer(detour.Settings()).IsNotNil()
}

View File

@@ -0,0 +1,9 @@
package json
type LogConfig struct {
AccessLogValue string `json:"access"`
}
func (config *LogConfig) AccessLog() string {
return config.AccessLogValue
}

View File

@@ -0,0 +1,79 @@
package json
import (
"encoding/json"
"errors"
"strconv"
"strings"
"github.com/v2ray/v2ray-core/common/log"
)
var (
InvalidPortRange = errors.New("Invalid port range.")
)
type PortRange struct {
from uint16
to uint16
}
func (this *PortRange) From() uint16 {
return this.from
}
func (this *PortRange) To() uint16 {
return this.to
}
func (this *PortRange) UnmarshalJSON(data []byte) error {
var maybeint int
err := json.Unmarshal(data, &maybeint)
if err == nil {
if maybeint <= 0 || maybeint >= 65535 {
log.Error("Invalid port [%s]", string(data))
return InvalidPortRange
}
this.from = uint16(maybeint)
this.to = uint16(maybeint)
return nil
}
var maybestring string
err = json.Unmarshal(data, &maybestring)
if err == nil {
pair := strings.SplitN(maybestring, "-", 2)
if len(pair) == 1 {
value, err := strconv.Atoi(pair[0])
if err != nil || value <= 0 || value >= 65535 {
log.Error("Invalid from port %s", pair[0])
return InvalidPortRange
}
this.from = uint16(value)
this.to = uint16(value)
return nil
} else if len(pair) == 2 {
from, err := strconv.Atoi(pair[0])
if err != nil || from <= 0 || from >= 65535 {
log.Error("Invalid from port %s", pair[0])
return InvalidPortRange
}
this.from = uint16(from)
to, err := strconv.Atoi(pair[1])
if err != nil || to <= 0 || to >= 65535 {
log.Error("Invalid to port %s", pair[1])
return InvalidPortRange
}
this.to = uint16(to)
if this.from > this.to {
log.Error("Invalid port range %d -> %d", this.from, this.to)
return InvalidPortRange
}
return nil
}
}
return InvalidPortRange
}

View File

@@ -0,0 +1,69 @@
package json
import (
"encoding/json"
"testing"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestIntPort(t *testing.T) {
assert := unit.Assert(t)
var portRange PortRange
err := json.Unmarshal([]byte("1234"), &portRange)
assert.Error(err).IsNil()
assert.Uint16(portRange.from).Equals(uint16(1234))
assert.Uint16(portRange.to).Equals(uint16(1234))
}
func TestOverRangeIntPort(t *testing.T) {
assert := unit.Assert(t)
var portRange PortRange
err := json.Unmarshal([]byte("70000"), &portRange)
assert.Error(err).Equals(InvalidPortRange)
err = json.Unmarshal([]byte("-1"), &portRange)
assert.Error(err).Equals(InvalidPortRange)
}
func TestSingleStringPort(t *testing.T) {
assert := unit.Assert(t)
var portRange PortRange
err := json.Unmarshal([]byte("\"1234\""), &portRange)
assert.Error(err).IsNil()
assert.Uint16(portRange.from).Equals(uint16(1234))
assert.Uint16(portRange.to).Equals(uint16(1234))
}
func TestStringPairPort(t *testing.T) {
assert := unit.Assert(t)
var portRange PortRange
err := json.Unmarshal([]byte("\"1234-5678\""), &portRange)
assert.Error(err).IsNil()
assert.Uint16(portRange.from).Equals(uint16(1234))
assert.Uint16(portRange.to).Equals(uint16(5678))
}
func TestOverRangeStringPort(t *testing.T) {
assert := unit.Assert(t)
var portRange PortRange
err := json.Unmarshal([]byte("\"65536\""), &portRange)
assert.Error(err).Equals(InvalidPortRange)
err = json.Unmarshal([]byte("\"70000-80000\""), &portRange)
assert.Error(err).Equals(InvalidPortRange)
err = json.Unmarshal([]byte("\"1-90000\""), &portRange)
assert.Error(err).Equals(InvalidPortRange)
err = json.Unmarshal([]byte("\"700-600\""), &portRange)
assert.Error(err).Equals(InvalidPortRange)
}

View File

@@ -1,7 +1,7 @@
package mocks
import (
"github.com/v2ray/v2ray-core/config"
"github.com/v2ray/v2ray-core/app/point/config"
)
type ConnectionConfig struct {
@@ -13,7 +13,7 @@ func (config *ConnectionConfig) Protocol() string {
return config.ProtocolValue
}
func (config *ConnectionConfig) Settings(config.Type) interface{} {
func (config *ConnectionConfig) Settings() interface{} {
return config.SettingsValue
}
@@ -21,6 +21,28 @@ type LogConfig struct {
AccessLogValue string
}
type PortRange struct {
FromValue uint16
ToValue uint16
}
func (this *PortRange) From() uint16 {
return this.FromValue
}
func (this *PortRange) To() uint16 {
return this.ToValue
}
type InboundDetourConfig struct {
ConnectionConfig
PortRangeValue *PortRange
}
func (this *InboundDetourConfig) PortRange() config.PortRange {
return this.PortRangeValue
}
func (config *LogConfig) AccessLog() string {
return config.AccessLogValue
}
@@ -30,6 +52,7 @@ type Config struct {
LogConfigValue *LogConfig
InboundConfigValue *ConnectionConfig
OutboundConfigValue *ConnectionConfig
InboundDetoursValue []*InboundDetourConfig
}
func (config *Config) Port() uint16 {
@@ -47,3 +70,11 @@ func (config *Config) InboundConfig() config.ConnectionConfig {
func (config *Config) OutboundConfig() config.ConnectionConfig {
return config.OutboundConfigValue
}
func (this *Config) InboundDetours() []config.InboundDetourConfig {
detours := make([]config.InboundDetourConfig, len(this.InboundDetoursValue))
for idx, detour := range this.InboundDetoursValue {
detours[idx] = detour
}
return detours
}

View File

@@ -0,0 +1,56 @@
package point
import (
"github.com/v2ray/v2ray-core/app/point/config"
"github.com/v2ray/v2ray-core/common/log"
"github.com/v2ray/v2ray-core/common/retry"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
)
type InboundConnectionHandlerWithPort struct {
port uint16
handler connhandler.InboundConnectionHandler
}
type InboundDetourHandler struct {
point *Point
config config.InboundDetourConfig
ich []*InboundConnectionHandlerWithPort
}
func (this *InboundDetourHandler) Initialize() error {
ichFactory := connhandler.GetInboundConnectionHandlerFactory(this.config.Protocol())
if ichFactory == nil {
log.Error("Unknown inbound connection handler factory %s", this.config.Protocol())
return config.BadConfiguration
}
ports := this.config.PortRange()
this.ich = make([]*InboundConnectionHandlerWithPort, 0, ports.From()-ports.To()+1)
for i := ports.From(); i <= ports.To(); i++ {
ichConfig := this.config.Settings()
ich, err := ichFactory.Create(this.point, ichConfig)
if err != nil {
log.Error("Failed to create inbound connection handler: %v", err)
return err
}
this.ich = append(this.ich, &InboundConnectionHandlerWithPort{
port: i,
handler: ich,
})
}
return nil
}
func (this *InboundDetourHandler) Start() error {
for _, ich := range this.ich {
return retry.Timed(100 /* times */, 100 /* ms */).On(func() error {
err := ich.handler.Listen(ich.port)
if err != nil {
return err
}
return nil
})
}
return nil
}

View File

@@ -1,19 +1,20 @@
package point
import (
"github.com/v2ray/v2ray-core/app/point/config"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/retry"
"github.com/v2ray/v2ray-core/config"
"github.com/v2ray/v2ray-core/proxy"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
"github.com/v2ray/v2ray-core/transport/ray"
)
// Point is an single server in V2Ray system.
type Point struct {
port uint16
ich proxy.InboundConnectionHandler
och proxy.OutboundConnectionHandler
ich connhandler.InboundConnectionHandler
och connhandler.OutboundConnectionHandler
idh []*InboundDetourHandler
}
// NewPoint returns a new Point server based on given configuration.
@@ -22,12 +23,12 @@ func NewPoint(pConfig config.PointConfig) (*Point, error) {
var vpoint = new(Point)
vpoint.port = pConfig.Port()
ichFactory := proxy.GetInboundConnectionHandlerFactory(pConfig.InboundConfig().Protocol())
ichFactory := connhandler.GetInboundConnectionHandlerFactory(pConfig.InboundConfig().Protocol())
if ichFactory == nil {
log.Error("Unknown inbound connection handler factory %s", pConfig.InboundConfig().Protocol())
return nil, config.BadConfiguration
}
ichConfig := pConfig.InboundConfig().Settings(config.TypeInbound)
ichConfig := pConfig.InboundConfig().Settings()
ich, err := ichFactory.Create(vpoint, ichConfig)
if err != nil {
log.Error("Failed to create inbound connection handler: %v", err)
@@ -35,12 +36,12 @@ func NewPoint(pConfig config.PointConfig) (*Point, error) {
}
vpoint.ich = ich
ochFactory := proxy.GetOutboundConnectionHandlerFactory(pConfig.OutboundConfig().Protocol())
ochFactory := connhandler.GetOutboundConnectionHandlerFactory(pConfig.OutboundConfig().Protocol())
if ochFactory == nil {
log.Error("Unknown outbound connection handler factory %s", pConfig.OutboundConfig().Protocol())
return nil, config.BadConfiguration
}
ochConfig := pConfig.OutboundConfig().Settings(config.TypeOutbound)
ochConfig := pConfig.OutboundConfig().Settings()
och, err := ochFactory.Create(ochConfig)
if err != nil {
log.Error("Failed to create outbound connection handler: %v", err)
@@ -48,6 +49,22 @@ func NewPoint(pConfig config.PointConfig) (*Point, error) {
}
vpoint.och = och
detours := pConfig.InboundDetours()
if len(detours) > 0 {
vpoint.idh = make([]*InboundDetourHandler, len(detours))
for idx, detourConfig := range detours {
detourHandler := &InboundDetourHandler{
point: vpoint,
config: detourConfig,
}
err := detourHandler.Initialize()
if err != nil {
return nil, err
}
vpoint.idh[idx] = detourHandler
}
}
return vpoint, nil
}
@@ -59,14 +76,26 @@ func (vp *Point) Start() error {
return config.BadConfiguration
}
return retry.Timed(100 /* times */, 100 /* ms */).On(func() error {
err := retry.Timed(100 /* times */, 100 /* ms */).On(func() error {
err := vp.ich.Listen(vp.port)
if err == nil {
log.Warning("Point server started on port %d", vp.port)
return nil
if err != nil {
return err
}
return err
log.Warning("Point server started on port %d", vp.port)
return nil
})
if err != nil {
return err
}
for _, detourHandler := range vp.idh {
err := detourHandler.Start()
if err != nil {
return err
}
}
return nil
}
func (p *Point) DispatchToOutbound(packet v2net.Packet) ray.InboundRay {

37
app/router/router.go Normal file
View File

@@ -0,0 +1,37 @@
package router
import (
"errors"
"github.com/v2ray/v2ray-core/app/point/config"
v2net "github.com/v2ray/v2ray-core/common/net"
)
var (
RouterNotFound = errors.New("Router not found.")
)
type Router interface {
TakeDetour(v2net.Packet) (config.ConnectionTag, error)
}
type RouterFactory interface {
Create(rawConfig interface{}) (Router, error)
}
var (
routerCache = make(map[string]RouterFactory)
)
func RegisterRouter(name string, factory RouterFactory) error {
// TODO: check name
routerCache[name] = factory
return nil
}
func CreateRouter(name string, rawConfig interface{}) (Router, error) {
if factory, found := routerCache[name]; found {
return factory.Create(rawConfig)
}
return nil, RouterNotFound
}

View File

@@ -0,0 +1,25 @@
package wildcard_router
import (
"github.com/v2ray/v2ray-core/app/point/config"
"github.com/v2ray/v2ray-core/app/router"
v2net "github.com/v2ray/v2ray-core/common/net"
)
type WildcardRouter struct {
}
func (router *WildcardRouter) TakeDetour(packet v2net.Packet) (config.ConnectionTag, error) {
return "", nil
}
type WildcardRouterFactory struct {
}
func (factory *WildcardRouterFactory) Create(rawConfig interface{}) (router.Router, error) {
return &WildcardRouter{}, nil
}
func init() {
router.RegisterRouter("wildcard", &WildcardRouterFactory{})
}

View File

@@ -124,7 +124,7 @@ func (p *bufferPool) cleanup(tick <-chan time.Time) {
}
}
var smallPool = newBufferPool(1024, 16, 64)
var smallPool = newBufferPool(1024, 64, 512)
var mediumPool = newBufferPool(8*1024, 256, 2048)
var largePool = newBufferPool(64*1024, 128, 1024)

View File

@@ -72,7 +72,7 @@ func (queue *TimedQueue) RemovedEntries() <-chan interface{} {
func (queue *TimedQueue) cleanup(tick <-chan time.Time) {
for now := range tick {
nowSec := now.UTC().Unix()
nowSec := now.Unix()
for {
queue.access.RLock()
queueLen := queue.queue.Len()

View File

@@ -12,7 +12,7 @@ func TestTimedQueue(t *testing.T) {
removed := make(map[string]bool)
nowSec := time.Now().UTC().Unix()
nowSec := time.Now().Unix()
q := NewTimedQueue(2)
go func() {
@@ -51,7 +51,7 @@ func TestTimedQueue(t *testing.T) {
<-tick
assert.Bool(removed["Values"]).IsFalse()
q.Add("Value1", time.Now().UTC().Unix()+10)
q.Add("Value1", time.Now().Unix()+10)
<-tick
v1, ok = removed["Value1"]

62
common/crypto/aes.go Normal file
View File

@@ -0,0 +1,62 @@
package crypto
import (
"crypto/aes"
"crypto/cipher"
"io"
)
func NewAesDecryptionStream(key []byte, iv []byte) (cipher.Stream, error) {
aesBlock, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return cipher.NewCFBDecrypter(aesBlock, iv), nil
}
func NewAesEncryptionStream(key []byte, iv []byte) (cipher.Stream, error) {
aesBlock, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return cipher.NewCFBEncrypter(aesBlock, iv), nil
}
type cryptionReader struct {
stream cipher.Stream
reader io.Reader
}
func NewCryptionReader(stream cipher.Stream, reader io.Reader) io.Reader {
return &cryptionReader{
stream: stream,
reader: reader,
}
}
func (this *cryptionReader) Read(data []byte) (int, error) {
nBytes, err := this.reader.Read(data)
if nBytes > 0 {
this.stream.XORKeyStream(data[:nBytes], data[:nBytes])
}
return nBytes, err
}
type cryptionWriter struct {
stream cipher.Stream
writer io.Writer
}
func NewCryptionWriter(stream cipher.Stream, writer io.Writer) io.Writer {
return &cryptionWriter{
stream: stream,
writer: writer,
}
}
func (this *cryptionWriter) Write(data []byte) (int, error) {
this.stream.XORKeyStream(data, data)
return this.writer.Write(data)
}

View File

@@ -1,27 +0,0 @@
package io
import (
"crypto/aes"
"crypto/cipher"
"io"
)
func NewAesDecryptReader(key []byte, iv []byte, reader io.Reader) (*CryptionReader, error) {
aesBlock, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aesStream := cipher.NewCFBDecrypter(aesBlock, iv)
return NewCryptionReader(aesStream, reader), nil
}
func NewAesEncryptWriter(key []byte, iv []byte, writer io.Writer) (*CryptionWriter, error) {
aesBlock, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aesStream := cipher.NewCFBEncrypter(aesBlock, iv)
return NewCryptionWriter(aesStream, writer), nil
}

View File

@@ -1,54 +0,0 @@
package io
import (
"crypto/cipher"
"io"
)
// CryptionReader is a general purpose reader that applies a stream cipher on top of a regular reader.
type CryptionReader struct {
stream cipher.Stream
reader io.Reader
}
// NewCryptionReader creates a new CryptionReader instance from given stream cipher and reader.
func NewCryptionReader(stream cipher.Stream, reader io.Reader) *CryptionReader {
return &CryptionReader{
stream: stream,
reader: reader,
}
}
// Read reads blocks from underlying reader, and crypt it. The content of blocks is modified in place.
func (reader CryptionReader) Read(blocks []byte) (int, error) {
nBytes, err := reader.reader.Read(blocks)
if nBytes > 0 {
reader.stream.XORKeyStream(blocks[:nBytes], blocks[:nBytes])
}
return nBytes, err
}
// Cryption writer is a general purpose of byte stream writer that applies a stream cipher on top of a regular writer.
type CryptionWriter struct {
stream cipher.Stream
writer io.Writer
}
// NewCryptionWriter creates a new CryptionWriter from given stream cipher and writer.
func NewCryptionWriter(stream cipher.Stream, writer io.Writer) *CryptionWriter {
return &CryptionWriter{
stream: stream,
writer: writer,
}
}
// Crypt crypts the content of blocks without writing them into the underlying writer.
func (writer CryptionWriter) Crypt(blocks []byte) {
writer.stream.XORKeyStream(blocks, blocks)
}
// Write crypts the content of blocks in place, and then writes the give blocks to underlying writer.
func (writer CryptionWriter) Write(blocks []byte) (int, error) {
writer.Crypt(blocks)
return writer.writer.Write(blocks)
}

View File

@@ -1,6 +1,7 @@
package log
import (
"bytes"
"testing"
"github.com/v2ray/v2ray-core/testing/unit"
@@ -17,3 +18,18 @@ func TestLogLevelSetting(t *testing.T) {
assert.Pointer(debugLogger).Equals(noOpLoggerInstance)
assert.Pointer(infoLogger).Equals(streamLoggerInstance)
}
func TestStreamLogger(t *testing.T) {
assert := unit.Assert(t)
buffer := bytes.NewBuffer(make([]byte, 0, 1024))
logger := &streamLogger{
writer: buffer,
}
logger.WriteLog("TestPrefix: ", "Test %s Format", "Stream Logger")
assert.Bytes(buffer.Bytes()).Equals([]byte("TestPrefix: Test Stream Logger Format\n"))
buffer.Reset()
logger.WriteLog("TestPrefix: ", "Test No Format")
assert.Bytes(buffer.Bytes()).Equals([]byte("TestPrefix: Test No Format\n"))
}

View File

@@ -0,0 +1,25 @@
package net
import (
"testing"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestTCPDestination(t *testing.T) {
assert := unit.Assert(t)
dest := NewTCPDestination(IPAddress([]byte{1, 2, 3, 4}, 80))
assert.Bool(dest.IsTCP()).IsTrue()
assert.Bool(dest.IsUDP()).IsFalse()
assert.String(dest.String()).Equals("tcp:1.2.3.4:80")
}
func TestUDPDestination(t *testing.T) {
assert := unit.Assert(t)
dest := NewUDPDestination(IPAddress([]byte{0x20, 0x01, 0x48, 0x60, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88}, 53))
assert.Bool(dest.IsTCP()).IsFalse()
assert.Bool(dest.IsUDP()).IsTrue()
assert.String(dest.String()).Equals("udp:[2001:4860:4860::8888]:53")
}

View File

@@ -0,0 +1,46 @@
package json
import (
"encoding/json"
"errors"
"strings"
v2net "github.com/v2ray/v2ray-core/common/net"
)
type NetworkList []string
func NewNetworkList(networks []string) NetworkList {
list := NetworkList(make([]string, len(networks)))
for idx, network := range networks {
list[idx] = strings.ToLower(strings.TrimSpace(network))
}
return list
}
func (this *NetworkList) UnmarshalJSON(data []byte) error {
var strList []string
err := json.Unmarshal(data, &strList)
if err == nil {
*this = NewNetworkList(strList)
return nil
}
var str string
err = json.Unmarshal(data, &str)
if err == nil {
strList := strings.Split(str, ",")
*this = NewNetworkList(strList)
return nil
}
return errors.New("Unknown format of network list: " + string(data))
}
func (this *NetworkList) HasNetwork(network v2net.Network) bool {
for _, value := range *this {
if value == string(network) {
return true
}
}
return false
}

View File

@@ -0,0 +1,28 @@
package json
import (
"encoding/json"
"testing"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestArrayNetworkList(t *testing.T) {
assert := unit.Assert(t)
var list NetworkList
err := json.Unmarshal([]byte("[\"Tcp\"]"), &list)
assert.Error(err).IsNil()
assert.Bool(list.HasNetwork("tcp")).IsTrue()
assert.Bool(list.HasNetwork("udp")).IsFalse()
}
func TestStringNetworkList(t *testing.T) {
assert := unit.Assert(t)
var list NetworkList
err := json.Unmarshal([]byte("\"TCP, ip\""), &list)
assert.Error(err).IsNil()
assert.Bool(list.HasNetwork("tcp")).IsTrue()
assert.Bool(list.HasNetwork("udp")).IsFalse()
}

12
common/net/network.go Normal file
View File

@@ -0,0 +1,12 @@
package net
const (
TCPNetwork = Network("tcp")
UDPNetwork = Network("udp")
)
type Network string
type NetworkList interface {
HasNetwork(Network) bool
}

View File

@@ -0,0 +1,13 @@
package testing
import (
"sync/atomic"
)
var (
port = int32(30000)
)
func PickPort() uint16 {
return uint16(atomic.AddInt32(&port, 1))
}

View File

@@ -1,6 +1,7 @@
package net
import (
"io"
"net"
"time"
)
@@ -12,21 +13,19 @@ var (
type TimeOutReader struct {
timeout int
connection net.Conn
worker io.Reader
}
func NewTimeOutReader(timeout int, connection net.Conn) *TimeOutReader {
return &TimeOutReader{
timeout: timeout,
reader := &TimeOutReader{
connection: connection,
}
reader.SetTimeOut(timeout)
return reader
}
func (reader *TimeOutReader) Read(p []byte) (n int, err error) {
deadline := time.Duration(reader.timeout) * time.Second
reader.connection.SetReadDeadline(time.Now().Add(deadline))
n, err = reader.connection.Read(p)
reader.connection.SetReadDeadline(emptyTime)
return
func (reader *TimeOutReader) Read(p []byte) (int, error) {
return reader.worker.Read(p)
}
func (reader *TimeOutReader) GetTimeOut() int {
@@ -35,4 +34,35 @@ func (reader *TimeOutReader) GetTimeOut() int {
func (reader *TimeOutReader) SetTimeOut(value int) {
reader.timeout = value
if value > 0 {
reader.worker = &timedReaderWorker{
timeout: value,
connection: reader.connection,
}
} else {
reader.worker = &noOpReaderWorker{
connection: reader.connection,
}
}
}
type timedReaderWorker struct {
timeout int
connection net.Conn
}
func (this *timedReaderWorker) Read(p []byte) (int, error) {
deadline := time.Duration(this.timeout) * time.Second
this.connection.SetReadDeadline(time.Now().Add(deadline))
nBytes, err := this.connection.Read(p)
this.connection.SetReadDeadline(emptyTime)
return nBytes, err
}
type noOpReaderWorker struct {
connection net.Conn
}
func (this *noOpReaderWorker) Read(p []byte) (int, error) {
return this.connection.Read(p)
}

View File

@@ -17,6 +17,20 @@ func ReadFrom(reader io.Reader, buffer *alloc.Buffer) (*alloc.Buffer, error) {
return buffer, err
}
// ReadAllBytes reads all bytes required from reader, if no error happens.
func ReadAllBytes(reader io.Reader, buffer []byte) (int, error) {
bytesRead := 0
bytesAsked := len(buffer)
for bytesRead < bytesAsked {
nBytes, err := reader.Read(buffer[bytesRead:])
bytesRead += nBytes
if err != nil {
return bytesRead, err
}
}
return bytesRead, nil
}
// ReaderToChan dumps all content from a given reader to a chan by constantly reading it until EOF.
func ReaderToChan(stream chan<- *alloc.Buffer, reader io.Reader) error {
allocate := alloc.NewBuffer

View File

@@ -0,0 +1,80 @@
package retry
import (
"errors"
"testing"
"time"
"github.com/v2ray/v2ray-core/testing/unit"
)
var (
TestError = errors.New("This is a fake error.")
)
func TestNoRetry(t *testing.T) {
assert := unit.Assert(t)
startTime := time.Now().Unix()
err := Timed(10, 100000).On(func() error {
return nil
})
endTime := time.Now().Unix()
assert.Error(err).IsNil()
assert.Int64(endTime - startTime).AtLeast(0)
}
func TestRetryOnce(t *testing.T) {
assert := unit.Assert(t)
startTime := time.Now()
called := 0
err := Timed(10, 1000).On(func() error {
if called == 0 {
called++
return TestError
}
return nil
})
duration := time.Since(startTime)
assert.Error(err).IsNil()
assert.Int64(int64(duration / time.Millisecond)).AtLeast(900)
}
func TestRetryMultiple(t *testing.T) {
assert := unit.Assert(t)
startTime := time.Now()
called := 0
err := Timed(10, 1000).On(func() error {
if called < 5 {
called++
return TestError
}
return nil
})
duration := time.Since(startTime)
assert.Error(err).IsNil()
assert.Int64(int64(duration / time.Millisecond)).AtLeast(4900)
}
func TestRetryExhausted(t *testing.T) {
assert := unit.Assert(t)
startTime := time.Now()
called := 0
err := Timed(2, 1000).On(func() error {
if called < 5 {
called++
return TestError
}
return nil
})
duration := time.Since(startTime)
assert.Error(err).Equals(RetryFailed)
assert.Int64(int64(duration / time.Millisecond)).AtLeast(1900)
}

View File

@@ -1,24 +0,0 @@
package config
type Type string
const (
TypeInbound = Type("inbound")
TypeOutbound = Type("outbound")
)
type ConnectionConfig interface {
Protocol() string
Settings(configType Type) interface{}
}
type LogConfig interface {
AccessLog() string
}
type PointConfig interface {
Port() uint16
LogConfig() LogConfig
InboundConfig() ConnectionConfig
OutboundConfig() ConnectionConfig
}

View File

@@ -1,20 +0,0 @@
package json
import (
"github.com/v2ray/v2ray-core/config"
)
type ConfigObjectCreator func() interface{}
var (
configCache = make(map[string]ConfigObjectCreator)
)
func getConfigKey(protocol string, cType config.Type) string {
return protocol + "_" + string(cType)
}
func RegisterConfigType(protocol string, cType config.Type, creator ConfigObjectCreator) {
// TODO: check name
configCache[getConfigKey(protocol, cType)] = creator
}

View File

@@ -8,7 +8,7 @@ import (
)
var (
version = "0.9"
version = "0.11"
build = "Custom"
codename = "Post Apocalypse"
intro = "A stable and unbreakable connection for everyone."

View File

@@ -0,0 +1,40 @@
package blackhole
import (
"io/ioutil"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
"github.com/v2ray/v2ray-core/transport/ray"
)
// BlackHole is an outbound connection that sliently swallow the entire payload.
type BlackHole struct {
}
func NewBlackHole() *BlackHole {
return &BlackHole{}
}
func (bh *BlackHole) Dispatch(firstPacket v2net.Packet, ray ray.OutboundRay) error {
if chunk := firstPacket.Chunk(); chunk != nil {
chunk.Release()
}
close(ray.OutboundOutput())
if firstPacket.MoreChunks() {
v2net.ChanToWriter(ioutil.Discard, ray.OutboundInput())
}
return nil
}
type BlackHoleFactory struct {
}
func (factory BlackHoleFactory) Create(config interface{}) (connhandler.OutboundConnectionHandler, error) {
return NewBlackHole(), nil
}
func init() {
connhandler.RegisterOutboundConnectionHandlerFactory("blackhole", BlackHoleFactory{})
}

View File

@@ -0,0 +1,14 @@
package json
import (
"github.com/v2ray/v2ray-core/proxy/common/config/json"
)
type BlackHoleConfig struct {
}
func init() {
json.RegisterOutboundConnectionConfig("blackhole", func() interface{} {
return new(BlackHoleConfig)
})
}

View File

@@ -0,0 +1,9 @@
package config
import (
"errors"
)
var (
BadConfiguration = errors.New("Bad proxy configuration.")
)

View File

@@ -0,0 +1,45 @@
package json
import (
"github.com/v2ray/v2ray-core/proxy/common/config"
)
type ConfigObjectCreator func() interface{}
var (
configCache map[string]ConfigObjectCreator
)
func getConfigKey(protocol string, cType config.Type) string {
return protocol + "_" + string(cType)
}
func registerConfigType(protocol string, cType config.Type, creator ConfigObjectCreator) error {
// TODO: check name
configCache[getConfigKey(protocol, cType)] = creator
return nil
}
func RegisterInboundConnectionConfig(protocol string, creator ConfigObjectCreator) error {
return registerConfigType(protocol, config.TypeInbound, creator)
}
func RegisterOutboundConnectionConfig(protocol string, creator ConfigObjectCreator) error {
return registerConfigType(protocol, config.TypeOutbound, creator)
}
func CreateConfig(protocol string, cType config.Type) interface{} {
creator, found := configCache[getConfigKey(protocol, cType)]
if !found {
return nil
}
return creator()
}
func initializeConfigCache() {
configCache = make(map[string]ConfigObjectCreator)
}
func init() {
initializeConfigCache()
}

View File

@@ -0,0 +1,46 @@
package json
import (
"testing"
"github.com/v2ray/v2ray-core/proxy/common/config"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestRegisterInboundConfig(t *testing.T) {
assert := unit.Assert(t)
initializeConfigCache()
protocol := "test_protocol"
creator := func() interface{} {
return true
}
err := RegisterInboundConnectionConfig(protocol, creator)
assert.Error(err).IsNil()
configObj := CreateConfig(protocol, config.TypeInbound)
assert.Bool(configObj.(bool)).IsTrue()
configObj = CreateConfig(protocol, config.TypeOutbound)
assert.Pointer(configObj).IsNil()
}
func TestRegisterOutboundConfig(t *testing.T) {
assert := unit.Assert(t)
initializeConfigCache()
protocol := "test_protocol"
creator := func() interface{} {
return true
}
err := RegisterOutboundConnectionConfig(protocol, creator)
assert.Error(err).IsNil()
configObj := CreateConfig(protocol, config.TypeOutbound)
assert.Bool(configObj.(bool)).IsTrue()
configObj = CreateConfig(protocol, config.TypeInbound)
assert.Pointer(configObj).IsNil()
}

View File

@@ -0,0 +1,8 @@
package config
type Type string
const (
TypeInbound = Type("inbound")
TypeOutbound = Type("outbound")
)

View File

@@ -1,4 +1,4 @@
package proxy
package connhandler
import (
"github.com/v2ray/v2ray-core/app"

View File

@@ -1,4 +1,4 @@
package proxy
package connhandler
import (
v2net "github.com/v2ray/v2ray-core/common/net"

View File

@@ -1,4 +1,4 @@
package proxy
package connhandler
var (
inboundFactories = make(map[string]InboundConnectionHandlerFactory)

View File

@@ -1,4 +1,4 @@
package proxy
package errors
import (
"errors"

View File

@@ -0,0 +1,19 @@
package json
import (
v2netjson "github.com/v2ray/v2ray-core/common/net/json"
"github.com/v2ray/v2ray-core/proxy/common/config/json"
)
type DokodemoConfig struct {
Host string `json:"address"`
Port int `json:"port"`
Network *v2netjson.NetworkList `json:"network"`
Timeout int `json:"timeout"`
}
func init() {
json.RegisterInboundConnectionConfig("dokodemo-door", func() interface{} {
return new(DokodemoConfig)
})
}

102
proxy/dokodemo/dokodemo.go Normal file
View File

@@ -0,0 +1,102 @@
package dokodemo
import (
"io"
"net"
"sync"
"github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/common/alloc"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/retry"
"github.com/v2ray/v2ray-core/proxy/dokodemo/config/json"
)
type DokodemoDoor struct {
config *json.DokodemoConfig
accepting bool
address v2net.Address
dispatcher app.PacketDispatcher
}
func NewDokodemoDoor(dispatcher app.PacketDispatcher, config *json.DokodemoConfig) *DokodemoDoor {
d := &DokodemoDoor{
config: config,
dispatcher: dispatcher,
}
ip := net.ParseIP(config.Host)
if ip != nil {
d.address = v2net.IPAddress(ip, uint16(config.Port))
} else {
d.address = v2net.DomainAddress(config.Host, uint16(config.Port))
}
return d
}
func (this *DokodemoDoor) Listen(port uint16) error {
if this.config.Network.HasNetwork(v2net.TCPNetwork) {
err := this.ListenTCP(port)
if err != nil {
return err
}
}
return nil
}
func (this *DokodemoDoor) ListenTCP(port uint16) error {
tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{
IP: []byte{0, 0, 0, 0},
Port: int(port),
Zone: "",
})
if err != nil {
log.Error("Dokodemo failed to listen on port %d: %v", port, err)
return err
}
this.accepting = true
go this.AcceptTCPConnections(tcpListener)
return nil
}
func (this *DokodemoDoor) AcceptTCPConnections(tcpListener *net.TCPListener) {
for this.accepting {
retry.Timed(100, 100).On(func() error {
connection, err := tcpListener.AcceptTCP()
if err != nil {
log.Error("Dokodemo failed to accept new connections: %v", err)
return err
}
go this.HandleTCPConnection(connection)
return nil
})
}
}
func (this *DokodemoDoor) HandleTCPConnection(conn *net.TCPConn) {
defer conn.Close()
packet := v2net.NewPacket(v2net.NewTCPDestination(this.address), nil, true)
ray := this.dispatcher.DispatchToOutbound(packet)
var inputFinish, outputFinish sync.Mutex
inputFinish.Lock()
outputFinish.Lock()
reader := v2net.NewTimeOutReader(this.config.Timeout, conn)
go dumpInput(reader, ray.InboundInput(), &inputFinish)
go dumpOutput(conn, ray.InboundOutput(), &outputFinish)
outputFinish.Lock()
}
func dumpInput(reader io.Reader, input chan<- *alloc.Buffer, finish *sync.Mutex) {
v2net.ReaderToChan(input, reader)
finish.Unlock()
close(input)
}
func dumpOutput(writer io.Writer, output <-chan *alloc.Buffer, finish *sync.Mutex) {
v2net.ChanToWriter(writer, output)
finish.Unlock()
}

View File

@@ -0,0 +1,19 @@
package dokodemo
import (
"github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
"github.com/v2ray/v2ray-core/proxy/dokodemo/config/json"
)
type DokodemoDoorFactory struct {
}
func (this DokodemoDoorFactory) Create(dispatcher app.PacketDispatcher, rawConfig interface{}) (connhandler.InboundConnectionHandler, error) {
config := rawConfig.(*json.DokodemoConfig)
return NewDokodemoDoor(dispatcher, config), nil
}
func init() {
connhandler.RegisterInboundConnectionHandlerFactory("dokodemo-door", DokodemoDoorFactory{})
}

View File

@@ -0,0 +1,77 @@
package dokodemo
import (
"net"
"testing"
"github.com/v2ray/v2ray-core/app/point"
"github.com/v2ray/v2ray-core/app/point/config/testing/mocks"
v2netjson "github.com/v2ray/v2ray-core/common/net/json"
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
"github.com/v2ray/v2ray-core/proxy/dokodemo/config/json"
_ "github.com/v2ray/v2ray-core/proxy/freedom"
"github.com/v2ray/v2ray-core/testing/servers/tcp"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestDokodemoTCP(t *testing.T) {
assert := unit.Assert(t)
port := v2nettesting.PickPort()
data2Send := "Data to be sent to remote."
tcpServer := &tcp.Server{
Port: port,
MsgProcessor: func(data []byte) []byte {
buffer := make([]byte, 0, 2048)
buffer = append(buffer, []byte("Processed: ")...)
buffer = append(buffer, data...)
return buffer
},
}
_, err := tcpServer.Start()
assert.Error(err).IsNil()
pointPort := v2nettesting.PickPort()
networkList := v2netjson.NetworkList([]string{"tcp"})
config := mocks.Config{
PortValue: pointPort,
InboundConfigValue: &mocks.ConnectionConfig{
ProtocolValue: "dokodemo-door",
SettingsValue: &json.DokodemoConfig{
Host: "127.0.0.1",
Port: int(port),
Network: &networkList,
Timeout: 0,
},
},
OutboundConfigValue: &mocks.ConnectionConfig{
ProtocolValue: "freedom",
SettingsValue: nil,
},
}
point, err := point.NewPoint(&config)
assert.Error(err).IsNil()
err = point.Start()
assert.Error(err).IsNil()
tcpClient, err := net.DialTCP("tcp", nil, &net.TCPAddr{
IP: []byte{127, 0, 0, 1},
Port: int(pointPort),
Zone: "",
})
assert.Error(err).IsNil()
tcpClient.Write([]byte(data2Send))
tcpClient.CloseWrite()
response := make([]byte, 1024)
nBytes, err := tcpClient.Read(response)
assert.Error(err).IsNil()
tcpClient.Close()
assert.String("Processed: " + data2Send).Equals(string(response[:nBytes]))
}

View File

@@ -1,15 +1,14 @@
package json
import (
"github.com/v2ray/v2ray-core/config"
"github.com/v2ray/v2ray-core/config/json"
"github.com/v2ray/v2ray-core/proxy/common/config/json"
)
type FreedomConfiguration struct {
}
func init() {
json.RegisterConfigType("freedom", config.TypeOutbound, func() interface{} {
json.RegisterOutboundConnectionConfig("freedom", func() interface{} {
return &FreedomConfiguration{}
})
}

View File

@@ -2,6 +2,7 @@ package freedom
import (
"bytes"
"fmt"
"io/ioutil"
"net"
"testing"
@@ -9,12 +10,14 @@ import (
"golang.org/x/net/proxy"
"github.com/v2ray/v2ray-core/app/point"
"github.com/v2ray/v2ray-core/app/point/config/testing/mocks"
"github.com/v2ray/v2ray-core/common/alloc"
v2net "github.com/v2ray/v2ray-core/common/net"
v2proxy "github.com/v2ray/v2ray-core/proxy"
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
_ "github.com/v2ray/v2ray-core/proxy/socks"
"github.com/v2ray/v2ray-core/proxy/socks/config/json"
"github.com/v2ray/v2ray-core/testing/mocks"
proxymocks "github.com/v2ray/v2ray-core/proxy/testing/mocks"
"github.com/v2ray/v2ray-core/testing/servers/tcp"
"github.com/v2ray/v2ray-core/testing/servers/udp"
"github.com/v2ray/v2ray-core/testing/unit"
@@ -38,14 +41,15 @@ func TestUDPSend(t *testing.T) {
udpServerAddr, err := udpServer.Start()
assert.Error(err).IsNil()
ich := &mocks.InboundConnectionHandler{
Data2Send: []byte("Not Used"),
DataReturned: bytes.NewBuffer(make([]byte, 0, 1024)),
connOutput := bytes.NewBuffer(make([]byte, 0, 1024))
ich := &proxymocks.InboundConnectionHandler{
ConnInput: bytes.NewReader([]byte("Not Used")),
ConnOutput: connOutput,
}
v2proxy.RegisterInboundConnectionHandlerFactory("mock_ich", ich)
connhandler.RegisterInboundConnectionHandlerFactory("mock_ich", ich)
pointPort := uint16(38724)
pointPort := v2nettesting.PickPort()
config := mocks.Config{
PortValue: pointPort,
InboundConfigValue: &mocks.ConnectionConfig{
@@ -64,17 +68,16 @@ func TestUDPSend(t *testing.T) {
err = point.Start()
assert.Error(err).IsNil()
data2SendBuffer := alloc.NewBuffer()
data2SendBuffer.Clear()
data2SendBuffer := alloc.NewBuffer().Clear()
data2SendBuffer.Append([]byte(data2Send))
dest := v2net.NewUDPDestination(udpServerAddr)
ich.Communicate(v2net.NewPacket(dest, data2SendBuffer, false))
assert.Bytes(ich.DataReturned.Bytes()).Equals([]byte("Processed: Data to be sent to remote"))
assert.Bytes(connOutput.Bytes()).Equals([]byte("Processed: Data to be sent to remote"))
}
func TestSocksTcpConnect(t *testing.T) {
assert := unit.Assert(t)
port := uint16(38293)
port := v2nettesting.PickPort()
data2Send := "Data to be sent to remote"
@@ -90,7 +93,7 @@ func TestSocksTcpConnect(t *testing.T) {
_, err := tcpServer.Start()
assert.Error(err).IsNil()
pointPort := uint16(38724)
pointPort := v2nettesting.PickPort()
config := mocks.Config{
PortValue: pointPort,
InboundConfigValue: &mocks.ConnectionConfig{
@@ -111,10 +114,10 @@ func TestSocksTcpConnect(t *testing.T) {
err = point.Start()
assert.Error(err).IsNil()
socks5Client, err := proxy.SOCKS5("tcp", "127.0.0.1:38724", nil, proxy.Direct)
socks5Client, err := proxy.SOCKS5("tcp", fmt.Sprintf("127.0.0.1:%d", pointPort), nil, proxy.Direct)
assert.Error(err).IsNil()
targetServer := "127.0.0.1:38293"
targetServer := fmt.Sprintf("127.0.0.1:%d", port)
conn, err := socks5Client.Dial("tcp", targetServer)
assert.Error(err).IsNil()

View File

@@ -1,16 +1,16 @@
package freedom
import (
"github.com/v2ray/v2ray-core/proxy"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
)
type FreedomFactory struct {
}
func (factory FreedomFactory) Create(config interface{}) (proxy.OutboundConnectionHandler, error) {
func (factory FreedomFactory) Create(config interface{}) (connhandler.OutboundConnectionHandler, error) {
return NewFreedomConnection(), nil
}
func init() {
proxy.RegisterOutboundConnectionHandlerFactory("freedom", FreedomFactory{})
connhandler.RegisterOutboundConnectionHandlerFactory("freedom", FreedomFactory{})
}

View File

@@ -0,0 +1,14 @@
package json
import (
"github.com/v2ray/v2ray-core/proxy/common/config/json"
)
type HttpProxyConfig struct {
}
func init() {
json.RegisterInboundConnectionConfig("http", func() interface{} {
return new(HttpProxyConfig)
})
}

36
proxy/http/http.go Normal file
View File

@@ -0,0 +1,36 @@
package http
import (
"net"
// "net/http"
"github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/common/log"
jsonconfig "github.com/v2ray/v2ray-core/proxy/http/config/json"
)
type HttpProxyServer struct {
accepting bool
dispatcher app.PacketDispatcher
config *jsonconfig.HttpProxyConfig
}
func NewHttpProxyServer(dispatcher app.PacketDispatcher, config *jsonconfig.HttpProxyConfig) *HttpProxyServer {
return &HttpProxyServer{
dispatcher: dispatcher,
config: config,
}
}
func (server *HttpProxyServer) Listen(port uint16) error {
_, err := net.ListenTCP("tcp", &net.TCPAddr{
IP: []byte{0, 0, 0, 0},
Port: int(port),
Zone: "",
})
if err != nil {
log.Error("HTTP Proxy failed to listen on port %d: %v", port, err)
return err
}
return nil
}

View File

@@ -1,10 +1,11 @@
package json
import (
"encoding/json"
"errors"
"net"
"github.com/v2ray/v2ray-core/config"
"github.com/v2ray/v2ray-core/config/json"
jsonconfig "github.com/v2ray/v2ray-core/proxy/common/config/json"
)
const (
@@ -17,28 +18,49 @@ type SocksAccount struct {
Password string `json:"pass"`
}
type SocksConfig struct {
AuthMethod string `json:"auth"`
Accounts []SocksAccount `json:"accounts"`
UDPEnabled bool `json:"udp"`
HostIP string `json:"ip"`
type SocksAccountMap map[string]string
accountMap map[string]string
ip net.IP
func (this *SocksAccountMap) UnmarshalJSON(data []byte) error {
var accounts []SocksAccount
err := json.Unmarshal(data, &accounts)
if err != nil {
return err
}
*this = make(map[string]string)
for _, account := range accounts {
(*this)[account.Username] = account.Password
}
return nil
}
func (sc *SocksConfig) Initialize() {
sc.accountMap = make(map[string]string)
for _, account := range sc.Accounts {
sc.accountMap[account.Username] = account.Password
func (this *SocksAccountMap) HasAccount(user, pass string) bool {
if actualPass, found := (*this)[user]; found {
return actualPass == pass
}
return false
}
if len(sc.HostIP) > 0 {
sc.ip = net.ParseIP(sc.HostIP)
if sc.ip == nil {
sc.ip = net.IPv4(127, 0, 0, 1)
}
type IPAddress net.IP
func (this *IPAddress) UnmarshalJSON(data []byte) error {
var ipStr string
err := json.Unmarshal(data, &ipStr)
if err != nil {
return err
}
ip := net.ParseIP(ipStr)
if ip == nil {
return errors.New("Unknown IP format: " + ipStr)
}
*this = IPAddress(ip)
return nil
}
type SocksConfig struct {
AuthMethod string `json:"auth"`
Accounts SocksAccountMap `json:"accounts"`
UDPEnabled bool `json:"udp"`
HostIP IPAddress `json:"ip"`
}
func (sc *SocksConfig) IsNoAuth() bool {
@@ -50,18 +72,17 @@ func (sc *SocksConfig) IsPassword() bool {
}
func (sc *SocksConfig) HasAccount(user, pass string) bool {
if actualPass, found := sc.accountMap[user]; found {
return actualPass == pass
}
return false
return sc.Accounts.HasAccount(user, pass)
}
func (sc *SocksConfig) IP() net.IP {
return sc.ip
return net.IP(sc.HostIP)
}
func init() {
json.RegisterConfigType("socks", config.TypeInbound, func() interface{} {
return new(SocksConfig)
jsonconfig.RegisterInboundConnectionConfig("socks", func() interface{} {
return &SocksConfig{
HostIP: IPAddress(net.IPv4(127, 0, 0, 1)),
}
})
}

View File

@@ -0,0 +1,64 @@
package json
import (
"encoding/json"
"net"
"testing"
"github.com/v2ray/v2ray-core/proxy/common/config"
jsonconfig "github.com/v2ray/v2ray-core/proxy/common/config/json"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestAccountMapParsing(t *testing.T) {
assert := unit.Assert(t)
var accountMap SocksAccountMap
err := json.Unmarshal([]byte("[{\"user\": \"a\", \"pass\":\"b\"}, {\"user\": \"c\", \"pass\":\"d\"}]"), &accountMap)
assert.Error(err).IsNil()
assert.Bool(accountMap.HasAccount("a", "b")).IsTrue()
assert.Bool(accountMap.HasAccount("a", "c")).IsFalse()
assert.Bool(accountMap.HasAccount("c", "d")).IsTrue()
assert.Bool(accountMap.HasAccount("e", "d")).IsFalse()
}
func TestDefaultIPAddress(t *testing.T) {
assert := unit.Assert(t)
socksConfig := jsonconfig.CreateConfig("socks", config.TypeInbound).(*SocksConfig)
assert.String(socksConfig.IP().String()).Equals("127.0.0.1")
}
func TestIPAddressParsing(t *testing.T) {
assert := unit.Assert(t)
var ipAddress IPAddress
err := json.Unmarshal([]byte("\"1.2.3.4\""), &ipAddress)
assert.Error(err).IsNil()
assert.String(net.IP(ipAddress).String()).Equals("1.2.3.4")
}
func TestNoAuthConfig(t *testing.T) {
assert := unit.Assert(t)
var config SocksConfig
err := json.Unmarshal([]byte("{\"auth\":\"noauth\", \"ip\":\"8.8.8.8\"}"), &config)
assert.Error(err).IsNil()
assert.Bool(config.IsNoAuth()).IsTrue()
assert.Bool(config.IsPassword()).IsFalse()
assert.String(config.IP().String()).Equals("8.8.8.8")
assert.Bool(config.UDPEnabled).IsFalse()
}
func TestUserPassConfig(t *testing.T) {
assert := unit.Assert(t)
var config SocksConfig
err := json.Unmarshal([]byte("{\"auth\":\"password\", \"accounts\":[{\"user\":\"x\", \"pass\":\"y\"}], \"udp\":true}"), &config)
assert.Error(err).IsNil()
assert.Bool(config.IsNoAuth()).IsFalse()
assert.Bool(config.IsPassword()).IsTrue()
assert.Bool(config.HasAccount("x", "y")).IsTrue()
assert.Bool(config.UDPEnabled).IsTrue()
}

View File

@@ -7,7 +7,7 @@ import (
"github.com/v2ray/v2ray-core/common/alloc"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy"
proxyerrors "github.com/v2ray/v2ray-core/proxy/common/errors"
"github.com/v2ray/v2ray-core/transport"
)
@@ -66,7 +66,7 @@ func ReadAuthentication(reader io.Reader) (auth Socks5AuthenticationRequest, aut
auth.version = buffer.Value[0]
if auth.version != socksVersion {
log.Warning("Unknown protocol version %d", auth.version)
err = proxy.InvalidProtocolVersion
err = proxyerrors.InvalidProtocolVersion
return
}

View File

@@ -28,11 +28,7 @@ func TestSocks4AuthenticationRequestRead(t *testing.T) {
func TestSocks4AuthenticationResponseToBytes(t *testing.T) {
assert := unit.Assert(t)
response := &Socks4AuthenticationResponse{
result: byte(0x10),
port: 443,
ip: []byte{1, 2, 3, 4},
}
response := NewSocks4AuthenticationResponse(byte(0x10), 443, []byte{1, 2, 3, 4})
buffer := alloc.NewSmallBuffer().Clear()
defer buffer.Release()

View File

@@ -2,10 +2,12 @@ package protocol
import (
"bytes"
"io"
"testing"
"github.com/v2ray/v2ray-core/common/alloc"
"github.com/v2ray/v2ray-core/testing/unit"
"github.com/v2ray/v2ray-core/transport"
)
func TestHasAuthenticationMethod(t *testing.T) {
@@ -94,3 +96,17 @@ func TestResponseWrite(t *testing.T) {
}
assert.Bytes(buffer.Value).Named("raw response").Equals(expectedBytes)
}
func TestEOF(t *testing.T) {
assert := unit.Assert(t)
_, _, err := ReadAuthentication(bytes.NewReader(make([]byte, 0)))
assert.Error(err).Equals(io.EOF)
}
func TestSignleByte(t *testing.T) {
assert := unit.Assert(t)
_, _, err := ReadAuthentication(bytes.NewReader(make([]byte, 1)))
assert.Error(err).Equals(transport.CorruptedPacket)
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/v2ray/v2ray-core/common/alloc"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/transport"
)
var (
@@ -37,7 +38,12 @@ func (request *Socks5UDPRequest) Write(buffer *alloc.Buffer) {
buffer.Append(request.Data.Value)
}
func ReadUDPRequest(packet []byte) (request Socks5UDPRequest, err error) {
func ReadUDPRequest(packet []byte) (*Socks5UDPRequest, error) {
if len(packet) < 5 {
return nil, transport.CorruptedPacket
}
request := new(Socks5UDPRequest)
// packet[0] and packet[1] are reserved
request.Fragment = packet[2]
@@ -46,28 +52,38 @@ func ReadUDPRequest(packet []byte) (request Socks5UDPRequest, err error) {
switch addrType {
case AddrTypeIPv4:
if len(packet) < 10 {
return nil, transport.CorruptedPacket
}
ip := packet[4:8]
port := binary.BigEndian.Uint16(packet[8:10])
request.Address = v2net.IPAddress(ip, port)
dataBegin = 10
case AddrTypeIPv6:
if len(packet) < 22 {
return nil, transport.CorruptedPacket
}
ip := packet[4:20]
port := binary.BigEndian.Uint16(packet[20:22])
request.Address = v2net.IPAddress(ip, port)
dataBegin = 22
case AddrTypeDomain:
domainLength := int(packet[4])
if len(packet) < 5+domainLength+2 {
return nil, transport.CorruptedPacket
}
domain := string(packet[5 : 5+domainLength])
port := binary.BigEndian.Uint16(packet[5+domainLength : 5+domainLength+2])
request.Address = v2net.DomainAddress(domain, port)
dataBegin = 5 + domainLength + 2
default:
log.Warning("Unknown address type %d", addrType)
err = ErrorUnknownAddressType
return
return nil, ErrorUnknownAddressType
}
request.Data = alloc.NewBuffer().Clear().Append(packet[dataBegin:])
if len(packet) > dataBegin {
request.Data = alloc.NewBuffer().Clear().Append(packet[dataBegin:])
}
return
return request, nil
}

View File

@@ -0,0 +1,35 @@
package protocol
import (
"testing"
"github.com/v2ray/v2ray-core/testing/unit"
"github.com/v2ray/v2ray-core/transport"
)
func TestSingleByteRequest(t *testing.T) {
assert := unit.Assert(t)
request, err := ReadUDPRequest(make([]byte, 1))
if request != nil {
t.Fail()
}
assert.Error(err).Equals(transport.CorruptedPacket)
}
func TestDomainAddressRequest(t *testing.T) {
assert := unit.Assert(t)
payload := make([]byte, 0, 1024)
payload = append(payload, 0, 0, 1, AddrTypeDomain, byte(len("v2ray.com")))
payload = append(payload, []byte("v2ray.com")...)
payload = append(payload, 0, 80)
payload = append(payload, []byte("Actual payload")...)
request, err := ReadUDPRequest(payload)
assert.Error(err).IsNil()
assert.Byte(request.Fragment).Equals(1)
assert.String(request.Address.String()).Equals("v2ray.com:80")
assert.Bytes(request.Data.Value).Equals([]byte("Actual payload"))
}

View File

@@ -12,7 +12,7 @@ import (
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/retry"
"github.com/v2ray/v2ray-core/proxy"
proxyerrors "github.com/v2ray/v2ray-core/proxy/common/errors"
jsonconfig "github.com/v2ray/v2ray-core/proxy/socks/config/json"
"github.com/v2ray/v2ray-core/proxy/socks/protocol"
)
@@ -128,7 +128,7 @@ func (server *SocksServer) handleSocks5(reader *v2net.TimeOutReader, writer io.W
}
if status != byte(0) {
log.Warning("Invalid user account: %s", upRequest.AuthDetail())
return proxy.InvalidAuthentication
return proxyerrors.InvalidAuthentication
}
}
@@ -145,6 +145,8 @@ func (server *SocksServer) handleSocks5(reader *v2net.TimeOutReader, writer io.W
if request.Command == protocol.CmdBind || request.Command == protocol.CmdUdpAssociate {
response := protocol.NewSocks5Response()
response.Error = protocol.ErrorCommandNotSupported
response.Port = uint16(0)
response.SetIPv4([]byte{0, 0, 0, 0})
responseBuffer := alloc.NewSmallBuffer().Clear()
response.Write(responseBuffer)
@@ -163,11 +165,7 @@ func (server *SocksServer) handleSocks5(reader *v2net.TimeOutReader, writer io.W
// Some SOCKS software requires a value other than dest. Let's fake one:
response.Port = uint16(1717)
response.AddrType = protocol.AddrTypeIPv4
response.IPv4[0] = 0
response.IPv4[1] = 0
response.IPv4[2] = 0
response.IPv4[3] = 0
response.SetIPv4([]byte{0, 0, 0, 0})
responseBuffer := alloc.NewSmallBuffer().Clear()
response.Write(responseBuffer)
@@ -198,14 +196,11 @@ func (server *SocksServer) handleUDP(reader *v2net.TimeOutReader, writer io.Writ
response.Port = udpAddr.Port()
switch {
case udpAddr.IsIPv4():
response.AddrType = protocol.AddrTypeIPv4
copy(response.IPv4[:], udpAddr.IP())
response.SetIPv4(udpAddr.IP())
case udpAddr.IsIPv6():
response.AddrType = protocol.AddrTypeIPv6
copy(response.IPv6[:], udpAddr.IP())
response.SetIPv6(udpAddr.IP())
case udpAddr.IsDomain():
response.AddrType = protocol.AddrTypeDomain
response.Domain = udpAddr.Domain()
response.SetDomain(udpAddr.Domain())
}
responseBuffer := alloc.NewSmallBuffer().Clear()

View File

@@ -2,6 +2,7 @@ package socks
import (
"bytes"
"fmt"
"io/ioutil"
"net"
"testing"
@@ -9,22 +10,26 @@ import (
"golang.org/x/net/proxy"
"github.com/v2ray/v2ray-core/app/point"
v2proxy "github.com/v2ray/v2ray-core/proxy"
"github.com/v2ray/v2ray-core/app/point/config/testing/mocks"
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
"github.com/v2ray/v2ray-core/proxy/socks/config/json"
"github.com/v2ray/v2ray-core/testing/mocks"
proxymocks "github.com/v2ray/v2ray-core/proxy/testing/mocks"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestSocksTcpConnect(t *testing.T) {
assert := unit.Assert(t)
port := uint16(12385)
port := v2nettesting.PickPort()
och := &mocks.OutboundConnectionHandler{
Data2Send: bytes.NewBuffer(make([]byte, 0, 1024)),
Data2Return: []byte("The data to be returned to socks server."),
connInput := []byte("The data to be returned to socks server.")
connOutput := bytes.NewBuffer(make([]byte, 0, 1024))
och := &proxymocks.OutboundConnectionHandler{
ConnOutput: connOutput,
ConnInput: bytes.NewReader(connInput),
}
v2proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
connhandler.RegisterOutboundConnectionHandlerFactory("mock_och", och)
config := mocks.Config{
PortValue: port,
@@ -46,7 +51,7 @@ func TestSocksTcpConnect(t *testing.T) {
err = point.Start()
assert.Error(err).IsNil()
socks5Client, err := proxy.SOCKS5("tcp", "127.0.0.1:12385", nil, proxy.Direct)
socks5Client, err := proxy.SOCKS5("tcp", fmt.Sprintf("127.0.0.1:%d", port), nil, proxy.Direct)
assert.Error(err).IsNil()
targetServer := "google.com:80"
@@ -63,21 +68,23 @@ func TestSocksTcpConnect(t *testing.T) {
assert.Error(err).IsNil()
conn.Close()
assert.Bytes([]byte(data2Send)).Equals(och.Data2Send.Bytes())
assert.Bytes(dataReturned).Equals(och.Data2Return)
assert.Bytes([]byte(data2Send)).Equals(connOutput.Bytes())
assert.Bytes(dataReturned).Equals(connInput)
assert.String(targetServer).Equals(och.Destination.Address().String())
}
func TestSocksTcpConnectWithUserPass(t *testing.T) {
assert := unit.Assert(t)
port := uint16(12386)
port := v2nettesting.PickPort()
och := &mocks.OutboundConnectionHandler{
Data2Send: bytes.NewBuffer(make([]byte, 0, 1024)),
Data2Return: []byte("The data to be returned to socks server."),
connInput := []byte("The data to be returned to socks server.")
connOutput := bytes.NewBuffer(make([]byte, 0, 1024))
och := &proxymocks.OutboundConnectionHandler{
ConnInput: bytes.NewReader(connInput),
ConnOutput: connOutput,
}
v2proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
connhandler.RegisterOutboundConnectionHandlerFactory("mock_och", och)
config := mocks.Config{
PortValue: port,
@@ -85,11 +92,8 @@ func TestSocksTcpConnectWithUserPass(t *testing.T) {
ProtocolValue: "socks",
SettingsValue: &json.SocksConfig{
AuthMethod: "password",
Accounts: []json.SocksAccount{
json.SocksAccount{
Username: "userx",
Password: "passy",
},
Accounts: json.SocksAccountMap{
"userx": "passy",
},
},
},
@@ -105,7 +109,7 @@ func TestSocksTcpConnectWithUserPass(t *testing.T) {
err = point.Start()
assert.Error(err).IsNil()
socks5Client, err := proxy.SOCKS5("tcp", "127.0.0.1:12386", &proxy.Auth{"userx", "passy"}, proxy.Direct)
socks5Client, err := proxy.SOCKS5("tcp", fmt.Sprintf("127.0.0.1:%d", port), &proxy.Auth{"userx", "passy"}, proxy.Direct)
assert.Error(err).IsNil()
targetServer := "1.2.3.4:443"
@@ -122,21 +126,23 @@ func TestSocksTcpConnectWithUserPass(t *testing.T) {
assert.Error(err).IsNil()
conn.Close()
assert.Bytes([]byte(data2Send)).Equals(och.Data2Send.Bytes())
assert.Bytes(dataReturned).Equals(och.Data2Return)
assert.Bytes([]byte(data2Send)).Equals(connOutput.Bytes())
assert.Bytes(dataReturned).Equals(connInput)
assert.String(targetServer).Equals(och.Destination.Address().String())
}
func TestSocksTcpConnectWithWrongUserPass(t *testing.T) {
assert := unit.Assert(t)
port := uint16(12389)
port := v2nettesting.PickPort()
och := &mocks.OutboundConnectionHandler{
Data2Send: bytes.NewBuffer(make([]byte, 0, 1024)),
Data2Return: []byte("The data to be returned to socks server."),
connInput := []byte("The data to be returned to socks server.")
connOutput := bytes.NewBuffer(make([]byte, 0, 1024))
och := &proxymocks.OutboundConnectionHandler{
ConnInput: bytes.NewReader(connInput),
ConnOutput: connOutput,
}
v2proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
connhandler.RegisterOutboundConnectionHandlerFactory("mock_och", och)
config := mocks.Config{
PortValue: port,
@@ -144,11 +150,8 @@ func TestSocksTcpConnectWithWrongUserPass(t *testing.T) {
ProtocolValue: "socks",
SettingsValue: &json.SocksConfig{
AuthMethod: "password",
Accounts: []json.SocksAccount{
json.SocksAccount{
Username: "userx",
Password: "passy",
},
Accounts: json.SocksAccountMap{
"userx": "passy",
},
},
},
@@ -164,7 +167,7 @@ func TestSocksTcpConnectWithWrongUserPass(t *testing.T) {
err = point.Start()
assert.Error(err).IsNil()
socks5Client, err := proxy.SOCKS5("tcp", "127.0.0.1:12389", &proxy.Auth{"userx", "passz"}, proxy.Direct)
socks5Client, err := proxy.SOCKS5("tcp", fmt.Sprintf("127.0.0.1:%d", port), &proxy.Auth{"userx", "passz"}, proxy.Direct)
assert.Error(err).IsNil()
targetServer := "1.2.3.4:443"
@@ -174,14 +177,16 @@ func TestSocksTcpConnectWithWrongUserPass(t *testing.T) {
func TestSocksTcpConnectWithWrongAuthMethod(t *testing.T) {
assert := unit.Assert(t)
port := uint16(38405)
port := v2nettesting.PickPort()
och := &mocks.OutboundConnectionHandler{
Data2Send: bytes.NewBuffer(make([]byte, 0, 1024)),
Data2Return: []byte("The data to be returned to socks server."),
connInput := []byte("The data to be returned to socks server.")
connOutput := bytes.NewBuffer(make([]byte, 0, 1024))
och := &proxymocks.OutboundConnectionHandler{
ConnInput: bytes.NewReader(connInput),
ConnOutput: connOutput,
}
v2proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
connhandler.RegisterOutboundConnectionHandlerFactory("mock_och", och)
config := mocks.Config{
PortValue: port,
@@ -189,11 +194,8 @@ func TestSocksTcpConnectWithWrongAuthMethod(t *testing.T) {
ProtocolValue: "socks",
SettingsValue: &json.SocksConfig{
AuthMethod: "password",
Accounts: []json.SocksAccount{
json.SocksAccount{
Username: "userx",
Password: "passy",
},
Accounts: json.SocksAccountMap{
"userx": "passy",
},
},
},
@@ -209,7 +211,7 @@ func TestSocksTcpConnectWithWrongAuthMethod(t *testing.T) {
err = point.Start()
assert.Error(err).IsNil()
socks5Client, err := proxy.SOCKS5("tcp", "127.0.0.1:38405", nil, proxy.Direct)
socks5Client, err := proxy.SOCKS5("tcp", fmt.Sprintf("127.0.0.1:%d", port), nil, proxy.Direct)
assert.Error(err).IsNil()
targetServer := "1.2.3.4:443"
@@ -219,14 +221,16 @@ func TestSocksTcpConnectWithWrongAuthMethod(t *testing.T) {
func TestSocksUdpSend(t *testing.T) {
assert := unit.Assert(t)
port := uint16(12372)
port := v2nettesting.PickPort()
och := &mocks.OutboundConnectionHandler{
Data2Send: bytes.NewBuffer(make([]byte, 0, 1024)),
Data2Return: []byte("The data to be returned to socks server."),
connInput := []byte("The data to be returned to socks server.")
connOutput := bytes.NewBuffer(make([]byte, 0, 1024))
och := &proxymocks.OutboundConnectionHandler{
ConnInput: bytes.NewReader(connInput),
ConnOutput: connOutput,
}
v2proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
connhandler.RegisterOutboundConnectionHandlerFactory("mock_och", och)
config := mocks.Config{
PortValue: port,
@@ -270,7 +274,7 @@ func TestSocksUdpSend(t *testing.T) {
nBytes, err := conn.Read(response)
assert.Error(err).IsNil()
assert.Bytes(response[10:nBytes]).Equals(och.Data2Return)
assert.Bytes(data2Send).Equals(och.Data2Send.Bytes())
assert.Bytes(response[10:nBytes]).Equals(connInput)
assert.Bytes(data2Send).Equals(connOutput.Bytes())
assert.String(och.Destination.String()).Equals("udp:8.8.4.4:53")
}

View File

@@ -2,19 +2,17 @@ package socks
import (
"github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/proxy"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
"github.com/v2ray/v2ray-core/proxy/socks/config/json"
)
type SocksServerFactory struct {
}
func (factory SocksServerFactory) Create(dispatcher app.PacketDispatcher, rawConfig interface{}) (proxy.InboundConnectionHandler, error) {
config := rawConfig.(*json.SocksConfig)
config.Initialize()
return NewSocksServer(dispatcher, config), nil
func (factory SocksServerFactory) Create(dispatcher app.PacketDispatcher, rawConfig interface{}) (connhandler.InboundConnectionHandler, error) {
return NewSocksServer(dispatcher, rawConfig.(*json.SocksConfig)), nil
}
func init() {
proxy.RegisterInboundConnectionHandlerFactory("socks", SocksServerFactory{})
connhandler.RegisterInboundConnectionHandlerFactory("socks", SocksServerFactory{})
}

View File

@@ -46,7 +46,9 @@ func (server *SocksServer) AcceptPackets(conn *net.UDPConn) error {
buffer.Release()
if err != nil {
log.Error("Socks failed to parse UDP request: %v", err)
request.Data.Release()
continue
}
if request.Data == nil || request.Data.Len() == 0 {
continue
}
if request.Fragment != 0 {

View File

@@ -0,0 +1,55 @@
package mocks
import (
"io"
"sync"
"github.com/v2ray/v2ray-core/app"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
)
type InboundConnectionHandler struct {
Port uint16
Dispatcher app.PacketDispatcher
ConnInput io.Reader
ConnOutput io.Writer
}
func (this *InboundConnectionHandler) Listen(port uint16) error {
this.Port = port
return nil
}
func (this *InboundConnectionHandler) Communicate(packet v2net.Packet) error {
ray := this.Dispatcher.DispatchToOutbound(packet)
input := ray.InboundInput()
output := ray.InboundOutput()
readFinish := &sync.Mutex{}
writeFinish := &sync.Mutex{}
readFinish.Lock()
writeFinish.Lock()
go func() {
v2net.ReaderToChan(input, this.ConnInput)
close(input)
readFinish.Unlock()
}()
go func() {
v2net.ChanToWriter(this.ConnOutput, output)
writeFinish.Unlock()
}()
readFinish.Lock()
writeFinish.Lock()
return nil
}
func (this *InboundConnectionHandler) Create(dispatcher app.PacketDispatcher, config interface{}) (connhandler.InboundConnectionHandler, error) {
this.Dispatcher = dispatcher
return this, nil
}

View File

@@ -0,0 +1,49 @@
package mocks
import (
"io"
"sync"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
"github.com/v2ray/v2ray-core/transport/ray"
)
type OutboundConnectionHandler struct {
Destination v2net.Destination
ConnInput io.Reader
ConnOutput io.Writer
}
func (this *OutboundConnectionHandler) Dispatch(packet v2net.Packet, ray ray.OutboundRay) error {
input := ray.OutboundInput()
output := ray.OutboundOutput()
this.Destination = packet.Destination()
if packet.Chunk() != nil {
this.ConnOutput.Write(packet.Chunk().Value)
packet.Chunk().Release()
}
if packet.MoreChunks() {
writeFinish := &sync.Mutex{}
writeFinish.Lock()
go func() {
v2net.ChanToWriter(this.ConnOutput, input)
writeFinish.Unlock()
}()
writeFinish.Lock()
}
v2net.ReaderToChan(output, this.ConnInput)
close(output)
return nil
}
func (this *OutboundConnectionHandler) Create(config interface{}) (connhandler.OutboundConnectionHandler, error) {
return this, nil
}

View File

@@ -2,5 +2,4 @@ package config
type Inbound interface {
AllowedUsers() []User
UDPEnabled() bool
}

View File

@@ -1,14 +1,12 @@
package json
import (
"github.com/v2ray/v2ray-core/config"
"github.com/v2ray/v2ray-core/config/json"
"github.com/v2ray/v2ray-core/proxy/common/config/json"
vmessconfig "github.com/v2ray/v2ray-core/proxy/vmess/config"
)
type Inbound struct {
AllowedClients []*ConfigUser `json:"clients"`
UDP bool `json:"udp"`
}
func (c *Inbound) AllowedUsers() []vmessconfig.User {
@@ -19,12 +17,8 @@ func (c *Inbound) AllowedUsers() []vmessconfig.User {
return users
}
func (c *Inbound) UDPEnabled() bool {
return c.UDP
}
func init() {
json.RegisterConfigType("vmess", config.TypeInbound, func() interface{} {
json.RegisterInboundConnectionConfig("vmess", func() interface{} {
return new(Inbound)
})
}

View File

@@ -3,12 +3,11 @@ package json
import (
"encoding/json"
"net"
"strings"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/config"
jsonconfig "github.com/v2ray/v2ray-core/config/json"
proxyconfig "github.com/v2ray/v2ray-core/proxy/common/config"
jsonconfig "github.com/v2ray/v2ray-core/proxy/common/config/json"
vmessconfig "github.com/v2ray/v2ray-core/proxy/vmess/config"
)
@@ -16,18 +15,11 @@ type RawConfigTarget struct {
Address string `json:"address"`
Port uint16 `json:"port"`
Users []*ConfigUser `json:"users"`
Network string `json:"network"`
}
func (config RawConfigTarget) HasNetwork(network string) bool {
return strings.Contains(config.Network, network)
}
type ConfigTarget struct {
Address v2net.Address
Users []*ConfigUser
TCPEnabled bool
UDPEnabled bool
Address v2net.Address
Users []*ConfigUser
}
func (t *ConfigTarget) UnmarshalJSON(data []byte) error {
@@ -39,15 +31,9 @@ func (t *ConfigTarget) UnmarshalJSON(data []byte) error {
ip := net.ParseIP(rawConfig.Address)
if ip == nil {
log.Error("Unable to parse IP: %s", rawConfig.Address)
return config.BadConfiguration
return proxyconfig.BadConfiguration
}
t.Address = v2net.IPAddress(ip, rawConfig.Port)
if rawConfig.HasNetwork("tcp") {
t.TCPEnabled = true
}
if rawConfig.HasNetwork("udp") {
t.UDPEnabled = true
}
return nil
}
@@ -62,24 +48,16 @@ func (o *Outbound) Targets() []*vmessconfig.OutboundTarget {
for _, rawUser := range rawTarget.Users {
users = append(users, rawUser)
}
if rawTarget.TCPEnabled {
targets = append(targets, &vmessconfig.OutboundTarget{
Destination: v2net.NewTCPDestination(rawTarget.Address),
Accounts: users,
})
}
if rawTarget.UDPEnabled {
targets = append(targets, &vmessconfig.OutboundTarget{
Destination: v2net.NewUDPDestination(rawTarget.Address),
Accounts: users,
})
}
targets = append(targets, &vmessconfig.OutboundTarget{
Destination: v2net.NewTCPDestination(rawTarget.Address),
Accounts: users,
})
}
return targets
}
func init() {
jsonconfig.RegisterConfigType("vmess", config.TypeOutbound, func() interface{} {
jsonconfig.RegisterOutboundConnectionConfig("vmess", func() interface{} {
return new(Outbound)
})
}

View File

@@ -8,14 +8,16 @@ import (
// ConfigUser is an user account in VMess configuration.
type ConfigUser struct {
Id *config.ID
Email string
Id *config.ID
Email string
LevelValue config.UserLevel
}
func (u *ConfigUser) UnmarshalJSON(data []byte) error {
type rawUser struct {
IdString string `json:"id"`
EmailString string `json:"email"`
LevelInt int `json:"level"`
}
var rawUserValue rawUser
if err := json.Unmarshal(data, &rawUserValue); err != nil {
@@ -27,9 +29,14 @@ func (u *ConfigUser) UnmarshalJSON(data []byte) error {
}
u.Id = id
u.Email = rawUserValue.EmailString
u.LevelValue = config.UserLevel(rawUserValue.LevelInt)
return nil
}
func (u *ConfigUser) ID() *config.ID {
return u.Id
}
func (this *ConfigUser) Level() config.UserLevel {
return this.LevelValue
}

View File

@@ -1,5 +1,27 @@
package config
type UserLevel int
const (
UserLevelAdmin = UserLevel(999)
UserLevelUntrusted = UserLevel(0)
)
type User interface {
ID() *ID
Level() UserLevel
}
type UserSettings struct {
PayloadReadTimeout int
}
func GetUserSettings(level UserLevel) UserSettings {
settings := UserSettings{
PayloadReadTimeout: 120,
}
if level > 0 {
settings.PayloadReadTimeout = 0
}
return settings
}

View File

@@ -5,20 +5,20 @@ import (
)
type MockUserSet struct {
UserIds []*config.ID
Users []config.User
UserHashes map[string]int
Timestamps map[string]int64
}
func (us *MockUserSet) AddUser(user config.User) error {
us.UserIds = append(us.UserIds, user.ID())
us.Users = append(us.Users, user)
return nil
}
func (us *MockUserSet) GetUser(userhash []byte) (*config.ID, int64, bool) {
func (us *MockUserSet) GetUser(userhash []byte) (config.User, int64, bool) {
idx, found := us.UserHashes[string(userhash)]
if found {
return us.UserIds[idx], us.Timestamps[string(userhash)], true
return us.Users[idx], us.Timestamps[string(userhash)], true
}
return nil, 0, false
}

View File

@@ -15,11 +15,11 @@ const (
type UserSet interface {
AddUser(user config.User) error
GetUser(timeHash []byte) (*config.ID, int64, bool)
GetUser(timeHash []byte) (config.User, int64, bool)
}
type TimedUserSet struct {
validUserIds []*config.ID
validUsers []config.User
userHash map[string]indexTimePair
userHashDeleteQueue *collect.TimedQueue
access sync.RWMutex
@@ -32,7 +32,7 @@ type indexTimePair struct {
func NewTimedUserSet() UserSet {
tus := &TimedUserSet{
validUserIds: make([]*config.ID, 0, 16),
validUsers: make([]config.User, 0, 16),
userHash: make(map[string]indexTimePair, 512),
userHashDeleteQueue: collect.NewTimedQueue(updateIntervalSec),
access: sync.RWMutex{},
@@ -63,12 +63,12 @@ func (us *TimedUserSet) generateNewHashes(lastSec, nowSec int64, idx int, id *co
}
func (us *TimedUserSet) updateUserHash(tick <-chan time.Time) {
lastSec := time.Now().UTC().Unix() - cacheDurationSec
lastSec := time.Now().Unix() - cacheDurationSec
for now := range tick {
nowSec := now.UTC().Unix() + cacheDurationSec
for idx, id := range us.validUserIds {
us.generateNewHashes(lastSec, nowSec, idx, id)
nowSec := now.Unix() + cacheDurationSec
for idx, user := range us.validUsers {
us.generateNewHashes(lastSec, nowSec, idx, user.ID())
}
lastSec = nowSec
}
@@ -76,22 +76,22 @@ func (us *TimedUserSet) updateUserHash(tick <-chan time.Time) {
func (us *TimedUserSet) AddUser(user config.User) error {
id := user.ID()
idx := len(us.validUserIds)
us.validUserIds = append(us.validUserIds, id)
idx := len(us.validUsers)
us.validUsers = append(us.validUsers, user)
nowSec := time.Now().UTC().Unix()
nowSec := time.Now().Unix()
lastSec := nowSec - cacheDurationSec
us.generateNewHashes(lastSec, nowSec+cacheDurationSec, idx, id)
return nil
}
func (us TimedUserSet) GetUser(userHash []byte) (*config.ID, int64, bool) {
func (us TimedUserSet) GetUser(userHash []byte) (config.User, int64, bool) {
defer us.access.RUnlock()
us.access.RLock()
pair, found := us.userHash[string(userHash)]
if found {
return us.validUserIds[pair.index], pair.timeSec, true
return us.validUsers[pair.index], pair.timeSec, true
}
return nil, 0, false
}

View File

@@ -2,17 +2,16 @@
package protocol
import (
"crypto/aes"
"crypto/cipher"
"encoding/binary"
"hash/fnv"
"io"
"time"
v2io "github.com/v2ray/v2ray-core/common/io"
"github.com/v2ray/v2ray-core/common/alloc"
v2crypto "github.com/v2ray/v2ray-core/common/crypto"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy"
proxyerrors "github.com/v2ray/v2ray-core/proxy/common/errors"
"github.com/v2ray/v2ray-core/proxy/vmess/config"
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
"github.com/v2ray/v2ray-core/transport"
@@ -36,7 +35,7 @@ const (
// streaming.
type VMessRequest struct {
Version byte
UserId config.ID
User config.User
RequestIV []byte
RequestKey []byte
ResponseHeader []byte
@@ -67,90 +66,86 @@ func NewVMessRequestReader(vUserSet user.UserSet) *VMessRequestReader {
// Read reads a VMessRequest from a byte stream.
func (r *VMessRequestReader) Read(reader io.Reader) (*VMessRequest, error) {
buffer := make([]byte, 256)
buffer := alloc.NewSmallBuffer()
nBytes, err := reader.Read(buffer[:config.IDBytesLen])
nBytes, err := v2net.ReadAllBytes(reader, buffer.Value[:config.IDBytesLen])
if err != nil {
return nil, err
}
userId, timeSec, valid := r.vUserSet.GetUser(buffer[:nBytes])
userObj, timeSec, valid := r.vUserSet.GetUser(buffer.Value[:nBytes])
if !valid {
return nil, proxy.InvalidAuthentication
return nil, proxyerrors.InvalidAuthentication
}
aesCipher, err := aes.NewCipher(userId.CmdKey())
if err != nil {
return nil, err
}
aesStream := cipher.NewCFBDecrypter(aesCipher, user.Int64Hash(timeSec))
decryptor := v2io.NewCryptionReader(aesStream, reader)
aesStream, err := v2crypto.NewAesDecryptionStream(userObj.ID().CmdKey(), user.Int64Hash(timeSec))
if err != nil {
return nil, err
}
nBytes, err = decryptor.Read(buffer[:41])
decryptor := v2crypto.NewCryptionReader(aesStream, reader)
nBytes, err = v2net.ReadAllBytes(decryptor, buffer.Value[:41])
if err != nil {
return nil, err
}
bufferLen := nBytes
request := &VMessRequest{
UserId: *userId,
Version: buffer[0],
User: userObj,
Version: buffer.Value[0],
}
if request.Version != Version {
log.Warning("Invalid protocol version %d", request.Version)
return nil, proxy.InvalidProtocolVersion
return nil, proxyerrors.InvalidProtocolVersion
}
request.RequestIV = buffer[1:17] // 16 bytes
request.RequestKey = buffer[17:33] // 16 bytes
request.ResponseHeader = buffer[33:37] // 4 bytes
request.Command = buffer[37]
request.RequestIV = buffer.Value[1:17] // 16 bytes
request.RequestKey = buffer.Value[17:33] // 16 bytes
request.ResponseHeader = buffer.Value[33:37] // 4 bytes
request.Command = buffer.Value[37]
port := binary.BigEndian.Uint16(buffer[38:40])
port := binary.BigEndian.Uint16(buffer.Value[38:40])
switch buffer[40] {
switch buffer.Value[40] {
case addrTypeIPv4:
_, err = decryptor.Read(buffer[41:45]) // 4 bytes
_, err = v2net.ReadAllBytes(decryptor, buffer.Value[41:45]) // 4 bytes
bufferLen += 4
if err != nil {
return nil, err
}
request.Address = v2net.IPAddress(buffer[41:45], port)
request.Address = v2net.IPAddress(buffer.Value[41:45], port)
case addrTypeIPv6:
_, err = decryptor.Read(buffer[41:57]) // 16 bytes
_, err = v2net.ReadAllBytes(decryptor, buffer.Value[41:57]) // 16 bytes
bufferLen += 16
if err != nil {
return nil, err
}
request.Address = v2net.IPAddress(buffer[41:57], port)
request.Address = v2net.IPAddress(buffer.Value[41:57], port)
case addrTypeDomain:
_, err = decryptor.Read(buffer[41:42])
_, err = v2net.ReadAllBytes(decryptor, buffer.Value[41:42])
if err != nil {
return nil, err
}
domainLength := int(buffer[41])
_, err = decryptor.Read(buffer[42 : 42+domainLength])
domainLength := int(buffer.Value[41])
_, err = v2net.ReadAllBytes(decryptor, buffer.Value[42:42+domainLength])
if err != nil {
return nil, err
}
bufferLen += 1 + domainLength
request.Address = v2net.DomainAddress(string(buffer[42:42+domainLength]), port)
request.Address = v2net.DomainAddress(string(buffer.Value[42:42+domainLength]), port)
}
_, err = decryptor.Read(buffer[bufferLen : bufferLen+4])
_, err = v2net.ReadAllBytes(decryptor, buffer.Value[bufferLen:bufferLen+4])
if err != nil {
return nil, err
}
fnv1a := fnv.New32a()
fnv1a.Write(buffer[:bufferLen])
fnv1a.Write(buffer.Value[:bufferLen])
actualHash := fnv1a.Sum32()
expectedHash := binary.BigEndian.Uint32(buffer[bufferLen : bufferLen+4])
expectedHash := binary.BigEndian.Uint32(buffer.Value[bufferLen : bufferLen+4])
if actualHash != expectedHash {
return nil, transport.CorruptedPacket
@@ -160,56 +155,51 @@ func (r *VMessRequestReader) Read(reader io.Reader) (*VMessRequest, error) {
}
// ToBytes returns a VMessRequest in the form of byte array.
func (request *VMessRequest) ToBytes(idHash user.CounterHash, randomRangeInt64 user.RandomInt64InRange, buffer []byte) ([]byte, error) {
func (request *VMessRequest) ToBytes(idHash user.CounterHash, randomRangeInt64 user.RandomInt64InRange, buffer *alloc.Buffer) (*alloc.Buffer, error) {
if buffer == nil {
buffer = make([]byte, 0, 300)
buffer = alloc.NewSmallBuffer().Clear()
}
counter := randomRangeInt64(time.Now().UTC().Unix(), 30)
hash := idHash.Hash(request.UserId.Bytes[:], counter)
counter := randomRangeInt64(time.Now().Unix(), 30)
hash := idHash.Hash(request.User.ID().Bytes[:], counter)
buffer = append(buffer, hash...)
buffer.Append(hash)
encryptionBegin := len(buffer)
encryptionBegin := buffer.Len()
buffer = append(buffer, request.Version)
buffer = append(buffer, request.RequestIV...)
buffer = append(buffer, request.RequestKey...)
buffer = append(buffer, request.ResponseHeader...)
buffer = append(buffer, request.Command)
buffer = append(buffer, request.Address.PortBytes()...)
buffer.AppendBytes(request.Version)
buffer.Append(request.RequestIV)
buffer.Append(request.RequestKey)
buffer.Append(request.ResponseHeader)
buffer.AppendBytes(request.Command)
buffer.Append(request.Address.PortBytes())
switch {
case request.Address.IsIPv4():
buffer = append(buffer, addrTypeIPv4)
buffer = append(buffer, request.Address.IP()...)
buffer.AppendBytes(addrTypeIPv4)
buffer.Append(request.Address.IP())
case request.Address.IsIPv6():
buffer = append(buffer, addrTypeIPv6)
buffer = append(buffer, request.Address.IP()...)
buffer.AppendBytes(addrTypeIPv6)
buffer.Append(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.AppendBytes(addrTypeDomain, byte(len(request.Address.Domain())))
buffer.Append([]byte(request.Address.Domain()))
}
encryptionEnd := len(buffer)
encryptionEnd := buffer.Len()
fnv1a := fnv.New32a()
fnv1a.Write(buffer[encryptionBegin:encryptionEnd])
fnv1a.Write(buffer.Value[encryptionBegin:encryptionEnd])
fnvHash := fnv1a.Sum32()
buffer = append(buffer, byte(fnvHash>>24))
buffer = append(buffer, byte(fnvHash>>16))
buffer = append(buffer, byte(fnvHash>>8))
buffer = append(buffer, byte(fnvHash))
buffer.AppendBytes(byte(fnvHash>>24), byte(fnvHash>>16), byte(fnvHash>>8), byte(fnvHash))
encryptionEnd += 4
aesCipher, err := aes.NewCipher(request.UserId.CmdKey())
aesStream, err := v2crypto.NewAesEncryptionStream(request.User.ID().CmdKey(), user.Int64Hash(counter))
if err != nil {
return nil, err
}
aesStream := cipher.NewCFBEncrypter(aesCipher, user.Int64Hash(counter))
aesStream.XORKeyStream(buffer[encryptionBegin:encryptionEnd], buffer[encryptionBegin:encryptionEnd])
aesStream.XORKeyStream(buffer.Value[encryptionBegin:encryptionEnd], buffer.Value[encryptionBegin:encryptionEnd])
return buffer, nil
}

View File

@@ -3,23 +3,29 @@ package protocol
import (
"bytes"
"crypto/rand"
"io"
"testing"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy/vmess/config"
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
"github.com/v2ray/v2ray-core/testing/mocks"
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user/testing/mocks"
"github.com/v2ray/v2ray-core/testing/unit"
)
type TestUser struct {
id *config.ID
id *config.ID
level config.UserLevel
}
func (u *TestUser) ID() *config.ID {
return u.id
}
func (this *TestUser) Level() config.UserLevel {
return this.level
}
func TestVMessSerialization(t *testing.T) {
assert := unit.Assert(t)
@@ -28,12 +34,16 @@ func TestVMessSerialization(t *testing.T) {
t.Fatal(err)
}
userSet := mocks.MockUserSet{[]*config.ID{}, make(map[string]int), make(map[string]int64)}
userSet.AddUser(&TestUser{userId})
testUser := &TestUser{
id: userId,
}
userSet := mocks.MockUserSet{[]config.User{}, make(map[string]int), make(map[string]int64)}
userSet.AddUser(testUser)
request := new(VMessRequest)
request.Version = byte(0x01)
request.UserId = *userId
request.User = testUser
randBytes := make([]byte, 36)
_, err = rand.Read(randBytes)
@@ -46,22 +56,23 @@ func TestVMessSerialization(t *testing.T) {
request.Address = v2net.DomainAddress("v2ray.com", 80)
mockTime := int64(1823730)
buffer, err := request.ToBytes(user.NewTimeHash(user.HMACHash{}), func(base int64, delta int) int64 { return mockTime }, nil)
if err != nil {
t.Fatal(err)
}
userSet.UserHashes[string(buffer[:16])] = 0
userSet.Timestamps[string(buffer[:16])] = mockTime
userSet.UserHashes[string(buffer.Value[:16])] = 0
userSet.Timestamps[string(buffer.Value[:16])] = mockTime
requestReader := NewVMessRequestReader(&userSet)
actualRequest, err := requestReader.Read(bytes.NewReader(buffer))
actualRequest, err := requestReader.Read(bytes.NewReader(buffer.Value))
if err != nil {
t.Fatal(err)
}
assert.Byte(actualRequest.Version).Named("Version").Equals(byte(0x01))
assert.String(actualRequest.UserId.String).Named("UserId").Equals(request.UserId.String)
assert.String(actualRequest.User.ID().String).Named("UserId").Equals(request.User.ID().String)
assert.Bytes(actualRequest.RequestIV).Named("RequestIV").Equals(request.RequestIV[:])
assert.Bytes(actualRequest.RequestKey).Named("RequestKey").Equals(request.RequestKey[:])
assert.Bytes(actualRequest.ResponseHeader).Named("ResponseHeader").Equals(request.ResponseHeader[:])
@@ -69,14 +80,26 @@ func TestVMessSerialization(t *testing.T) {
assert.String(actualRequest.Address.String()).Named("Address").Equals(request.Address.String())
}
func TestReadSingleByte(t *testing.T) {
assert := unit.Assert(t)
reader := NewVMessRequestReader(nil)
_, err := reader.Read(bytes.NewReader(make([]byte, 1)))
assert.Error(err).Equals(io.EOF)
}
func BenchmarkVMessRequestWriting(b *testing.B) {
userId, _ := config.NewID("2b2966ac-16aa-4fbf-8d81-c5f172a3da51")
userSet := mocks.MockUserSet{[]*config.ID{}, make(map[string]int), make(map[string]int64)}
userSet.AddUser(&TestUser{userId})
userSet := mocks.MockUserSet{[]config.User{}, make(map[string]int), make(map[string]int64)}
testUser := &TestUser{
id: userId,
}
userSet.AddUser(testUser)
request := new(VMessRequest)
request.Version = byte(0x01)
request.UserId = *userId
request.User = testUser
randBytes := make([]byte, 36)
rand.Read(randBytes)

View File

@@ -5,29 +5,33 @@ import (
"testing"
"github.com/v2ray/v2ray-core/app/point"
"github.com/v2ray/v2ray-core/common/alloc"
"github.com/v2ray/v2ray-core/app/point/config/testing/mocks"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy"
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
proxymocks "github.com/v2ray/v2ray-core/proxy/testing/mocks"
"github.com/v2ray/v2ray-core/proxy/vmess/config"
"github.com/v2ray/v2ray-core/proxy/vmess/config/json"
"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."
testAccount, err := config.NewID("ad937d9d-6e23-4a5a-ba23-bce5092a7c51")
assert.Error(err).IsNil()
portA := uint16(17392)
ich := &mocks.InboundConnectionHandler{
Data2Send: []byte(data2Send),
DataReturned: bytes.NewBuffer(make([]byte, 0, 1024)),
portA := v2nettesting.PickPort()
portB := v2nettesting.PickPort()
ichConnInput := []byte("The data to be send to outbound server.")
ichConnOutput := bytes.NewBuffer(make([]byte, 0, 1024))
ich := &proxymocks.InboundConnectionHandler{
ConnInput: bytes.NewReader(ichConnInput),
ConnOutput: ichConnOutput,
}
proxy.RegisterInboundConnectionHandlerFactory("mock_ich", ich)
connhandler.RegisterInboundConnectionHandlerFactory("mock_ich", ich)
configA := mocks.Config{
PortValue: portA,
@@ -40,8 +44,7 @@ func TestVMessInAndOut(t *testing.T) {
SettingsValue: &json.Outbound{
[]*json.ConfigTarget{
&json.ConfigTarget{
Address: v2net.IPAddress([]byte{127, 0, 0, 1}, 13829),
TCPEnabled: true,
Address: v2net.IPAddress([]byte{127, 0, 0, 1}, portB),
Users: []*json.ConfigUser{
&json.ConfigUser{Id: testAccount},
},
@@ -57,14 +60,14 @@ func TestVMessInAndOut(t *testing.T) {
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."),
ochConnInput := []byte("The data to be returned to inbound server.")
ochConnOutput := bytes.NewBuffer(make([]byte, 0, 1024))
och := &proxymocks.OutboundConnectionHandler{
ConnInput: bytes.NewReader(ochConnInput),
ConnOutput: ochConnOutput,
}
proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
connhandler.RegisterOutboundConnectionHandlerFactory("mock_och", och)
configB := mocks.Config{
PortValue: portB,
@@ -90,90 +93,6 @@ func TestVMessInAndOut(t *testing.T) {
dest := v2net.NewTCPDestination(v2net.IPAddress([]byte{1, 2, 3, 4}, 80))
ich.Communicate(v2net.NewPacket(dest, nil, true))
assert.Bytes([]byte(data2Send)).Equals(och.Data2Send.Bytes())
assert.Bytes(ich.DataReturned.Bytes()).Equals(och.Data2Return)
}
func TestVMessInAndOutUDP(t *testing.T) {
assert := unit.Assert(t)
data2Send := "The data to be send to outbound server."
testAccount, err := config.NewID("ad937d9d-6e23-4a5a-ba23-bce5092a7c51")
assert.Error(err).IsNil()
portA := uint16(17394)
ich := &mocks.InboundConnectionHandler{
Data2Send: []byte(data2Send),
DataReturned: bytes.NewBuffer(make([]byte, 0, 1024)),
}
proxy.RegisterInboundConnectionHandlerFactory("mock_ich", ich)
configA := mocks.Config{
PortValue: portA,
InboundConfigValue: &mocks.ConnectionConfig{
ProtocolValue: "mock_ich",
SettingsValue: nil,
},
OutboundConfigValue: &mocks.ConnectionConfig{
ProtocolValue: "vmess",
SettingsValue: &json.Outbound{
[]*json.ConfigTarget{
&json.ConfigTarget{
Address: v2net.IPAddress([]byte{127, 0, 0, 1}, 13841),
UDPEnabled: true,
Users: []*json.ConfigUser{
&json.ConfigUser{Id: testAccount},
},
},
},
},
},
}
pointA, err := point.NewPoint(&configA)
assert.Error(err).IsNil()
err = pointA.Start()
assert.Error(err).IsNil()
portB := uint16(13841)
och := &mocks.OutboundConnectionHandler{
Data2Send: bytes.NewBuffer(make([]byte, 0, 1024)),
Data2Return: []byte("The data to be returned to inbound server."),
}
proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
configB := mocks.Config{
PortValue: portB,
InboundConfigValue: &mocks.ConnectionConfig{
ProtocolValue: "vmess",
SettingsValue: &json.Inbound{
AllowedClients: []*json.ConfigUser{
&json.ConfigUser{Id: testAccount},
},
UDP: true,
},
},
OutboundConfigValue: &mocks.ConnectionConfig{
ProtocolValue: "mock_och",
SettingsValue: nil,
},
}
pointB, err := point.NewPoint(&configB)
assert.Error(err).IsNil()
err = pointB.Start()
assert.Error(err).IsNil()
data2SendBuffer := alloc.NewBuffer()
data2SendBuffer.Clear()
data2SendBuffer.Append([]byte(data2Send))
dest := v2net.NewUDPDestination(v2net.IPAddress([]byte{1, 2, 3, 4}, 80))
ich.Communicate(v2net.NewPacket(dest, data2SendBuffer, false))
assert.Bytes([]byte(data2Send)).Equals(och.Data2Send.Bytes())
assert.Bytes(ich.DataReturned.Bytes()).Equals(och.Data2Return)
assert.Bytes(ichConnInput).Equals(ochConnOutput.Bytes())
assert.Bytes(ichConnOutput.Bytes()).Equals(ochConnInput)
}

View File

@@ -8,11 +8,11 @@ import (
"github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/common/alloc"
v2io "github.com/v2ray/v2ray-core/common/io"
v2crypto "github.com/v2ray/v2ray-core/common/crypto"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/retry"
"github.com/v2ray/v2ray-core/proxy"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
"github.com/v2ray/v2ray-core/proxy/vmess/config"
"github.com/v2ray/v2ray-core/proxy/vmess/protocol"
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
@@ -22,14 +22,12 @@ type VMessInboundHandler struct {
dispatcher app.PacketDispatcher
clients user.UserSet
accepting bool
udpEnabled bool
}
func NewVMessInboundHandler(dispatcher app.PacketDispatcher, clients user.UserSet, udpEnabled bool) *VMessInboundHandler {
func NewVMessInboundHandler(dispatcher app.PacketDispatcher, clients user.UserSet) *VMessInboundHandler {
return &VMessInboundHandler{
dispatcher: dispatcher,
clients: clients,
udpEnabled: udpEnabled,
}
}
@@ -45,11 +43,6 @@ func (handler *VMessInboundHandler) Listen(port uint16) error {
}
handler.accepting = true
go handler.AcceptConnections(listener)
if handler.udpEnabled {
handler.ListenUDP(port)
}
return nil
}
@@ -72,7 +65,7 @@ func (handler *VMessInboundHandler) AcceptConnections(listener *net.TCPListener)
func (handler *VMessInboundHandler) HandleConnection(connection *net.TCPConn) error {
defer connection.Close()
connReader := v2net.NewTimeOutReader(120, connection)
connReader := v2net.NewTimeOutReader(16, connection)
requestReader := protocol.NewVMessRequestReader(handler.clients)
request, err := requestReader.Read(connReader)
@@ -91,17 +84,21 @@ func (handler *VMessInboundHandler) HandleConnection(connection *net.TCPConn) er
readFinish.Lock()
writeFinish.Lock()
userSettings := config.GetUserSettings(request.User.Level())
connReader.SetTimeOut(userSettings.PayloadReadTimeout)
go handleInput(request, connReader, input, &readFinish)
responseKey := md5.Sum(request.RequestKey)
responseIV := md5.Sum(request.RequestIV)
responseWriter, err := v2io.NewAesEncryptWriter(responseKey[:], responseIV[:], connection)
aesStream, err := v2crypto.NewAesEncryptionStream(responseKey[:], responseIV[:])
if err != nil {
log.Error("VMessIn: Failed to create encrypt writer: %v", err)
log.Error("VMessIn: Failed to create AES decryption stream: %v", err)
return err
}
responseWriter := v2crypto.NewCryptionWriter(aesStream, connection)
// Optimize for small response packet
buffer := alloc.NewLargeBuffer().Clear()
buffer.Append(request.ResponseHeader)
@@ -125,12 +122,12 @@ func handleInput(request *protocol.VMessRequest, reader io.Reader, input chan<-
defer close(input)
defer finish.Unlock()
requestReader, err := v2io.NewAesDecryptReader(request.RequestKey, request.RequestIV, reader)
aesStream, err := v2crypto.NewAesDecryptionStream(request.RequestKey, request.RequestIV)
if err != nil {
log.Error("VMessIn: Failed to create decrypt reader: %v", err)
log.Error("VMessIn: Failed to create AES decryption stream: %v", err)
return
}
requestReader := v2crypto.NewCryptionReader(aesStream, reader)
v2net.ReaderToChan(input, requestReader)
}
@@ -142,7 +139,7 @@ func handleOutput(request *protocol.VMessRequest, writer io.Writer, output <-cha
type VMessInboundHandlerFactory struct {
}
func (factory *VMessInboundHandlerFactory) Create(dispatcher app.PacketDispatcher, rawConfig interface{}) (proxy.InboundConnectionHandler, error) {
func (factory *VMessInboundHandlerFactory) Create(dispatcher app.PacketDispatcher, rawConfig interface{}) (connhandler.InboundConnectionHandler, error) {
config := rawConfig.(config.Inbound)
allowedClients := user.NewTimedUserSet()
@@ -150,9 +147,9 @@ func (factory *VMessInboundHandlerFactory) Create(dispatcher app.PacketDispatche
allowedClients.AddUser(user)
}
return NewVMessInboundHandler(dispatcher, allowedClients, config.UDPEnabled()), nil
return NewVMessInboundHandler(dispatcher, allowedClients), nil
}
func init() {
proxy.RegisterInboundConnectionHandlerFactory("vmess", &VMessInboundHandlerFactory{})
connhandler.RegisterInboundConnectionHandlerFactory("vmess", &VMessInboundHandlerFactory{})
}

View File

@@ -1,104 +0,0 @@
package vmess
import (
"bytes"
"crypto/md5"
"net"
"github.com/v2ray/v2ray-core/common/alloc"
v2io "github.com/v2ray/v2ray-core/common/io"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy/vmess/protocol"
)
const (
bufferSize = 2 * 1024
)
func (handler *VMessInboundHandler) ListenUDP(port uint16) error {
addr := &net.UDPAddr{
IP: net.IP{0, 0, 0, 0},
Port: int(port),
Zone: "",
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
log.Error("VMessIn failed to listen UDP on port %d: %v", port, err)
return err
}
go handler.AcceptPackets(conn)
return nil
}
func (handler *VMessInboundHandler) AcceptPackets(conn *net.UDPConn) {
for {
buffer := make([]byte, bufferSize)
nBytes, addr, err := conn.ReadFromUDP(buffer)
if err != nil {
log.Error("VMessIn failed to read UDP packets: %v", err)
continue
}
reader := bytes.NewReader(buffer[:nBytes])
requestReader := protocol.NewVMessRequestReader(handler.clients)
request, err := requestReader.Read(reader)
if err != nil {
log.Access(addr.String(), "", log.AccessRejected, err.Error())
log.Warning("VMessIn: Invalid request from (%s): %v", addr.String(), err)
continue
}
log.Access(addr.String(), request.Address.String(), log.AccessAccepted, "")
cryptReader, err := v2io.NewAesDecryptReader(request.RequestKey, request.RequestIV, reader)
if err != nil {
log.Error("VMessIn: Failed to create decrypt reader: %v", err)
continue
}
data := alloc.NewBuffer()
nBytes, err = cryptReader.Read(data.Value)
if err != nil {
log.Warning("VMessIn: Unable to decrypt data: %v", err)
data.Release()
continue
}
data.Slice(0, nBytes)
packet := v2net.NewPacket(request.Destination(), data, false)
go handler.handlePacket(conn, request, packet, addr)
}
}
func (handler *VMessInboundHandler) handlePacket(conn *net.UDPConn, request *protocol.VMessRequest, packet v2net.Packet, clientAddr *net.UDPAddr) {
ray := handler.dispatcher.DispatchToOutbound(packet)
close(ray.InboundInput())
responseKey := md5.Sum(request.RequestKey)
responseIV := md5.Sum(request.RequestIV)
buffer := alloc.NewBuffer().Clear()
defer buffer.Release()
responseWriter, err := v2io.NewAesEncryptWriter(responseKey[:], responseIV[:], buffer)
if err != nil {
log.Error("VMessIn: Failed to create encrypt writer: %v", err)
return
}
responseWriter.Write(request.ResponseHeader)
hasData := false
if data, ok := <-ray.InboundOutput(); ok {
hasData = true
responseWriter.Write(data.Value)
data.Release()
}
if hasData {
conn.WriteToUDP(buffer.Value, clientAddr)
log.Info("VMessIn sending %d bytes to %s", buffer.Len(), clientAddr.String())
}
}

View File

@@ -9,29 +9,23 @@ import (
"sync"
"github.com/v2ray/v2ray-core/common/alloc"
v2io "github.com/v2ray/v2ray-core/common/io"
v2crypto "github.com/v2ray/v2ray-core/common/crypto"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy"
"github.com/v2ray/v2ray-core/proxy/common/connhandler"
"github.com/v2ray/v2ray-core/proxy/vmess/config"
"github.com/v2ray/v2ray-core/proxy/vmess/protocol"
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
"github.com/v2ray/v2ray-core/transport/ray"
)
const (
InfoTimeNotSync = "Please check the User ID in your vmess configuration, and make sure the time on your local and remote server are in sync."
)
type VMessOutboundHandler struct {
vNextList []*config.OutboundTarget
vNextListUDP []*config.OutboundTarget
vNextList []*config.OutboundTarget
}
func NewVMessOutboundHandler(vNextList, vNextListUDP []*config.OutboundTarget) *VMessOutboundHandler {
func NewVMessOutboundHandler(vNextList []*config.OutboundTarget) *VMessOutboundHandler {
return &VMessOutboundHandler{
vNextList: vNextList,
vNextListUDP: vNextListUDP,
vNextList: vNextList,
}
}
@@ -60,9 +54,6 @@ func pickVNext(serverList []*config.OutboundTarget) (v2net.Destination, config.U
func (handler *VMessOutboundHandler) Dispatch(firstPacket v2net.Packet, ray ray.OutboundRay) error {
vNextList := handler.vNextList
if firstPacket.Destination().IsUDP() {
vNextList = handler.vNextListUDP
}
vNextAddress, vNextUser := pickVNext(vNextList)
command := protocol.CmdTCP
@@ -71,16 +62,17 @@ func (handler *VMessOutboundHandler) Dispatch(firstPacket v2net.Packet, ray ray.
}
request := &protocol.VMessRequest{
Version: protocol.Version,
UserId: *vNextUser.ID(),
User: vNextUser,
Command: command,
Address: firstPacket.Destination().Address(),
}
buffer := make([]byte, 36) // 16 + 16 + 4
rand.Read(buffer)
request.RequestIV = buffer[:16]
request.RequestKey = buffer[16:32]
request.ResponseHeader = buffer[32:]
buffer := alloc.NewSmallBuffer()
defer buffer.Release()
v2net.ReadAllBytes(rand.Reader, buffer.Value[:36]) // 16 + 16 + 4
request.RequestIV = buffer.Value[:16]
request.RequestKey = buffer.Value[16:32]
request.ResponseHeader = buffer.Value[32:36]
return startCommunicate(request, vNextAddress, ray, firstPacket)
}
@@ -117,14 +109,15 @@ func startCommunicate(request *protocol.VMessRequest, dest v2net.Destination, ra
func handleRequest(conn net.Conn, request *protocol.VMessRequest, firstPacket v2net.Packet, input <-chan *alloc.Buffer, finish *sync.Mutex) {
defer finish.Unlock()
encryptRequestWriter, err := v2io.NewAesEncryptWriter(request.RequestKey[:], request.RequestIV[:], conn)
aesStream, err := v2crypto.NewAesEncryptionStream(request.RequestKey[:], request.RequestIV[:])
if err != nil {
log.Error("VMessOut: Failed to create encrypt writer: %v", err)
log.Error("VMessOut: Failed to create AES encryption stream: %v", err)
return
}
encryptRequestWriter := v2crypto.NewCryptionWriter(aesStream, conn)
buffer := alloc.NewBuffer().Clear()
requestBytes, err := request.ToBytes(user.NewTimeHash(user.HMACHash{}), user.GenerateRandomInt64InRange, buffer.Value)
buffer, err = request.ToBytes(user.NewTimeHash(user.HMACHash{}), user.GenerateRandomInt64InRange, buffer)
if err != nil {
log.Error("VMessOut: Failed to serialize VMess request: %v", err)
return
@@ -139,11 +132,11 @@ func handleRequest(conn net.Conn, request *protocol.VMessRequest, firstPacket v2
}
if firstChunk != nil {
encryptRequestWriter.Crypt(firstChunk.Value)
requestBytes = append(requestBytes, firstChunk.Value...)
aesStream.XORKeyStream(firstChunk.Value, firstChunk.Value)
buffer.Append(firstChunk.Value)
firstChunk.Release()
_, err = conn.Write(requestBytes)
_, err = conn.Write(buffer.Value)
buffer.Release()
if err != nil {
log.Error("VMessOut: Failed to write VMess request: %v", err)
@@ -163,11 +156,12 @@ func handleResponse(conn net.Conn, request *protocol.VMessRequest, output chan<-
responseKey := md5.Sum(request.RequestKey[:])
responseIV := md5.Sum(request.RequestIV[:])
decryptResponseReader, err := v2io.NewAesDecryptReader(responseKey[:], responseIV[:], conn)
aesStream, err := v2crypto.NewAesDecryptionStream(responseKey[:], responseIV[:])
if err != nil {
log.Error("VMessOut: Failed to create decrypt reader: %v", err)
log.Error("VMessOut: Failed to create AES encryption stream: %v", err)
return
}
decryptResponseReader := v2crypto.NewCryptionReader(aesStream, conn)
buffer, err := v2net.ReadFrom(decryptResponseReader, nil)
if err != nil {
@@ -193,21 +187,11 @@ func handleResponse(conn net.Conn, request *protocol.VMessRequest, output chan<-
type VMessOutboundHandlerFactory struct {
}
func (factory *VMessOutboundHandlerFactory) Create(rawConfig interface{}) (proxy.OutboundConnectionHandler, error) {
func (factory *VMessOutboundHandlerFactory) Create(rawConfig interface{}) (connhandler.OutboundConnectionHandler, error) {
vOutConfig := rawConfig.(config.Outbound)
servers := make([]*config.OutboundTarget, 0, 16)
udpServers := make([]*config.OutboundTarget, 0, 16)
for _, target := range vOutConfig.Targets() {
if target.Destination.IsTCP() {
servers = append(servers, target)
}
if target.Destination.IsUDP() {
udpServers = append(udpServers, target)
}
}
return NewVMessOutboundHandler(servers, udpServers), nil
return NewVMessOutboundHandler(vOutConfig.Targets()), nil
}
func init() {
proxy.RegisterOutboundConnectionHandlerFactory("vmess", &VMessOutboundHandlerFactory{})
connhandler.RegisterOutboundConnectionHandlerFactory("vmess", &VMessOutboundHandlerFactory{})
}

View File

@@ -0,0 +1,40 @@
{
"port": 1080,
"log": {
"access": ""
},
"inbound": {
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": false,
"ip": "127.0.0.1"
}
},
"outbound": {
"protocol": "vmess",
"settings": {
"vnext": [
{
"address": "127.0.0.1",
"port": 37192,
"users": [
{"id": "27848739-7e62-4138-9fd3-098a63964b6b"}
]
}
]
}
},
"inboundDetour": [
{
"protocol": "dokodemo-door",
"port": 28394,
"settings": {
"address": "8.8.8.8",
"port": 53,
"network": "tcp",
"timeout": 0
}
}
]
}

View File

@@ -17,11 +17,11 @@
"vnext": [
{
"address": "127.0.0.1",
"port": 27183,
"port": 37192,
"users": [
{"id": "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"}
],
"network": "tcp"
{"id": "27848739-7e62-4138-9fd3-098a63964b6b"},
{"id": "3b129dec-72a3-4d28-aeee-028a0fe86e22"}
]
}
]
}

View File

@@ -1,5 +1,5 @@
{
"port": 27183,
"port": 37192,
"log" : {
"access": "access.log"
},
@@ -7,9 +7,15 @@
"protocol": "vmess",
"settings": {
"clients": [
{"id": "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"}
],
"udp": false
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"level": 0
},
{
"id": "3b129dec-72a3-4d28-aeee-028a0fe86e22",
"level": 1
}
]
}
},
"outbound": {

View File

@@ -8,10 +8,12 @@ import (
"github.com/v2ray/v2ray-core"
"github.com/v2ray/v2ray-core/app/point"
jsonconf "github.com/v2ray/v2ray-core/app/point/config/json"
"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/proxy/dokodemo"
_ "github.com/v2ray/v2ray-core/proxy/dokodemo/config/json"
_ "github.com/v2ray/v2ray-core/proxy/freedom"
_ "github.com/v2ray/v2ray-core/proxy/freedom/config/json"
_ "github.com/v2ray/v2ray-core/proxy/socks"

View File

@@ -24,7 +24,8 @@
"protocol": "socks", // 传入数据所用协议
"settings": {
"auth": "noauth", // 认证方式,暂时只支持匿名
"udp": false // 如果要使用 UDP 转发,请改成 true
"udp": false, // 如果要使用 UDP 转发,请改成 true
"ip": "127.0.0.1" // 如果 Server A 不是运行在本地,请标明 Server A 的实际 IP 地址,否则 UDP 转发将无法进行。
}
},
"outbound": {

View File

@@ -1,34 +1,8 @@
# V2Ray 开发计划
## 版本号
V2Ray 的版本号形如 X.Y其中 X 表示 MilestoneY 表示 Release如 2.3 表示第二个 Milestone 的第三个 Release。
V2Ray 的版本号形如 X.Y.Z,其中 X 表示 MilestoneY 表示 Release如 2.3 表示第二个 Milestone 的第三个 ReleaseZ 仅作为修复紧急 Bug 之后的发布使用,一般不出现
## 周期
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

@@ -27,7 +27,7 @@ for DIR in $(find * -type d -not -path "*.git*"); do
fi
done
cat coverall.out | sort -t: -k1 > coverallsorted.out
cat coverall.out | sort -t: -k1 | grep -vw "testing" > coverallsorted.out
echo "mode: set" | cat - coverallsorted.out > coverall.out
rm coverallsorted.out

View File

@@ -1,43 +0,0 @@
package mocks
import (
"bytes"
"github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/common/alloc"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy"
)
type InboundConnectionHandler struct {
Data2Send []byte
DataReturned *bytes.Buffer
Port uint16
Dispatcher app.PacketDispatcher
}
func (handler *InboundConnectionHandler) Listen(port uint16) error {
handler.Port = port
return nil
}
func (handler *InboundConnectionHandler) Communicate(packet v2net.Packet) error {
ray := handler.Dispatcher.DispatchToOutbound(packet)
input := ray.InboundInput()
output := ray.InboundOutput()
buffer := alloc.NewBuffer()
buffer.Clear()
buffer.Append(handler.Data2Send)
input <- buffer
close(input)
v2net.ChanToWriter(handler.DataReturned, output)
return nil
}
func (handler *InboundConnectionHandler) Create(dispatcher app.PacketDispatcher, config interface{}) (proxy.InboundConnectionHandler, error) {
handler.Dispatcher = dispatcher
return handler, nil
}

View File

@@ -1,48 +0,0 @@
package mocks
import (
"bytes"
"github.com/v2ray/v2ray-core/common/alloc"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy"
"github.com/v2ray/v2ray-core/transport/ray"
)
type OutboundConnectionHandler struct {
Data2Send *bytes.Buffer
Data2Return []byte
Destination v2net.Destination
}
func (handler *OutboundConnectionHandler) Dispatch(packet v2net.Packet, ray ray.OutboundRay) error {
input := ray.OutboundInput()
output := ray.OutboundOutput()
handler.Destination = packet.Destination()
if packet.Chunk() != nil {
handler.Data2Send.Write(packet.Chunk().Value)
}
go func() {
for {
data, open := <-input
if !open {
break
}
handler.Data2Send.Write(data.Value)
data.Release()
}
response := alloc.NewBuffer()
response.Clear()
response.Append(handler.Data2Return)
output <- response
close(output)
}()
return nil
}
func (handler *OutboundConnectionHandler) Create(config interface{}) (proxy.OutboundConnectionHandler, error) {
return handler, nil
}

View File

@@ -0,0 +1,132 @@
package scenarios
import (
"net"
"github.com/v2ray/v2ray-core/app/point"
"github.com/v2ray/v2ray-core/app/point/config/testing/mocks"
v2net "github.com/v2ray/v2ray-core/common/net"
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
_ "github.com/v2ray/v2ray-core/proxy/freedom"
_ "github.com/v2ray/v2ray-core/proxy/socks"
socksjson "github.com/v2ray/v2ray-core/proxy/socks/config/json"
_ "github.com/v2ray/v2ray-core/proxy/vmess"
"github.com/v2ray/v2ray-core/proxy/vmess/config"
vmessjson "github.com/v2ray/v2ray-core/proxy/vmess/config/json"
)
const (
socks5Version = byte(0x05)
)
func socks5AuthMethodRequest(methods ...byte) []byte {
request := []byte{socks5Version, byte(len(methods))}
request = append(request, methods...)
return request
}
func appendAddress(request []byte, address v2net.Address) []byte {
switch {
case address.IsIPv4():
request = append(request, byte(0x01))
request = append(request, address.IP()...)
case address.IsIPv6():
request = append(request, byte(0x04))
request = append(request, address.IP()...)
case address.IsDomain():
request = append(request, byte(0x03), byte(len(address.Domain())))
request = append(request, []byte(address.Domain())...)
}
request = append(request, address.PortBytes()...)
return request
}
func socks5Request(command byte, address v2net.Address) []byte {
request := []byte{socks5Version, command, 0}
request = appendAddress(request, address)
return request
}
func socks5UDPRequest(address v2net.Address, payload []byte) []byte {
request := make([]byte, 0, 1024)
request = append(request, 0, 0, 0)
request = appendAddress(request, address)
request = append(request, payload...)
return request
}
func setUpV2Ray() (uint16, error) {
id1, err := config.NewID("ad937d9d-6e23-4a5a-ba23-bce5092a7c51")
if err != nil {
return 0, err
}
id2, err := config.NewID("93ccfc71-b136-4015-ac85-e037bd1ead9e")
if err != nil {
return 0, err
}
users := []*vmessjson.ConfigUser{
&vmessjson.ConfigUser{Id: id1},
&vmessjson.ConfigUser{Id: id2},
}
portB := v2nettesting.PickPort()
configB := mocks.Config{
PortValue: portB,
InboundConfigValue: &mocks.ConnectionConfig{
ProtocolValue: "vmess",
SettingsValue: &vmessjson.Inbound{
AllowedClients: users,
},
},
OutboundConfigValue: &mocks.ConnectionConfig{
ProtocolValue: "freedom",
SettingsValue: nil,
},
}
pointB, err := point.NewPoint(&configB)
if err != nil {
return 0, err
}
err = pointB.Start()
if err != nil {
return 0, err
}
portA := v2nettesting.PickPort()
configA := mocks.Config{
PortValue: portA,
InboundConfigValue: &mocks.ConnectionConfig{
ProtocolValue: "socks",
SettingsValue: &socksjson.SocksConfig{
AuthMethod: "noauth",
UDPEnabled: true,
HostIP: socksjson.IPAddress(net.IPv4(127, 0, 0, 1)),
},
},
OutboundConfigValue: &mocks.ConnectionConfig{
ProtocolValue: "vmess",
SettingsValue: &vmessjson.Outbound{
[]*vmessjson.ConfigTarget{
&vmessjson.ConfigTarget{
Address: v2net.IPAddress([]byte{127, 0, 0, 1}, portB),
Users: users,
},
},
},
},
}
pointA, err := point.NewPoint(&configA)
if err != nil {
return 0, err
}
err = pointA.Start()
if err != nil {
return 0, err
}
return portA, nil
}

View File

@@ -0,0 +1,192 @@
package scenarios
import (
"net"
"testing"
v2net "github.com/v2ray/v2ray-core/common/net"
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
"github.com/v2ray/v2ray-core/testing/servers/tcp"
"github.com/v2ray/v2ray-core/testing/servers/udp"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestTCPConnection(t *testing.T) {
assert := unit.Assert(t)
targetPort := v2nettesting.PickPort()
tcpServer := &tcp.Server{
Port: targetPort,
MsgProcessor: func(data []byte) []byte {
buffer := make([]byte, 0, 2048)
buffer = append(buffer, []byte("Processed: ")...)
buffer = append(buffer, data...)
return buffer
},
}
_, err := tcpServer.Start()
assert.Error(err).IsNil()
v2rayPort, err := setUpV2Ray()
assert.Error(err).IsNil()
for i := 0; i < 100; i++ {
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{
IP: []byte{127, 0, 0, 1},
Port: int(v2rayPort),
})
authRequest := socks5AuthMethodRequest(byte(0))
nBytes, err := conn.Write(authRequest)
assert.Int(nBytes).Equals(len(authRequest))
assert.Error(err).IsNil()
authResponse := make([]byte, 1024)
nBytes, err = conn.Read(authResponse)
assert.Error(err).IsNil()
assert.Bytes(authResponse[:nBytes]).Equals([]byte{socks5Version, 0})
connectRequest := socks5Request(byte(1), v2net.IPAddress([]byte{127, 0, 0, 1}, targetPort))
nBytes, err = conn.Write(connectRequest)
assert.Int(nBytes).Equals(len(connectRequest))
assert.Error(err).IsNil()
connectResponse := make([]byte, 1024)
nBytes, err = conn.Read(connectResponse)
assert.Error(err).IsNil()
assert.Bytes(connectResponse[:nBytes]).Equals([]byte{socks5Version, 0, 0, 1, 0, 0, 0, 0, 6, 181})
actualRequest := []byte("Request to target server.")
nBytes, err = conn.Write(actualRequest)
assert.Error(err).IsNil()
assert.Int(nBytes).Equals(len(actualRequest))
actualRequest = []byte("Request to target server again.")
nBytes, err = conn.Write(actualRequest)
assert.Error(err).IsNil()
assert.Int(nBytes).Equals(len(actualRequest))
conn.CloseWrite()
actualResponse := make([]byte, 1024)
nBytes, err = conn.Read(actualResponse)
assert.Error(err).IsNil()
assert.String(string(actualResponse[:nBytes])).Equals("Processed: Request to target server.Request to target server again.")
conn.Close()
}
}
func TestTCPBind(t *testing.T) {
assert := unit.Assert(t)
targetPort := v2nettesting.PickPort()
tcpServer := &tcp.Server{
Port: targetPort,
MsgProcessor: func(data []byte) []byte {
buffer := make([]byte, 0, 2048)
buffer = append(buffer, []byte("Processed: ")...)
buffer = append(buffer, data...)
return buffer
},
}
_, err := tcpServer.Start()
assert.Error(err).IsNil()
v2rayPort, err := setUpV2Ray()
assert.Error(err).IsNil()
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{
IP: []byte{127, 0, 0, 1},
Port: int(v2rayPort),
})
authRequest := socks5AuthMethodRequest(byte(0))
nBytes, err := conn.Write(authRequest)
assert.Int(nBytes).Equals(len(authRequest))
assert.Error(err).IsNil()
authResponse := make([]byte, 1024)
nBytes, err = conn.Read(authResponse)
assert.Error(err).IsNil()
assert.Bytes(authResponse[:nBytes]).Equals([]byte{socks5Version, 0})
connectRequest := socks5Request(byte(2), v2net.IPAddress([]byte{127, 0, 0, 1}, targetPort))
nBytes, err = conn.Write(connectRequest)
assert.Int(nBytes).Equals(len(connectRequest))
assert.Error(err).IsNil()
connectResponse := make([]byte, 1024)
nBytes, err = conn.Read(connectResponse)
assert.Error(err).IsNil()
assert.Bytes(connectResponse[:nBytes]).Equals([]byte{socks5Version, 7, 0, 1, 0, 0, 0, 0, 0, 0})
conn.Close()
}
func TestUDPAssociate(t *testing.T) {
assert := unit.Assert(t)
targetPort := v2nettesting.PickPort()
udpServer := &udp.Server{
Port: targetPort,
MsgProcessor: func(data []byte) []byte {
buffer := make([]byte, 0, 2048)
buffer = append(buffer, []byte("Processed: ")...)
buffer = append(buffer, data...)
return buffer
},
}
_, err := udpServer.Start()
assert.Error(err).IsNil()
v2rayPort, err := setUpV2Ray()
assert.Error(err).IsNil()
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{
IP: []byte{127, 0, 0, 1},
Port: int(v2rayPort),
})
authRequest := socks5AuthMethodRequest(byte(0))
nBytes, err := conn.Write(authRequest)
assert.Int(nBytes).Equals(len(authRequest))
assert.Error(err).IsNil()
authResponse := make([]byte, 1024)
nBytes, err = conn.Read(authResponse)
assert.Error(err).IsNil()
assert.Bytes(authResponse[:nBytes]).Equals([]byte{socks5Version, 0})
connectRequest := socks5Request(byte(3), v2net.IPAddress([]byte{127, 0, 0, 1}, targetPort))
nBytes, err = conn.Write(connectRequest)
assert.Int(nBytes).Equals(len(connectRequest))
assert.Error(err).IsNil()
connectResponse := make([]byte, 1024)
nBytes, err = conn.Read(connectResponse)
assert.Error(err).IsNil()
assert.Bytes(connectResponse[:nBytes]).Equals([]byte{socks5Version, 0, 0, 1, 127, 0, 0, 1, byte(v2rayPort >> 8), byte(v2rayPort)})
udpConn, err := net.DialUDP("udp", nil, &net.UDPAddr{
IP: []byte{127, 0, 0, 1},
Port: int(v2rayPort),
})
assert.Error(err).IsNil()
udpPayload := "UDP request to udp server."
udpRequest := socks5UDPRequest(v2net.IPAddress([]byte{127, 0, 0, 1}, targetPort), []byte(udpPayload))
nBytes, err = udpConn.Write(udpRequest)
assert.Int(nBytes).Equals(len(udpRequest))
assert.Error(err).IsNil()
udpResponse := make([]byte, 1024)
nBytes, err = udpConn.Read(udpResponse)
assert.Error(err).IsNil()
assert.Bytes(udpResponse[:nBytes]).Equals(
socks5UDPRequest(v2net.IPAddress([]byte{127, 0, 0, 1}, targetPort), []byte("Processed: UDP request to udp server.")))
udpConn.Close()
conn.Close()
}

View File

@@ -11,16 +11,17 @@ import (
)
var (
targetOS = flag.String("os", runtime.GOOS, "Target OS of this build.")
targetArch = flag.String("arch", runtime.GOARCH, "Target CPU arch of this build.")
archive = flag.Bool("zip", false, "Whether to make an archive of files or not.")
flagTargetOS = flag.String("os", runtime.GOOS, "Target OS of this build.")
flagTargetArch = flag.String("arch", runtime.GOARCH, "Target CPU arch of this build.")
flagArchive = flag.Bool("zip", false, "Whether to make an archive of files or not.")
binPath string
)
func createTargetDirectory(version string, goOS GoOS, goArch GoArch) (string, error) {
suffix := getSuffix(goOS, goArch)
GOPATH := os.Getenv("GOPATH")
targetDir := filepath.Join(GOPATH, "bin", "v2ray-"+version+suffix)
targetDir := filepath.Join(binPath, "v2ray-"+version+suffix)
if version != "custom" {
os.RemoveAll(targetDir)
}
@@ -36,19 +37,31 @@ func getTargetFile(goOS GoOS) string {
return "v2ray" + suffix
}
func getBinPath() string {
GOPATH := os.Getenv("GOPATH")
return filepath.Join(GOPATH, "bin")
}
func main() {
flag.Parse()
binPath = getBinPath()
build(*flagTargetOS, *flagTargetArch, *flagArchive, "")
}
v2rayOS := parseOS(*targetOS)
v2rayArch := parseArch(*targetArch)
func build(targetOS, targetArch string, archive bool, version string) {
v2rayOS := parseOS(targetOS)
v2rayArch := parseArch(targetArch)
version, err := git.RepoVersionHead()
if version == git.VersionUndefined {
version = "custom"
}
if err != nil {
fmt.Println("Unable to detect V2Ray version: " + err.Error())
return
if len(version) == 0 {
v, err := git.RepoVersionHead()
if v == git.VersionUndefined {
v = "custom"
}
if err != nil {
fmt.Println("Unable to detect V2Ray version: " + err.Error())
return
}
version = v
}
fmt.Printf("Building V2Ray (%s) for %s %s\n", version, v2rayOS, v2rayArch)
@@ -68,9 +81,7 @@ func main() {
fmt.Println("Unable to copy config files: " + err.Error())
}
if *archive {
GOPATH := os.Getenv("GOPATH")
binPath := filepath.Join(GOPATH, "bin")
if archive {
err := os.Chdir(binPath)
if err != nil {
fmt.Printf("Unable to switch to directory (%s): %v\n", binPath, err)

59
tools/build/build_test.go Normal file
View File

@@ -0,0 +1,59 @@
package main
import (
"fmt"
"os"
"path/filepath"
"testing"
"github.com/v2ray/v2ray-core/testing/unit"
)
func cleanBinPath() {
os.RemoveAll(binPath)
os.Mkdir(binPath, os.ModeDir|0777)
}
func fileExists(file string) bool {
_, err := os.Stat(file)
return err == nil
}
func allFilesExists(files ...string) bool {
for _, file := range files {
fullPath := filepath.Join(binPath, file)
if !fileExists(fullPath) {
fmt.Println(fullPath + " doesn't exist.")
return false
}
}
return true
}
func TestBuildMacOS(t *testing.T) {
assert := unit.Assert(t)
binPath = filepath.Join(os.Getenv("GOPATH"), "testing")
cleanBinPath()
build("macos", "amd64", true, "test")
assert.Bool(allFilesExists(
"v2ray-macos.zip",
"v2ray-test-macos",
filepath.Join("v2ray-test-macos", "config.json"),
filepath.Join("v2ray-test-macos", "v2ray"))).IsTrue()
build("windows", "amd64", true, "test")
assert.Bool(allFilesExists(
"v2ray-windows-64.zip",
"v2ray-test-windows-64",
filepath.Join("v2ray-test-windows-64", "config.json"),
filepath.Join("v2ray-test-windows-64", "v2ray.exe"))).IsTrue()
build("linux", "amd64", true, "test")
assert.Bool(allFilesExists(
"v2ray-linux-64.zip",
"v2ray-test-linux-64",
filepath.Join("v2ray-test-linux-64", "vpoint_socks_vmess.json"),
filepath.Join("v2ray-test-linux-64", "vpoint_vmess_freedom.json"),
filepath.Join("v2ray-test-linux-64", "v2ray"))).IsTrue()
}

View File

@@ -12,10 +12,6 @@ func TestRevParse(t *testing.T) {
rev, err := RevParse("HEAD")
assert.Error(err).IsNil()
assert.Int(len(rev)).GreaterThan(0)
rev, err = RevParse("v0.8")
assert.Error(err).IsNil()
assert.String(rev).Equals("de7a1d30c3e6bda6a1297b5815369fcfa0e74f0e")
}
func TestRepoVersion(t *testing.T) {
@@ -24,8 +20,4 @@ func TestRepoVersion(t *testing.T) {
version, err := RepoVersionHead()
assert.Error(err).IsNil()
assert.Int(len(version)).GreaterThan(0)
version, err = RepoVersion("de7a1d30c3e6bda6a1297b5815369fcfa0e74f0e")
assert.Error(err).IsNil()
assert.String(version).Equals("v0.8")
}