You've already forked v2ray-core
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cd78bee07 | ||
|
|
288f469c36 | ||
|
|
a5888eea73 | ||
|
|
0ae69720fa | ||
|
|
a5af51c42c | ||
|
|
9803bfc3a9 | ||
|
|
b6a0d0aba9 | ||
|
|
0703c4469b | ||
|
|
58b88a70ab | ||
|
|
d221147eb4 | ||
|
|
74cba8b177 | ||
|
|
0de60adf3c | ||
|
|
acdada81ed | ||
|
|
dcb84b19bd | ||
|
|
af8c8f16e5 | ||
|
|
a81327c5b9 | ||
|
|
5c7eb63bea | ||
|
|
0ebc60febe | ||
|
|
771d0225c7 | ||
|
|
f10f08c87d | ||
|
|
0acfe57b0c | ||
|
|
c592d9c246 | ||
|
|
9071b3ea10 | ||
|
|
a84c03d5db | ||
|
|
ef6a200fe8 | ||
|
|
51773ea27d | ||
|
|
a03eaa0316 | ||
|
|
e79f4450b4 | ||
|
|
4bc91b0215 | ||
|
|
890d185979 | ||
|
|
1b80a1a85a | ||
|
|
2a00e2c6e9 | ||
|
|
8408b7735c | ||
|
|
3c946daf88 | ||
|
|
7cd2f32d7e | ||
|
|
99671a173f | ||
|
|
3c259b7069 | ||
|
|
4ac3eab385 | ||
|
|
8c5865d4e7 | ||
|
|
7cc8d90f26 | ||
|
|
b9b1e3fe61 | ||
|
|
3bd4238e88 | ||
|
|
dc7d4a2309 | ||
|
|
efb881d73c | ||
|
|
1675063dc6 | ||
|
|
9cf5c3edfb | ||
|
|
1d40220d31 | ||
|
|
93625dd656 | ||
|
|
06dac18f81 | ||
|
|
b2cd6e739e |
@@ -27,7 +27,8 @@ deploy:
|
||||
- "$GOPATH/bin/v2ray-windows-32.zip"
|
||||
- "$GOPATH/bin/v2ray-linux-64.zip"
|
||||
- "$GOPATH/bin/v2ray-linux-32.zip"
|
||||
- "$GOPATH/bin/v2ray-armv6.zip"
|
||||
- "$GOPATH/bin/v2ray-linux-arm.zip"
|
||||
- "$GOPATH/bin/v2ray-linux-arm64.zip"
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
[中文](https://github.com/V2Ray/v2ray-core/blob/master/README.md) | [English](https://github.com/V2Ray/v2ray-core/blob/master/spec/en/README.md)
|
||||
|
||||
V2Ray 是一个模块化的代理软件包,它的目标是提供常用的代理软件模块,减化网络代理软件的开发。
|
||||
V2Ray 是一个模块化的代理软件包,它的目标是提供常用的代理软件模块,简化网络代理软件的开发。
|
||||
|
||||
## 联系方式
|
||||
* 聊天室:https://gitter.im/v2ray/v2ray-core
|
||||
|
||||
11
app/packet_dispatcher.go
Normal file
11
app/packet_dispatcher.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/transport/ray"
|
||||
)
|
||||
|
||||
// PacketDispatcher dispatch a packet and possibly further network payload to its destination.
|
||||
type PacketDispatcher interface {
|
||||
DispatchToOutbound(packet v2net.Packet) ray.InboundRay
|
||||
}
|
||||
76
app/point/point.go
Normal file
76
app/point/point.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package point
|
||||
|
||||
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/config"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"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
|
||||
}
|
||||
|
||||
// NewPoint returns a new Point server based on given configuration.
|
||||
// The server is not started at this point.
|
||||
func NewPoint(pConfig config.PointConfig) (*Point, error) {
|
||||
var vpoint = new(Point)
|
||||
vpoint.port = pConfig.Port()
|
||||
|
||||
ichFactory := proxy.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)
|
||||
ich, err := ichFactory.Create(vpoint, ichConfig)
|
||||
if err != nil {
|
||||
log.Error("Failed to create inbound connection handler: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
vpoint.ich = ich
|
||||
|
||||
ochFactory := proxy.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)
|
||||
och, err := ochFactory.Create(ochConfig)
|
||||
if err != nil {
|
||||
log.Error("Failed to create outbound connection handler: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
vpoint.och = och
|
||||
|
||||
return vpoint, nil
|
||||
}
|
||||
|
||||
// Start starts the Point server, and return any error during the process.
|
||||
// In the case of any errors, the state of the server is unpredicatable.
|
||||
func (vp *Point) Start() error {
|
||||
if vp.port <= 0 {
|
||||
log.Error("Invalid port %d", vp.port)
|
||||
return config.BadConfiguration
|
||||
}
|
||||
|
||||
return 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
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Point) DispatchToOutbound(packet v2net.Packet) ray.InboundRay {
|
||||
direct := ray.NewRay()
|
||||
go p.och.Dispatch(packet, direct)
|
||||
return direct
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func HasCode(err error, code int) bool {
|
||||
if errWithCode, ok := err.(ErrorWithCode); ok {
|
||||
return errWithCode.Code() == code
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ErrorWithCode interface {
|
||||
error
|
||||
Code() int
|
||||
}
|
||||
|
||||
type ErrorCode int
|
||||
|
||||
func (code ErrorCode) Code() int {
|
||||
return int(code)
|
||||
}
|
||||
|
||||
func (code ErrorCode) Prefix() string {
|
||||
return fmt.Sprintf("[Error 0x%04X] ", code.Code())
|
||||
}
|
||||
|
||||
type AuthenticationError struct {
|
||||
ErrorCode
|
||||
AuthDetail interface{}
|
||||
}
|
||||
|
||||
func NewAuthenticationError(detail interface{}) AuthenticationError {
|
||||
return AuthenticationError{
|
||||
ErrorCode: 1,
|
||||
AuthDetail: detail,
|
||||
}
|
||||
}
|
||||
|
||||
func (err AuthenticationError) Error() string {
|
||||
return fmt.Sprintf("%sInvalid auth %v", err.Prefix(), err.AuthDetail)
|
||||
}
|
||||
|
||||
type ProtocolVersionError struct {
|
||||
ErrorCode
|
||||
Version int
|
||||
}
|
||||
|
||||
func NewProtocolVersionError(version int) ProtocolVersionError {
|
||||
return ProtocolVersionError{
|
||||
ErrorCode: 2,
|
||||
Version: version,
|
||||
}
|
||||
}
|
||||
|
||||
func (err ProtocolVersionError) Error() string {
|
||||
return fmt.Sprintf("%sInvalid version %d", err.Prefix(), err.Version)
|
||||
}
|
||||
|
||||
type CorruptedPacketError struct {
|
||||
ErrorCode
|
||||
}
|
||||
|
||||
var corruptedPacketErrorInstance = CorruptedPacketError{ErrorCode: 3}
|
||||
|
||||
func NewCorruptedPacketError() CorruptedPacketError {
|
||||
return corruptedPacketErrorInstance
|
||||
}
|
||||
|
||||
func (err CorruptedPacketError) Error() string {
|
||||
return err.Prefix() + "Corrupted packet."
|
||||
}
|
||||
|
||||
type IPFormatError struct {
|
||||
ErrorCode
|
||||
IP []byte
|
||||
}
|
||||
|
||||
func NewIPFormatError(ip []byte) IPFormatError {
|
||||
return IPFormatError{
|
||||
ErrorCode: 4,
|
||||
IP: ip,
|
||||
}
|
||||
}
|
||||
|
||||
func (err IPFormatError) Error() string {
|
||||
return fmt.Sprintf("%sInvalid IP %v", err.Prefix(), err.IP)
|
||||
}
|
||||
|
||||
type ConfigurationError struct {
|
||||
ErrorCode
|
||||
}
|
||||
|
||||
var configurationErrorInstance = ConfigurationError{ErrorCode: 5}
|
||||
|
||||
func NewConfigurationError() ConfigurationError {
|
||||
return configurationErrorInstance
|
||||
}
|
||||
|
||||
func (r ConfigurationError) Error() string {
|
||||
return r.Prefix() + "Invalid configuration."
|
||||
}
|
||||
|
||||
type InvalidOperationError struct {
|
||||
ErrorCode
|
||||
Operation string
|
||||
}
|
||||
|
||||
func NewInvalidOperationError(operation string) InvalidOperationError {
|
||||
return InvalidOperationError{
|
||||
ErrorCode: 6,
|
||||
Operation: operation,
|
||||
}
|
||||
}
|
||||
|
||||
func (r InvalidOperationError) Error() string {
|
||||
return r.Prefix() + "Invalid operation: " + r.Operation
|
||||
}
|
||||
|
||||
type BadConfigurationError struct {
|
||||
ErrorCode
|
||||
}
|
||||
|
||||
var badConfigurationErrorInstance = BadConfigurationError{ErrorCode: 6}
|
||||
|
||||
func NewBadConfigurationError() BadConfigurationError {
|
||||
return badConfigurationErrorInstance
|
||||
}
|
||||
|
||||
func (r BadConfigurationError) Error() string {
|
||||
return r.Prefix() + "Bad configuration."
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/errors"
|
||||
"github.com/v2ray/v2ray-core/testing/unit"
|
||||
)
|
||||
|
||||
type MockError struct {
|
||||
errors.ErrorCode
|
||||
}
|
||||
|
||||
func (err MockError) Error() string {
|
||||
return "This is a fake error."
|
||||
}
|
||||
|
||||
func TestHasCode(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
|
||||
err := MockError{ErrorCode: 101}
|
||||
assert.Error(err).HasCode(101)
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package log
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/platform"
|
||||
)
|
||||
|
||||
// AccessStatus is the status of an access request from clients.
|
||||
@@ -63,7 +65,7 @@ func (logger *fileAccessLogger) Run() {
|
||||
func newFileAccessLogger(path string) accessLogger {
|
||||
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
log.Printf("Unable to create or open file (%s): %v\n", path, err)
|
||||
log.Printf("Unable to create or open file (%s): %v%s", path, err, platform.LineSeparator())
|
||||
return nil
|
||||
}
|
||||
return &fileAccessLogger{
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/platform"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -13,41 +15,83 @@ const (
|
||||
ErrorLevel = LogLevel(3)
|
||||
)
|
||||
|
||||
var logLevel = WarningLevel
|
||||
|
||||
type LogLevel int
|
||||
|
||||
func SetLogLevel(level LogLevel) {
|
||||
logLevel = level
|
||||
type logger interface {
|
||||
WriteLog(prefix, format string, v ...interface{})
|
||||
}
|
||||
|
||||
func writeLog(level LogLevel, prefix, format string, v ...interface{}) string {
|
||||
if level < logLevel {
|
||||
return ""
|
||||
}
|
||||
type noOpLogger struct {
|
||||
}
|
||||
|
||||
func (l *noOpLogger) WriteLog(prefix, format string, v ...interface{}) {
|
||||
// Swallow
|
||||
}
|
||||
|
||||
type streamLogger struct {
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
func (l *streamLogger) WriteLog(prefix, format string, v ...interface{}) {
|
||||
var data string
|
||||
if v == nil || len(v) == 0 {
|
||||
data = format
|
||||
} else {
|
||||
data = fmt.Sprintf(format, v...)
|
||||
}
|
||||
log.Println(prefix + data)
|
||||
return data
|
||||
l.writer.Write([]byte(prefix + data + platform.LineSeparator()))
|
||||
}
|
||||
|
||||
var (
|
||||
noOpLoggerInstance logger = &noOpLogger{}
|
||||
streamLoggerInstance logger = &streamLogger{
|
||||
writer: os.Stdout,
|
||||
}
|
||||
|
||||
debugLogger = noOpLoggerInstance
|
||||
infoLogger = noOpLoggerInstance
|
||||
warningLogger = noOpLoggerInstance
|
||||
errorLogger = noOpLoggerInstance
|
||||
)
|
||||
|
||||
type LogLevel int
|
||||
|
||||
func SetLogLevel(level LogLevel) {
|
||||
debugLogger = noOpLoggerInstance
|
||||
if level <= DebugLevel {
|
||||
debugLogger = streamLoggerInstance
|
||||
}
|
||||
|
||||
infoLogger = noOpLoggerInstance
|
||||
if level <= InfoLevel {
|
||||
infoLogger = streamLoggerInstance
|
||||
}
|
||||
|
||||
warningLogger = noOpLoggerInstance
|
||||
if level <= WarningLevel {
|
||||
warningLogger = streamLoggerInstance
|
||||
}
|
||||
|
||||
errorLogger = noOpLoggerInstance
|
||||
if level <= ErrorLevel {
|
||||
errorLogger = streamLoggerInstance
|
||||
}
|
||||
}
|
||||
|
||||
// Debug outputs a debug log with given format and optional arguments.
|
||||
func Debug(format string, v ...interface{}) {
|
||||
writeLog(DebugLevel, "[Debug]", format, v...)
|
||||
debugLogger.WriteLog("[Debug]", format, v...)
|
||||
}
|
||||
|
||||
// Info outputs an info log with given format and optional arguments.
|
||||
func Info(format string, v ...interface{}) {
|
||||
writeLog(InfoLevel, "[Info]", format, v...)
|
||||
infoLogger.WriteLog("[Info]", format, v...)
|
||||
}
|
||||
|
||||
// Warning outputs a warning log with given format and optional arguments.
|
||||
func Warning(format string, v ...interface{}) {
|
||||
writeLog(WarningLevel, "[Warning]", format, v...)
|
||||
warningLogger.WriteLog("[Warning]", format, v...)
|
||||
}
|
||||
|
||||
func Error(format string, v ...interface{}) error {
|
||||
data := writeLog(ErrorLevel, "[Error]", format, v...)
|
||||
return errors.New(data)
|
||||
// Error outputs an error log with given format and optional arguments.
|
||||
func Error(format string, v ...interface{}) {
|
||||
errorLogger.WriteLog("[Error]", format, v...)
|
||||
}
|
||||
|
||||
19
common/log/log_test.go
Normal file
19
common/log/log_test.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core/testing/unit"
|
||||
)
|
||||
|
||||
func TestLogLevelSetting(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
|
||||
assert.Pointer(debugLogger).Equals(noOpLoggerInstance)
|
||||
SetLogLevel(DebugLevel)
|
||||
assert.Pointer(debugLogger).Equals(streamLoggerInstance)
|
||||
|
||||
SetLogLevel(InfoLevel)
|
||||
assert.Pointer(debugLogger).Equals(noOpLoggerInstance)
|
||||
assert.Pointer(infoLogger).Equals(streamLoggerInstance)
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/errors"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
)
|
||||
|
||||
@@ -23,6 +22,15 @@ type Address interface {
|
||||
String() string // String representation of this Address
|
||||
}
|
||||
|
||||
func allZeros(data []byte) bool {
|
||||
for _, v := range data {
|
||||
if v != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IPAddress creates an Address with given IP and port.
|
||||
func IPAddress(ip []byte, port uint16) Address {
|
||||
switch len(ip) {
|
||||
@@ -32,6 +40,9 @@ func IPAddress(ip []byte, port uint16) Address {
|
||||
ip: [4]byte{ip[0], ip[1], ip[2], ip[3]},
|
||||
}
|
||||
case net.IPv6len:
|
||||
if allZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff {
|
||||
return IPAddress(ip[12:16], port)
|
||||
}
|
||||
return IPv6Address{
|
||||
PortAddress: PortAddress{port: port},
|
||||
ip: [16]byte{
|
||||
@@ -42,7 +53,7 @@ func IPAddress(ip []byte, port uint16) Address {
|
||||
},
|
||||
}
|
||||
default:
|
||||
log.Error(errors.NewIPFormatError(ip).Error())
|
||||
log.Error("Invalid IP format: %v", ip)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core/testing/unit"
|
||||
@@ -55,3 +56,13 @@ func TestDomainAddress(t *testing.T) {
|
||||
assert.Uint16(addr.Port()).Equals(port)
|
||||
assert.String(addr.String()).Equals("v2ray.com:443")
|
||||
}
|
||||
|
||||
func TestNetIPv4Address(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
|
||||
ip := net.IPv4(1, 2, 3, 4)
|
||||
port := uint16(80)
|
||||
addr := IPAddress(ip, port)
|
||||
assert.Bool(addr.IsIPv4()).IsTrue()
|
||||
assert.String(addr.String()).Equals("1.2.3.4:80")
|
||||
}
|
||||
|
||||
@@ -6,11 +6,10 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
type otherPlatformEnvironment struct {
|
||||
}
|
||||
|
||||
var environmentInstance = &otherPlatformEnvironment{}
|
||||
|
||||
func (e *otherPlatformEnvironment) ExpandEnv(s string) string {
|
||||
func ExpandEnv(s string) string {
|
||||
return os.ExpandEnv(s)
|
||||
}
|
||||
|
||||
func LineSeparator() string {
|
||||
return "\n"
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
// Package platform provides platform specific functionalities.
|
||||
package platform
|
||||
|
||||
type environment interface {
|
||||
ExpandEnv(s string) string
|
||||
}
|
||||
|
||||
func ExpandEnv(s string) string {
|
||||
return environmentInstance.ExpandEnv(s)
|
||||
}
|
||||
@@ -2,16 +2,11 @@
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
type windowsEnvironment struct {
|
||||
}
|
||||
|
||||
var environmentInstance = &windowsEnvironment{}
|
||||
|
||||
func (e *windowsEnvironment) ExpandEnv(s string) string {
|
||||
func ExpandEnv(s string) string {
|
||||
// TODO
|
||||
return s
|
||||
}
|
||||
|
||||
func LineSeparator() string {
|
||||
return "\r\n"
|
||||
}
|
||||
|
||||
45
common/retry/retry.go
Normal file
45
common/retry/retry.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package retry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
RetryFailed = errors.New("All retry attempts failed.")
|
||||
)
|
||||
|
||||
type RetryStrategy interface {
|
||||
On(func() error) error
|
||||
}
|
||||
|
||||
type retryer struct {
|
||||
NextDelay func(int) int
|
||||
}
|
||||
|
||||
func (r *retryer) On(method func() error) error {
|
||||
attempt := 0
|
||||
for {
|
||||
err := method()
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
delay := r.NextDelay(attempt)
|
||||
if delay < 0 {
|
||||
return RetryFailed
|
||||
}
|
||||
<-time.After(time.Duration(delay) * time.Millisecond)
|
||||
attempt++
|
||||
}
|
||||
}
|
||||
|
||||
func Timed(attempts int, delay int) RetryStrategy {
|
||||
return &retryer{
|
||||
NextDelay: func(attempt int) int {
|
||||
if attempt >= attempts {
|
||||
return -1
|
||||
}
|
||||
return delay
|
||||
},
|
||||
}
|
||||
}
|
||||
9
config/errors.go
Normal file
9
config/errors.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
BadConfiguration = errors.New("Bad configuration.")
|
||||
)
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/v2ray/v2ray-core/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"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/vmess/config/json"
|
||||
|
||||
"github.com/v2ray/v2ray-core/testing/unit"
|
||||
)
|
||||
|
||||
26
core.go
26
core.go
@@ -1,20 +1,20 @@
|
||||
// Package core provides common definitions and functionalities of V2Ray.
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/platform"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "0.8"
|
||||
build = "Custom"
|
||||
version = "0.9"
|
||||
build = "Custom"
|
||||
codename = "Post Apocalypse"
|
||||
intro = "A stable and unbreakable connection for everyone."
|
||||
)
|
||||
|
||||
const (
|
||||
Codename = "Post Apocalypse"
|
||||
Intro = "A stable and unbreakable connection for everyone."
|
||||
)
|
||||
|
||||
func Version() string {
|
||||
return version
|
||||
}
|
||||
|
||||
func Build() string {
|
||||
return build
|
||||
func PrintVersion() {
|
||||
fmt.Printf("V2Ray %s (%s) %s%s", version, codename, build, platform.LineSeparator())
|
||||
fmt.Printf("%s%s", intro, platform.LineSeparator())
|
||||
}
|
||||
|
||||
102
point.go
102
point.go
@@ -1,102 +0,0 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/errors"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/config"
|
||||
)
|
||||
|
||||
var (
|
||||
inboundFactories = make(map[string]InboundConnectionHandlerFactory)
|
||||
outboundFactories = make(map[string]OutboundConnectionHandlerFactory)
|
||||
)
|
||||
|
||||
func RegisterInboundConnectionHandlerFactory(name string, factory InboundConnectionHandlerFactory) error {
|
||||
// TODO check name
|
||||
inboundFactories[name] = factory
|
||||
return nil
|
||||
}
|
||||
|
||||
func RegisterOutboundConnectionHandlerFactory(name string, factory OutboundConnectionHandlerFactory) error {
|
||||
// TODO check name
|
||||
outboundFactories[name] = factory
|
||||
return nil
|
||||
}
|
||||
|
||||
// Point is an single server in V2Ray system.
|
||||
type Point struct {
|
||||
port uint16
|
||||
ich InboundConnectionHandler
|
||||
och OutboundConnectionHandler
|
||||
}
|
||||
|
||||
// NewPoint returns a new Point server based on given configuration.
|
||||
// The server is not started at this point.
|
||||
func NewPoint(pConfig config.PointConfig) (*Point, error) {
|
||||
var vpoint = new(Point)
|
||||
vpoint.port = pConfig.Port()
|
||||
|
||||
ichFactory, ok := inboundFactories[pConfig.InboundConfig().Protocol()]
|
||||
if !ok {
|
||||
log.Error("Unknown inbound connection handler factory %s", pConfig.InboundConfig().Protocol())
|
||||
return nil, errors.NewBadConfigurationError()
|
||||
}
|
||||
ichConfig := pConfig.InboundConfig().Settings(config.TypeInbound)
|
||||
ich, err := ichFactory.Create(vpoint, ichConfig)
|
||||
if err != nil {
|
||||
log.Error("Failed to create inbound connection handler: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
vpoint.ich = ich
|
||||
|
||||
ochFactory, ok := outboundFactories[pConfig.OutboundConfig().Protocol()]
|
||||
if !ok {
|
||||
log.Error("Unknown outbound connection handler factory %s", pConfig.OutboundConfig().Protocol())
|
||||
return nil, errors.NewBadConfigurationError()
|
||||
}
|
||||
ochConfig := pConfig.OutboundConfig().Settings(config.TypeOutbound)
|
||||
och, err := ochFactory.Create(vpoint, ochConfig)
|
||||
if err != nil {
|
||||
log.Error("Failed to create outbound connection handler: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
vpoint.och = och
|
||||
|
||||
return vpoint, nil
|
||||
}
|
||||
|
||||
type InboundConnectionHandlerFactory interface {
|
||||
Create(vp *Point, config interface{}) (InboundConnectionHandler, error)
|
||||
}
|
||||
|
||||
type InboundConnectionHandler interface {
|
||||
Listen(port uint16) error
|
||||
}
|
||||
|
||||
type OutboundConnectionHandlerFactory interface {
|
||||
Create(VP *Point, config interface{}) (OutboundConnectionHandler, error)
|
||||
}
|
||||
|
||||
type OutboundConnectionHandler interface {
|
||||
Dispatch(firstPacket v2net.Packet, ray OutboundRay) error
|
||||
}
|
||||
|
||||
// Start starts the Point server, and return any error during the process.
|
||||
// In the case of any errors, the state of the server is unpredicatable.
|
||||
func (vp *Point) Start() error {
|
||||
if vp.port <= 0 {
|
||||
log.Error("Invalid port %d", vp.port)
|
||||
return errors.NewBadConfigurationError()
|
||||
}
|
||||
|
||||
err := vp.ich.Listen(vp.port)
|
||||
// TODO: handle error
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Point) DispatchToOutbound(packet v2net.Packet) InboundRay {
|
||||
ray := NewRay()
|
||||
go p.och.Dispatch(packet, ray)
|
||||
return ray
|
||||
}
|
||||
10
proxy/errors.go
Normal file
10
proxy/errors.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
InvalidAuthentication = errors.New("Invalid authentication.")
|
||||
InvalidProtocolVersion = errors.New("Invalid protocol version.")
|
||||
)
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"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/ray"
|
||||
)
|
||||
|
||||
type FreedomConnection struct {
|
||||
@@ -17,12 +17,13 @@ func NewFreedomConnection() *FreedomConnection {
|
||||
return &FreedomConnection{}
|
||||
}
|
||||
|
||||
func (vconn *FreedomConnection) Dispatch(firstPacket v2net.Packet, ray core.OutboundRay) error {
|
||||
func (vconn *FreedomConnection) Dispatch(firstPacket v2net.Packet, ray ray.OutboundRay) error {
|
||||
conn, err := net.Dial(firstPacket.Destination().Network(), firstPacket.Destination().Address().String())
|
||||
log.Info("Freedom: Opening connection to %s", firstPacket.Destination().String())
|
||||
if err != nil {
|
||||
close(ray.OutboundOutput())
|
||||
return log.Error("Freedom: Failed to open connection: %s : %v", firstPacket.Destination().String(), err)
|
||||
log.Error("Freedom: Failed to open connection: %s : %v", firstPacket.Destination().String(), err)
|
||||
return err
|
||||
}
|
||||
|
||||
input := ray.OutboundInput()
|
||||
|
||||
@@ -8,9 +8,10 @@ import (
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/app/point"
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
v2proxy "github.com/v2ray/v2ray-core/proxy"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/socks"
|
||||
"github.com/v2ray/v2ray-core/proxy/socks/config/json"
|
||||
"github.com/v2ray/v2ray-core/testing/mocks"
|
||||
@@ -42,7 +43,7 @@ func TestUDPSend(t *testing.T) {
|
||||
DataReturned: bytes.NewBuffer(make([]byte, 0, 1024)),
|
||||
}
|
||||
|
||||
core.RegisterInboundConnectionHandlerFactory("mock_ich", ich)
|
||||
v2proxy.RegisterInboundConnectionHandlerFactory("mock_ich", ich)
|
||||
|
||||
pointPort := uint16(38724)
|
||||
config := mocks.Config{
|
||||
@@ -57,7 +58,7 @@ func TestUDPSend(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
point, err := core.NewPoint(&config)
|
||||
point, err := point.NewPoint(&config)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = point.Start()
|
||||
@@ -104,7 +105,7 @@ func TestSocksTcpConnect(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
point, err := core.NewPoint(&config)
|
||||
point, err := point.NewPoint(&config)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = point.Start()
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package freedom
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
)
|
||||
|
||||
type FreedomFactory struct {
|
||||
}
|
||||
|
||||
func (factory FreedomFactory) Create(vp *core.Point, config interface{}) (core.OutboundConnectionHandler, error) {
|
||||
func (factory FreedomFactory) Create(config interface{}) (proxy.OutboundConnectionHandler, error) {
|
||||
return NewFreedomConnection(), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
core.RegisterOutboundConnectionHandlerFactory("freedom", FreedomFactory{})
|
||||
proxy.RegisterOutboundConnectionHandlerFactory("freedom", FreedomFactory{})
|
||||
}
|
||||
|
||||
18
proxy/inbound_connection.go
Normal file
18
proxy/inbound_connection.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
)
|
||||
|
||||
// A InboundConnectionHandlerFactory creates InboundConnectionHandler on demand.
|
||||
type InboundConnectionHandlerFactory interface {
|
||||
// Create creates a new InboundConnectionHandler with given configuration.
|
||||
Create(dispatch app.PacketDispatcher, config interface{}) (InboundConnectionHandler, error)
|
||||
}
|
||||
|
||||
// A InboundConnectionHandler handles inbound network connections to V2Ray.
|
||||
type InboundConnectionHandler interface {
|
||||
// Listen starts a InboundConnectionHandler by listen on a specific port. This method is called
|
||||
// exactly once during runtime.
|
||||
Listen(port uint16) error
|
||||
}
|
||||
18
proxy/outbound_connection.go
Normal file
18
proxy/outbound_connection.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/transport/ray"
|
||||
)
|
||||
|
||||
// An OutboundConnectionHandlerFactory creates OutboundConnectionHandler on demand.
|
||||
type OutboundConnectionHandlerFactory interface {
|
||||
// Create creates a new OutboundConnectionHandler with given config.
|
||||
Create(config interface{}) (OutboundConnectionHandler, error)
|
||||
}
|
||||
|
||||
// An OutboundConnectionHandler handles outbound network connection for V2Ray.
|
||||
type OutboundConnectionHandler interface {
|
||||
// Dispatch sends one or more Packets to its destination.
|
||||
Dispatch(firstPacket v2net.Packet, ray ray.OutboundRay) error
|
||||
}
|
||||
34
proxy/proxy_cache.go
Normal file
34
proxy/proxy_cache.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package proxy
|
||||
|
||||
var (
|
||||
inboundFactories = make(map[string]InboundConnectionHandlerFactory)
|
||||
outboundFactories = make(map[string]OutboundConnectionHandlerFactory)
|
||||
)
|
||||
|
||||
func RegisterInboundConnectionHandlerFactory(name string, factory InboundConnectionHandlerFactory) error {
|
||||
// TODO check name
|
||||
inboundFactories[name] = factory
|
||||
return nil
|
||||
}
|
||||
|
||||
func RegisterOutboundConnectionHandlerFactory(name string, factory OutboundConnectionHandlerFactory) error {
|
||||
// TODO check name
|
||||
outboundFactories[name] = factory
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetInboundConnectionHandlerFactory(name string) InboundConnectionHandlerFactory {
|
||||
factory, found := inboundFactories[name]
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
return factory
|
||||
}
|
||||
|
||||
func GetOutboundConnectionHandlerFactory(name string) OutboundConnectionHandlerFactory {
|
||||
factory, found := outboundFactories[name]
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
return factory
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/v2ray/v2ray-core/config"
|
||||
"github.com/v2ray/v2ray-core/config/json"
|
||||
)
|
||||
@@ -19,32 +21,45 @@ type SocksConfig struct {
|
||||
AuthMethod string `json:"auth"`
|
||||
Accounts []SocksAccount `json:"accounts"`
|
||||
UDPEnabled bool `json:"udp"`
|
||||
HostIP string `json:"ip"`
|
||||
|
||||
accountMap map[string]string
|
||||
ip net.IP
|
||||
}
|
||||
|
||||
func (config *SocksConfig) Initialize() {
|
||||
config.accountMap = make(map[string]string)
|
||||
for _, account := range config.Accounts {
|
||||
config.accountMap[account.Username] = account.Password
|
||||
func (sc *SocksConfig) Initialize() {
|
||||
sc.accountMap = make(map[string]string)
|
||||
for _, account := range sc.Accounts {
|
||||
sc.accountMap[account.Username] = account.Password
|
||||
}
|
||||
|
||||
if len(sc.HostIP) > 0 {
|
||||
sc.ip = net.ParseIP(sc.HostIP)
|
||||
if sc.ip == nil {
|
||||
sc.ip = net.IPv4(127, 0, 0, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (config *SocksConfig) IsNoAuth() bool {
|
||||
return config.AuthMethod == AuthMethodNoAuth
|
||||
func (sc *SocksConfig) IsNoAuth() bool {
|
||||
return sc.AuthMethod == AuthMethodNoAuth
|
||||
}
|
||||
|
||||
func (config *SocksConfig) IsPassword() bool {
|
||||
return config.AuthMethod == AuthMethodUserPass
|
||||
func (sc *SocksConfig) IsPassword() bool {
|
||||
return sc.AuthMethod == AuthMethodUserPass
|
||||
}
|
||||
|
||||
func (config *SocksConfig) HasAccount(user, pass string) bool {
|
||||
if actualPass, found := config.accountMap[user]; found {
|
||||
func (sc *SocksConfig) HasAccount(user, pass string) bool {
|
||||
if actualPass, found := sc.accountMap[user]; found {
|
||||
return actualPass == pass
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (sc *SocksConfig) IP() net.IP {
|
||||
return sc.ip
|
||||
}
|
||||
|
||||
func init() {
|
||||
json.RegisterConfigType("socks", config.TypeInbound, func() interface{} {
|
||||
return new(SocksConfig)
|
||||
|
||||
@@ -5,9 +5,10 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
"github.com/v2ray/v2ray-core/common/errors"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/transport"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -49,7 +50,7 @@ func ReadAuthentication(reader io.Reader) (auth Socks5AuthenticationRequest, aut
|
||||
}
|
||||
if nBytes < 2 {
|
||||
log.Info("Socks expected 2 bytes read, but only %d bytes read", nBytes)
|
||||
err = errors.NewCorruptedPacketError()
|
||||
err = transport.CorruptedPacket
|
||||
return
|
||||
}
|
||||
|
||||
@@ -58,26 +59,27 @@ func ReadAuthentication(reader io.Reader) (auth Socks5AuthenticationRequest, aut
|
||||
auth4.Command = buffer.Value[1]
|
||||
auth4.Port = binary.BigEndian.Uint16(buffer.Value[2:4])
|
||||
copy(auth4.IP[:], buffer.Value[4:8])
|
||||
err = NewSocksVersion4Error()
|
||||
err = Socks4Downgrade
|
||||
return
|
||||
}
|
||||
|
||||
auth.version = buffer.Value[0]
|
||||
if auth.version != socksVersion {
|
||||
err = errors.NewProtocolVersionError(int(auth.version))
|
||||
log.Warning("Unknown protocol version %d", auth.version)
|
||||
err = proxy.InvalidProtocolVersion
|
||||
return
|
||||
}
|
||||
|
||||
auth.nMethods = buffer.Value[1]
|
||||
if auth.nMethods <= 0 {
|
||||
log.Info("Zero length of authentication methods")
|
||||
err = errors.NewCorruptedPacketError()
|
||||
err = transport.CorruptedPacket
|
||||
return
|
||||
}
|
||||
|
||||
if nBytes-2 != int(auth.nMethods) {
|
||||
log.Info("Unmatching number of auth methods, expecting %d, but got %d", auth.nMethods, nBytes)
|
||||
err = errors.NewCorruptedPacketError()
|
||||
err = transport.CorruptedPacket
|
||||
return
|
||||
}
|
||||
copy(auth.authMethods[:], buffer.Value[2:nBytes])
|
||||
@@ -194,7 +196,7 @@ func ReadRequest(reader io.Reader) (request *Socks5Request, err error) {
|
||||
return
|
||||
}
|
||||
if nBytes < 4 {
|
||||
err = errors.NewCorruptedPacketError()
|
||||
err = transport.CorruptedPacket
|
||||
return
|
||||
}
|
||||
request = &Socks5Request{
|
||||
@@ -210,7 +212,7 @@ func ReadRequest(reader io.Reader) (request *Socks5Request, err error) {
|
||||
return
|
||||
}
|
||||
if nBytes != 4 {
|
||||
err = errors.NewCorruptedPacketError()
|
||||
err = transport.CorruptedPacket
|
||||
return
|
||||
}
|
||||
case AddrTypeDomain:
|
||||
@@ -226,7 +228,7 @@ func ReadRequest(reader io.Reader) (request *Socks5Request, err error) {
|
||||
|
||||
if nBytes != int(domainLength) {
|
||||
log.Info("Unable to read domain with %d bytes, expecting %d bytes", nBytes, domainLength)
|
||||
err = errors.NewCorruptedPacketError()
|
||||
err = transport.CorruptedPacket
|
||||
return
|
||||
}
|
||||
request.Domain = string(buffer.Value[:domainLength])
|
||||
@@ -236,12 +238,12 @@ func ReadRequest(reader io.Reader) (request *Socks5Request, err error) {
|
||||
return
|
||||
}
|
||||
if nBytes != 16 {
|
||||
err = errors.NewCorruptedPacketError()
|
||||
err = transport.CorruptedPacket
|
||||
return
|
||||
}
|
||||
default:
|
||||
log.Info("Unexpected address type %d", request.AddrType)
|
||||
err = errors.NewCorruptedPacketError()
|
||||
err = transport.CorruptedPacket
|
||||
return
|
||||
}
|
||||
|
||||
@@ -250,7 +252,7 @@ func ReadRequest(reader io.Reader) (request *Socks5Request, err error) {
|
||||
return
|
||||
}
|
||||
if nBytes != 2 {
|
||||
err = errors.NewCorruptedPacketError()
|
||||
err = transport.CorruptedPacket
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,14 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
"github.com/v2ray/v2ray-core/common/errors"
|
||||
)
|
||||
|
||||
type SocksVersion4Error struct {
|
||||
errors.ErrorCode
|
||||
}
|
||||
|
||||
var socksVersion4ErrorInstance = SocksVersion4Error{ErrorCode: 1000}
|
||||
|
||||
func NewSocksVersion4Error() SocksVersion4Error {
|
||||
return socksVersion4ErrorInstance
|
||||
}
|
||||
|
||||
func (err SocksVersion4Error) Error() string {
|
||||
return err.Prefix() + "Request is socks version 4."
|
||||
}
|
||||
var (
|
||||
Socks4Downgrade = errors.New("Downgraded to Socks 4.")
|
||||
)
|
||||
|
||||
type Socks4AuthenticationRequest struct {
|
||||
Version byte
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestSocks4AuthenticationRequestRead(t *testing.T) {
|
||||
0x72, 0x72, 0x72, 0x72,
|
||||
}
|
||||
_, request4, err := ReadAuthentication(bytes.NewReader(rawRequest))
|
||||
assert.Error(err).HasCode(1000)
|
||||
assert.Error(err).Equals(Socks4Downgrade)
|
||||
assert.Byte(request4.Version).Named("Version").Equals(0x04)
|
||||
assert.Byte(request4.Command).Named("Command").Equals(0x01)
|
||||
assert.Uint16(request4.Port).Named("Port").Equals(53)
|
||||
|
||||
@@ -1,37 +1,47 @@
|
||||
package socks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
"github.com/v2ray/v2ray-core/common/errors"
|
||||
"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"
|
||||
jsonconfig "github.com/v2ray/v2ray-core/proxy/socks/config/json"
|
||||
"github.com/v2ray/v2ray-core/proxy/socks/protocol"
|
||||
)
|
||||
|
||||
var (
|
||||
UnsupportedSocksCommand = errors.New("Unsupported socks command.")
|
||||
UnsupportedAuthMethod = errors.New("Unsupported auth method.")
|
||||
)
|
||||
|
||||
// SocksServer is a SOCKS 5 proxy server
|
||||
type SocksServer struct {
|
||||
accepting bool
|
||||
vPoint *core.Point
|
||||
config *jsonconfig.SocksConfig
|
||||
accepting bool
|
||||
dispatcher app.PacketDispatcher
|
||||
config *jsonconfig.SocksConfig
|
||||
}
|
||||
|
||||
func NewSocksServer(vp *core.Point, config *jsonconfig.SocksConfig) *SocksServer {
|
||||
func NewSocksServer(dispatcher app.PacketDispatcher, config *jsonconfig.SocksConfig) *SocksServer {
|
||||
return &SocksServer{
|
||||
vPoint: vp,
|
||||
config: config,
|
||||
dispatcher: dispatcher,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (server *SocksServer) Listen(port uint16) error {
|
||||
listener, err := net.Listen("tcp", ":"+strconv.Itoa(int(port)))
|
||||
listener, err := net.ListenTCP("tcp", &net.TCPAddr{
|
||||
IP: []byte{0, 0, 0, 0},
|
||||
Port: int(port),
|
||||
Zone: "",
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Socks failed to listen on port %d: %v", port, err)
|
||||
return err
|
||||
@@ -44,29 +54,33 @@ func (server *SocksServer) Listen(port uint16) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (server *SocksServer) AcceptConnections(listener net.Listener) {
|
||||
func (server *SocksServer) AcceptConnections(listener *net.TCPListener) {
|
||||
for server.accepting {
|
||||
connection, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Error("Socks failed to accept new connection %v", err)
|
||||
continue
|
||||
}
|
||||
go server.HandleConnection(connection)
|
||||
retry.Timed(100 /* times */, 100 /* ms */).On(func() error {
|
||||
connection, err := listener.AcceptTCP()
|
||||
if err != nil {
|
||||
log.Error("Socks failed to accept new connection %v", err)
|
||||
return err
|
||||
}
|
||||
go server.HandleConnection(connection)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (server *SocksServer) HandleConnection(connection net.Conn) error {
|
||||
func (server *SocksServer) HandleConnection(connection *net.TCPConn) error {
|
||||
defer connection.Close()
|
||||
|
||||
reader := v2net.NewTimeOutReader(120, connection)
|
||||
|
||||
auth, auth4, err := protocol.ReadAuthentication(reader)
|
||||
if err != nil && !errors.HasCode(err, 1000) {
|
||||
if err != nil && err != protocol.Socks4Downgrade {
|
||||
log.Error("Socks failed to read authentication: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err != nil && errors.HasCode(err, 1000) {
|
||||
if err != nil && err == protocol.Socks4Downgrade {
|
||||
return server.handleSocks4(reader, connection, auth4)
|
||||
} else {
|
||||
return server.handleSocks5(reader, connection, auth)
|
||||
@@ -87,7 +101,7 @@ func (server *SocksServer) handleSocks5(reader *v2net.TimeOutReader, writer io.W
|
||||
return err
|
||||
}
|
||||
log.Warning("Socks client doesn't support allowed any auth methods.")
|
||||
return errors.NewInvalidOperationError("Unsupported auth methods.")
|
||||
return UnsupportedAuthMethod
|
||||
}
|
||||
|
||||
authResponse := protocol.NewAuthenticationResponse(expectedAuthMethod)
|
||||
@@ -113,9 +127,8 @@ func (server *SocksServer) handleSocks5(reader *v2net.TimeOutReader, writer io.W
|
||||
return err
|
||||
}
|
||||
if status != byte(0) {
|
||||
err = errors.NewAuthenticationError(upRequest.AuthDetail())
|
||||
log.Warning(err.Error())
|
||||
return err
|
||||
log.Warning("Invalid user account: %s", upRequest.AuthDetail())
|
||||
return proxy.InvalidAuthentication
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +142,6 @@ func (server *SocksServer) handleSocks5(reader *v2net.TimeOutReader, writer io.W
|
||||
return server.handleUDP(reader, writer)
|
||||
}
|
||||
|
||||
response := protocol.NewSocks5Response()
|
||||
if request.Command == protocol.CmdBind || request.Command == protocol.CmdUdpAssociate {
|
||||
response := protocol.NewSocks5Response()
|
||||
response.Error = protocol.ErrorCommandNotSupported
|
||||
@@ -143,9 +155,10 @@ func (server *SocksServer) handleSocks5(reader *v2net.TimeOutReader, writer io.W
|
||||
return err
|
||||
}
|
||||
log.Warning("Unsupported socks command %d", request.Command)
|
||||
return errors.NewInvalidOperationError("Socks command " + strconv.Itoa(int(request.Command)))
|
||||
return UnsupportedSocksCommand
|
||||
}
|
||||
|
||||
response := protocol.NewSocks5Response()
|
||||
response.Error = protocol.ErrorSuccess
|
||||
|
||||
// Some SOCKS software requires a value other than dest. Let's fake one:
|
||||
@@ -228,7 +241,8 @@ func (server *SocksServer) handleSocks4(reader io.Reader, writer io.Writer, auth
|
||||
responseBuffer.Release()
|
||||
|
||||
if result == protocol.Socks4RequestRejected {
|
||||
return errors.NewInvalidOperationError("Socks4 command " + strconv.Itoa(int(auth.Command)))
|
||||
log.Warning("Unsupported socks 4 command %d", auth.Command)
|
||||
return UnsupportedSocksCommand
|
||||
}
|
||||
|
||||
dest := v2net.NewTCPDestination(v2net.IPAddress(auth.IP[:], auth.Port))
|
||||
@@ -243,7 +257,7 @@ func (server *SocksServer) handleSocks4(reader io.Reader, writer io.Writer, auth
|
||||
}
|
||||
|
||||
func (server *SocksServer) transport(reader io.Reader, writer io.Writer, firstPacket v2net.Packet) {
|
||||
ray := server.vPoint.DispatchToOutbound(firstPacket)
|
||||
ray := server.dispatcher.DispatchToOutbound(firstPacket)
|
||||
input := ray.InboundInput()
|
||||
output := ray.InboundOutput()
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ import (
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/app/point"
|
||||
v2proxy "github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/proxy/socks/config/json"
|
||||
"github.com/v2ray/v2ray-core/testing/mocks"
|
||||
"github.com/v2ray/v2ray-core/testing/unit"
|
||||
@@ -23,7 +24,7 @@ func TestSocksTcpConnect(t *testing.T) {
|
||||
Data2Return: []byte("The data to be returned to socks server."),
|
||||
}
|
||||
|
||||
core.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
v2proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
|
||||
config := mocks.Config{
|
||||
PortValue: port,
|
||||
@@ -39,7 +40,7 @@ func TestSocksTcpConnect(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
point, err := core.NewPoint(&config)
|
||||
point, err := point.NewPoint(&config)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = point.Start()
|
||||
@@ -76,7 +77,7 @@ func TestSocksTcpConnectWithUserPass(t *testing.T) {
|
||||
Data2Return: []byte("The data to be returned to socks server."),
|
||||
}
|
||||
|
||||
core.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
v2proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
|
||||
config := mocks.Config{
|
||||
PortValue: port,
|
||||
@@ -98,7 +99,7 @@ func TestSocksTcpConnectWithUserPass(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
point, err := core.NewPoint(&config)
|
||||
point, err := point.NewPoint(&config)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = point.Start()
|
||||
@@ -126,6 +127,96 @@ func TestSocksTcpConnectWithUserPass(t *testing.T) {
|
||||
assert.String(targetServer).Equals(och.Destination.Address().String())
|
||||
}
|
||||
|
||||
func TestSocksTcpConnectWithWrongUserPass(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
port := uint16(12389)
|
||||
|
||||
och := &mocks.OutboundConnectionHandler{
|
||||
Data2Send: bytes.NewBuffer(make([]byte, 0, 1024)),
|
||||
Data2Return: []byte("The data to be returned to socks server."),
|
||||
}
|
||||
|
||||
v2proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
|
||||
config := mocks.Config{
|
||||
PortValue: port,
|
||||
InboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "socks",
|
||||
SettingsValue: &json.SocksConfig{
|
||||
AuthMethod: "password",
|
||||
Accounts: []json.SocksAccount{
|
||||
json.SocksAccount{
|
||||
Username: "userx",
|
||||
Password: "passy",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
OutboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "mock_och",
|
||||
SettingsValue: nil,
|
||||
},
|
||||
}
|
||||
|
||||
point, err := point.NewPoint(&config)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = point.Start()
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
socks5Client, err := proxy.SOCKS5("tcp", "127.0.0.1:12389", &proxy.Auth{"userx", "passz"}, proxy.Direct)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
targetServer := "1.2.3.4:443"
|
||||
_, err = socks5Client.Dial("tcp", targetServer)
|
||||
assert.Error(err).IsNotNil()
|
||||
}
|
||||
|
||||
func TestSocksTcpConnectWithWrongAuthMethod(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
port := uint16(38405)
|
||||
|
||||
och := &mocks.OutboundConnectionHandler{
|
||||
Data2Send: bytes.NewBuffer(make([]byte, 0, 1024)),
|
||||
Data2Return: []byte("The data to be returned to socks server."),
|
||||
}
|
||||
|
||||
v2proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
|
||||
config := mocks.Config{
|
||||
PortValue: port,
|
||||
InboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "socks",
|
||||
SettingsValue: &json.SocksConfig{
|
||||
AuthMethod: "password",
|
||||
Accounts: []json.SocksAccount{
|
||||
json.SocksAccount{
|
||||
Username: "userx",
|
||||
Password: "passy",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
OutboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "mock_och",
|
||||
SettingsValue: nil,
|
||||
},
|
||||
}
|
||||
|
||||
point, err := point.NewPoint(&config)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = point.Start()
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
socks5Client, err := proxy.SOCKS5("tcp", "127.0.0.1:38405", nil, proxy.Direct)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
targetServer := "1.2.3.4:443"
|
||||
_, err = socks5Client.Dial("tcp", targetServer)
|
||||
assert.Error(err).IsNotNil()
|
||||
}
|
||||
|
||||
func TestSocksUdpSend(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
port := uint16(12372)
|
||||
@@ -135,7 +226,7 @@ func TestSocksUdpSend(t *testing.T) {
|
||||
Data2Return: []byte("The data to be returned to socks server."),
|
||||
}
|
||||
|
||||
core.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
v2proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
|
||||
config := mocks.Config{
|
||||
PortValue: port,
|
||||
@@ -152,7 +243,7 @@ func TestSocksUdpSend(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
point, err := core.NewPoint(&config)
|
||||
point, err := point.NewPoint(&config)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = point.Start()
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
package socks
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"github.com/v2ray/v2ray-core/proxy/socks/config/json"
|
||||
)
|
||||
|
||||
type SocksServerFactory struct {
|
||||
}
|
||||
|
||||
func (factory SocksServerFactory) Create(vp *core.Point, rawConfig interface{}) (core.InboundConnectionHandler, error) {
|
||||
func (factory SocksServerFactory) Create(dispatcher app.PacketDispatcher, rawConfig interface{}) (proxy.InboundConnectionHandler, error) {
|
||||
config := rawConfig.(*json.SocksConfig)
|
||||
config.Initialize()
|
||||
return NewSocksServer(vp, config), nil
|
||||
return NewSocksServer(dispatcher, config), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
core.RegisterInboundConnectionHandlerFactory("socks", SocksServerFactory{})
|
||||
proxy.RegisterInboundConnectionHandlerFactory("socks", SocksServerFactory{})
|
||||
}
|
||||
|
||||
@@ -9,10 +9,6 @@ import (
|
||||
"github.com/v2ray/v2ray-core/proxy/socks/protocol"
|
||||
)
|
||||
|
||||
const (
|
||||
bufferSize = 2 * 1024
|
||||
)
|
||||
|
||||
var udpAddress v2net.Address
|
||||
|
||||
func (server *SocksServer) ListenUDP(port uint16) error {
|
||||
@@ -26,8 +22,7 @@ func (server *SocksServer) ListenUDP(port uint16) error {
|
||||
log.Error("Socks failed to listen UDP on port %d: %v", port, err)
|
||||
return err
|
||||
}
|
||||
// TODO: make this configurable
|
||||
udpAddress = v2net.IPAddress([]byte{127, 0, 0, 1}, port)
|
||||
udpAddress = v2net.IPAddress(server.config.IP(), port)
|
||||
|
||||
go server.AcceptPackets(conn)
|
||||
return nil
|
||||
@@ -68,10 +63,10 @@ func (server *SocksServer) AcceptPackets(conn *net.UDPConn) error {
|
||||
}
|
||||
|
||||
func (server *SocksServer) handlePacket(conn *net.UDPConn, packet v2net.Packet, clientAddr *net.UDPAddr, targetAddr v2net.Address) {
|
||||
ray := server.vPoint.DispatchToOutbound(packet)
|
||||
ray := server.dispatcher.DispatchToOutbound(packet)
|
||||
close(ray.InboundInput())
|
||||
|
||||
if data, ok := <-ray.InboundOutput(); ok {
|
||||
for data := range ray.InboundOutput() {
|
||||
response := &protocol.Socks5UDPRequest{
|
||||
Fragment: 0,
|
||||
Address: targetAddr,
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
package vmess
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
"github.com/v2ray/v2ray-core/config"
|
||||
"github.com/v2ray/v2ray-core/config/json"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
|
||||
)
|
||||
|
||||
// VMessUser is an authenticated user account in VMess configuration.
|
||||
type VMessUser struct {
|
||||
Id string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
func (u *VMessUser) ToUser() (user.User, error) {
|
||||
id, err := user.NewID(u.Id)
|
||||
return user.User{
|
||||
Id: id,
|
||||
}, err
|
||||
}
|
||||
|
||||
// VMessInboundConfig is
|
||||
type VMessInboundConfig struct {
|
||||
AllowedClients []VMessUser `json:"clients"`
|
||||
UDPEnabled bool `json:"udp"`
|
||||
}
|
||||
|
||||
type VNextConfig struct {
|
||||
Address string `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
Users []VMessUser `json:"users"`
|
||||
Network string `json:"network"`
|
||||
}
|
||||
|
||||
func (config VNextConfig) HasNetwork(network string) bool {
|
||||
return strings.Contains(config.Network, network)
|
||||
}
|
||||
|
||||
func (config VNextConfig) ToVNextServer(network string) VNextServer {
|
||||
users := make([]user.User, 0, len(config.Users))
|
||||
for _, user := range config.Users {
|
||||
vuser, err := user.ToUser()
|
||||
if err != nil {
|
||||
panic(log.Error("Failed to convert %v to User.", user))
|
||||
}
|
||||
users = append(users, vuser)
|
||||
}
|
||||
ip := net.ParseIP(config.Address)
|
||||
if ip == nil {
|
||||
panic(log.Error("Unable to parse VNext IP: %s", config.Address))
|
||||
}
|
||||
address := v2net.IPAddress(ip, config.Port)
|
||||
var dest v2net.Destination
|
||||
if network == "tcp" {
|
||||
dest = v2net.NewTCPDestination(address)
|
||||
} else {
|
||||
dest = v2net.NewUDPDestination(address)
|
||||
}
|
||||
return VNextServer{
|
||||
Destination: dest,
|
||||
Users: users,
|
||||
}
|
||||
}
|
||||
|
||||
type VMessOutboundConfig struct {
|
||||
VNextList []VNextConfig `json:"vnext"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
json.RegisterConfigType("vmess", config.TypeInbound, func() interface{} {
|
||||
return new(VMessInboundConfig)
|
||||
})
|
||||
|
||||
json.RegisterConfigType("vmess", config.TypeOutbound, func() interface{} {
|
||||
return new(VMessOutboundConfig)
|
||||
})
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package user
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
)
|
||||
@@ -11,6 +12,10 @@ const (
|
||||
IDBytesLen = 16
|
||||
)
|
||||
|
||||
var (
|
||||
InvalidID = errors.New("Invalid ID.")
|
||||
)
|
||||
|
||||
// The ID of en entity, in the form of an UUID.
|
||||
type ID struct {
|
||||
String string
|
||||
@@ -18,10 +23,11 @@ type ID struct {
|
||||
cmdKey [IDBytesLen]byte
|
||||
}
|
||||
|
||||
func NewID(id string) (ID, error) {
|
||||
func NewID(id string) (*ID, error) {
|
||||
idBytes, err := UUIDToID(id)
|
||||
if err != nil {
|
||||
return ID{}, log.Error("Failed to parse id %s", id)
|
||||
log.Error("Failed to parse id %s", id)
|
||||
return &ID{}, InvalidID
|
||||
}
|
||||
|
||||
md5hash := md5.New()
|
||||
@@ -29,7 +35,7 @@ func NewID(id string) (ID, error) {
|
||||
md5hash.Write([]byte("c48619fe-8f02-49e0-b9e9-edf763e17e21"))
|
||||
cmdKey := md5.Sum(nil)
|
||||
|
||||
return ID{
|
||||
return &ID{
|
||||
String: id,
|
||||
Bytes: idBytes,
|
||||
cmdKey: cmdKey,
|
||||
@@ -46,7 +52,8 @@ var byteGroups = []int{8, 4, 4, 4, 12}
|
||||
func UUIDToID(uuid string) (v [IDBytesLen]byte, err error) {
|
||||
text := []byte(uuid)
|
||||
if len(text) < 32 {
|
||||
err = log.Error("uuid: invalid UUID string: %s", text)
|
||||
log.Error("uuid: invalid UUID string: %s", text)
|
||||
err = InvalidID
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package user
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
6
proxy/vmess/config/inbound.go
Normal file
6
proxy/vmess/config/inbound.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package config
|
||||
|
||||
type Inbound interface {
|
||||
AllowedUsers() []User
|
||||
UDPEnabled() bool
|
||||
}
|
||||
30
proxy/vmess/config/json/inbound.go
Normal file
30
proxy/vmess/config/json/inbound.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/config"
|
||||
"github.com/v2ray/v2ray-core/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 {
|
||||
users := make([]vmessconfig.User, 0, len(c.AllowedClients))
|
||||
for _, rawUser := range c.AllowedClients {
|
||||
users = append(users, rawUser)
|
||||
}
|
||||
return users
|
||||
}
|
||||
|
||||
func (c *Inbound) UDPEnabled() bool {
|
||||
return c.UDP
|
||||
}
|
||||
|
||||
func init() {
|
||||
json.RegisterConfigType("vmess", config.TypeInbound, func() interface{} {
|
||||
return new(Inbound)
|
||||
})
|
||||
}
|
||||
85
proxy/vmess/config/json/outbound.go
Normal file
85
proxy/vmess/config/json/outbound.go
Normal file
@@ -0,0 +1,85 @@
|
||||
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"
|
||||
vmessconfig "github.com/v2ray/v2ray-core/proxy/vmess/config"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (t *ConfigTarget) UnmarshalJSON(data []byte) error {
|
||||
var rawConfig RawConfigTarget
|
||||
if err := json.Unmarshal(data, &rawConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
t.Users = rawConfig.Users
|
||||
ip := net.ParseIP(rawConfig.Address)
|
||||
if ip == nil {
|
||||
log.Error("Unable to parse IP: %s", rawConfig.Address)
|
||||
return config.BadConfiguration
|
||||
}
|
||||
t.Address = v2net.IPAddress(ip, rawConfig.Port)
|
||||
if rawConfig.HasNetwork("tcp") {
|
||||
t.TCPEnabled = true
|
||||
}
|
||||
if rawConfig.HasNetwork("udp") {
|
||||
t.UDPEnabled = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Outbound struct {
|
||||
TargetList []*ConfigTarget `json:"vnext"`
|
||||
}
|
||||
|
||||
func (o *Outbound) Targets() []*vmessconfig.OutboundTarget {
|
||||
targets := make([]*vmessconfig.OutboundTarget, 0, 2*len(o.TargetList))
|
||||
for _, rawTarget := range o.TargetList {
|
||||
users := make([]vmessconfig.User, 0, len(rawTarget.Users))
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
return targets
|
||||
}
|
||||
|
||||
func init() {
|
||||
jsonconfig.RegisterConfigType("vmess", config.TypeOutbound, func() interface{} {
|
||||
return new(Outbound)
|
||||
})
|
||||
}
|
||||
35
proxy/vmess/config/json/user.go
Normal file
35
proxy/vmess/config/json/user.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/config"
|
||||
)
|
||||
|
||||
// ConfigUser is an user account in VMess configuration.
|
||||
type ConfigUser struct {
|
||||
Id *config.ID
|
||||
Email string
|
||||
}
|
||||
|
||||
func (u *ConfigUser) UnmarshalJSON(data []byte) error {
|
||||
type rawUser struct {
|
||||
IdString string `json:"id"`
|
||||
EmailString string `json:"email"`
|
||||
}
|
||||
var rawUserValue rawUser
|
||||
if err := json.Unmarshal(data, &rawUserValue); err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := config.NewID(rawUserValue.IdString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Id = id
|
||||
u.Email = rawUserValue.EmailString
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ConfigUser) ID() *config.ID {
|
||||
return u.Id
|
||||
}
|
||||
14
proxy/vmess/config/outbound.go
Normal file
14
proxy/vmess/config/outbound.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
v2net "github.com/v2ray/v2ray-core/common/net"
|
||||
)
|
||||
|
||||
type OutboundTarget struct {
|
||||
Destination v2net.Destination
|
||||
Accounts []User
|
||||
}
|
||||
|
||||
type Outbound interface {
|
||||
Targets() []*OutboundTarget
|
||||
}
|
||||
5
proxy/vmess/config/user.go
Normal file
5
proxy/vmess/config/user.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package config
|
||||
|
||||
type User interface {
|
||||
ID() *ID
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package user
|
||||
|
||||
// User is the user account that is used for connection to a Point
|
||||
type User struct {
|
||||
Id ID `json:"id"` // The ID of this User.
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/collect"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/config"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -13,12 +14,12 @@ const (
|
||||
)
|
||||
|
||||
type UserSet interface {
|
||||
AddUser(user User) error
|
||||
GetUser(timeHash []byte) (*ID, int64, bool)
|
||||
AddUser(user config.User) error
|
||||
GetUser(timeHash []byte) (*config.ID, int64, bool)
|
||||
}
|
||||
|
||||
type TimedUserSet struct {
|
||||
validUserIds []ID
|
||||
validUserIds []*config.ID
|
||||
userHash map[string]indexTimePair
|
||||
userHashDeleteQueue *collect.TimedQueue
|
||||
access sync.RWMutex
|
||||
@@ -31,7 +32,7 @@ type indexTimePair struct {
|
||||
|
||||
func NewTimedUserSet() UserSet {
|
||||
tus := &TimedUserSet{
|
||||
validUserIds: make([]ID, 0, 16),
|
||||
validUserIds: make([]*config.ID, 0, 16),
|
||||
userHash: make(map[string]indexTimePair, 512),
|
||||
userHashDeleteQueue: collect.NewTimedQueue(updateIntervalSec),
|
||||
access: sync.RWMutex{},
|
||||
@@ -49,7 +50,7 @@ func (us *TimedUserSet) removeEntries(entries <-chan interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
func (us *TimedUserSet) generateNewHashes(lastSec, nowSec int64, idx int, id ID) {
|
||||
func (us *TimedUserSet) generateNewHashes(lastSec, nowSec int64, idx int, id *config.ID) {
|
||||
idHash := NewTimeHash(HMACHash{})
|
||||
for lastSec < nowSec {
|
||||
idHash := idHash.Hash(id.Bytes[:], lastSec)
|
||||
@@ -73,8 +74,8 @@ func (us *TimedUserSet) updateUserHash(tick <-chan time.Time) {
|
||||
}
|
||||
}
|
||||
|
||||
func (us *TimedUserSet) AddUser(user User) error {
|
||||
id := user.Id
|
||||
func (us *TimedUserSet) AddUser(user config.User) error {
|
||||
id := user.ID()
|
||||
idx := len(us.validUserIds)
|
||||
us.validUserIds = append(us.validUserIds, id)
|
||||
|
||||
@@ -85,12 +86,12 @@ func (us *TimedUserSet) AddUser(user User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (us TimedUserSet) GetUser(userHash []byte) (*ID, int64, bool) {
|
||||
func (us TimedUserSet) GetUser(userHash []byte) (*config.ID, 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.validUserIds[pair.index], pair.timeSec, true
|
||||
}
|
||||
return nil, 0, false
|
||||
}
|
||||
|
||||
@@ -9,10 +9,13 @@ import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/errors"
|
||||
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"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/config"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
|
||||
"github.com/v2ray/v2ray-core/transport"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -33,7 +36,7 @@ const (
|
||||
// streaming.
|
||||
type VMessRequest struct {
|
||||
Version byte
|
||||
UserId user.ID
|
||||
UserId config.ID
|
||||
RequestIV []byte
|
||||
RequestKey []byte
|
||||
ResponseHeader []byte
|
||||
@@ -66,14 +69,14 @@ func NewVMessRequestReader(vUserSet user.UserSet) *VMessRequestReader {
|
||||
func (r *VMessRequestReader) Read(reader io.Reader) (*VMessRequest, error) {
|
||||
buffer := make([]byte, 256)
|
||||
|
||||
nBytes, err := reader.Read(buffer[:user.IDBytesLen])
|
||||
nBytes, err := reader.Read(buffer[:config.IDBytesLen])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userId, timeSec, valid := r.vUserSet.GetUser(buffer[:nBytes])
|
||||
if !valid {
|
||||
return nil, errors.NewAuthenticationError(buffer[:nBytes])
|
||||
return nil, proxy.InvalidAuthentication
|
||||
}
|
||||
|
||||
aesCipher, err := aes.NewCipher(userId.CmdKey())
|
||||
@@ -99,7 +102,8 @@ func (r *VMessRequestReader) Read(reader io.Reader) (*VMessRequest, error) {
|
||||
}
|
||||
|
||||
if request.Version != Version {
|
||||
return nil, errors.NewProtocolVersionError(int(request.Version))
|
||||
log.Warning("Invalid protocol version %d", request.Version)
|
||||
return nil, proxy.InvalidProtocolVersion
|
||||
}
|
||||
|
||||
request.RequestIV = buffer[1:17] // 16 bytes
|
||||
@@ -149,7 +153,7 @@ func (r *VMessRequestReader) Read(reader io.Reader) (*VMessRequest, error) {
|
||||
expectedHash := binary.BigEndian.Uint32(buffer[bufferLen : bufferLen+4])
|
||||
|
||||
if actualHash != expectedHash {
|
||||
return nil, errors.NewCorruptedPacketError()
|
||||
return nil, transport.CorruptedPacket
|
||||
}
|
||||
|
||||
return request, nil
|
||||
|
||||
@@ -6,25 +6,34 @@ import (
|
||||
"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/testing/unit"
|
||||
)
|
||||
|
||||
type TestUser struct {
|
||||
id *config.ID
|
||||
}
|
||||
|
||||
func (u *TestUser) ID() *config.ID {
|
||||
return u.id
|
||||
}
|
||||
|
||||
func TestVMessSerialization(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
|
||||
userId, err := user.NewID("2b2966ac-16aa-4fbf-8d81-c5f172a3da51")
|
||||
userId, err := config.NewID("2b2966ac-16aa-4fbf-8d81-c5f172a3da51")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
userSet := mocks.MockUserSet{[]user.ID{}, make(map[string]int), make(map[string]int64)}
|
||||
userSet.AddUser(user.User{userId})
|
||||
userSet := mocks.MockUserSet{[]*config.ID{}, make(map[string]int), make(map[string]int64)}
|
||||
userSet.AddUser(&TestUser{userId})
|
||||
|
||||
request := new(VMessRequest)
|
||||
request.Version = byte(0x01)
|
||||
request.UserId = userId
|
||||
request.UserId = *userId
|
||||
|
||||
randBytes := make([]byte, 36)
|
||||
_, err = rand.Read(randBytes)
|
||||
@@ -61,13 +70,13 @@ func TestVMessSerialization(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkVMessRequestWriting(b *testing.B) {
|
||||
userId, _ := user.NewID("2b2966ac-16aa-4fbf-8d81-c5f172a3da51")
|
||||
userSet := mocks.MockUserSet{[]user.ID{}, make(map[string]int), make(map[string]int64)}
|
||||
userSet.AddUser(user.User{userId})
|
||||
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})
|
||||
|
||||
request := new(VMessRequest)
|
||||
request.Version = byte(0x01)
|
||||
request.UserId = userId
|
||||
request.UserId = *userId
|
||||
|
||||
randBytes := make([]byte, 36)
|
||||
rand.Read(randBytes)
|
||||
|
||||
@@ -4,9 +4,12 @@ import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/app/point"
|
||||
"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/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"
|
||||
)
|
||||
@@ -15,6 +18,8 @@ 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{
|
||||
@@ -22,7 +27,7 @@ func TestVMessInAndOut(t *testing.T) {
|
||||
DataReturned: bytes.NewBuffer(make([]byte, 0, 1024)),
|
||||
}
|
||||
|
||||
core.RegisterInboundConnectionHandlerFactory("mock_ich", ich)
|
||||
proxy.RegisterInboundConnectionHandlerFactory("mock_ich", ich)
|
||||
|
||||
configA := mocks.Config{
|
||||
PortValue: portA,
|
||||
@@ -32,14 +37,13 @@ func TestVMessInAndOut(t *testing.T) {
|
||||
},
|
||||
OutboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "vmess",
|
||||
SettingsValue: &VMessOutboundConfig{
|
||||
[]VNextConfig{
|
||||
VNextConfig{
|
||||
Address: "127.0.0.1",
|
||||
Port: 13829,
|
||||
Network: "tcp",
|
||||
Users: []VMessUser{
|
||||
VMessUser{Id: "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"},
|
||||
SettingsValue: &json.Outbound{
|
||||
[]*json.ConfigTarget{
|
||||
&json.ConfigTarget{
|
||||
Address: v2net.IPAddress([]byte{127, 0, 0, 1}, 13829),
|
||||
TCPEnabled: true,
|
||||
Users: []*json.ConfigUser{
|
||||
&json.ConfigUser{Id: testAccount},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -47,7 +51,7 @@ func TestVMessInAndOut(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
pointA, err := core.NewPoint(&configA)
|
||||
pointA, err := point.NewPoint(&configA)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = pointA.Start()
|
||||
@@ -60,15 +64,15 @@ func TestVMessInAndOut(t *testing.T) {
|
||||
Data2Return: []byte("The data to be returned to inbound server."),
|
||||
}
|
||||
|
||||
core.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
|
||||
configB := mocks.Config{
|
||||
PortValue: portB,
|
||||
InboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "vmess",
|
||||
SettingsValue: &VMessInboundConfig{
|
||||
AllowedClients: []VMessUser{
|
||||
VMessUser{Id: "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"},
|
||||
SettingsValue: &json.Inbound{
|
||||
AllowedClients: []*json.ConfigUser{
|
||||
&json.ConfigUser{Id: testAccount},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -78,7 +82,7 @@ func TestVMessInAndOut(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
pointB, err := core.NewPoint(&configB)
|
||||
pointB, err := point.NewPoint(&configB)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = pointB.Start()
|
||||
@@ -94,6 +98,8 @@ 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{
|
||||
@@ -101,7 +107,7 @@ func TestVMessInAndOutUDP(t *testing.T) {
|
||||
DataReturned: bytes.NewBuffer(make([]byte, 0, 1024)),
|
||||
}
|
||||
|
||||
core.RegisterInboundConnectionHandlerFactory("mock_ich", ich)
|
||||
proxy.RegisterInboundConnectionHandlerFactory("mock_ich", ich)
|
||||
|
||||
configA := mocks.Config{
|
||||
PortValue: portA,
|
||||
@@ -111,14 +117,13 @@ func TestVMessInAndOutUDP(t *testing.T) {
|
||||
},
|
||||
OutboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "vmess",
|
||||
SettingsValue: &VMessOutboundConfig{
|
||||
[]VNextConfig{
|
||||
VNextConfig{
|
||||
Address: "127.0.0.1",
|
||||
Port: 13841,
|
||||
Network: "udp",
|
||||
Users: []VMessUser{
|
||||
VMessUser{Id: "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"},
|
||||
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},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -126,7 +131,7 @@ func TestVMessInAndOutUDP(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
pointA, err := core.NewPoint(&configA)
|
||||
pointA, err := point.NewPoint(&configA)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = pointA.Start()
|
||||
@@ -139,17 +144,17 @@ func TestVMessInAndOutUDP(t *testing.T) {
|
||||
Data2Return: []byte("The data to be returned to inbound server."),
|
||||
}
|
||||
|
||||
core.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
proxy.RegisterOutboundConnectionHandlerFactory("mock_och", och)
|
||||
|
||||
configB := mocks.Config{
|
||||
PortValue: portB,
|
||||
InboundConfigValue: &mocks.ConnectionConfig{
|
||||
ProtocolValue: "vmess",
|
||||
SettingsValue: &VMessInboundConfig{
|
||||
AllowedClients: []VMessUser{
|
||||
VMessUser{Id: "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"},
|
||||
SettingsValue: &json.Inbound{
|
||||
AllowedClients: []*json.ConfigUser{
|
||||
&json.ConfigUser{Id: testAccount},
|
||||
},
|
||||
UDPEnabled: true,
|
||||
UDP: true,
|
||||
},
|
||||
},
|
||||
OutboundConfigValue: &mocks.ConnectionConfig{
|
||||
@@ -158,7 +163,7 @@ func TestVMessInAndOutUDP(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
pointB, err := core.NewPoint(&configB)
|
||||
pointB, err := point.NewPoint(&configB)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
err = pointB.Start()
|
||||
|
||||
@@ -6,25 +6,28 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/app"
|
||||
"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/common/retry"
|
||||
"github.com/v2ray/v2ray-core/proxy"
|
||||
"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"
|
||||
)
|
||||
|
||||
type VMessInboundHandler struct {
|
||||
vPoint *core.Point
|
||||
dispatcher app.PacketDispatcher
|
||||
clients user.UserSet
|
||||
accepting bool
|
||||
udpEnabled bool
|
||||
}
|
||||
|
||||
func NewVMessInboundHandler(vp *core.Point, clients user.UserSet, udpEnabled bool) *VMessInboundHandler {
|
||||
func NewVMessInboundHandler(dispatcher app.PacketDispatcher, clients user.UserSet, udpEnabled bool) *VMessInboundHandler {
|
||||
return &VMessInboundHandler{
|
||||
vPoint: vp,
|
||||
dispatcher: dispatcher,
|
||||
clients: clients,
|
||||
udpEnabled: udpEnabled,
|
||||
}
|
||||
@@ -37,7 +40,8 @@ func (handler *VMessInboundHandler) Listen(port uint16) error {
|
||||
Zone: "",
|
||||
})
|
||||
if err != nil {
|
||||
return log.Error("Unable to listen tcp:%d", port)
|
||||
log.Error("Unable to listen tcp port %d: %v", port, err)
|
||||
return err
|
||||
}
|
||||
handler.accepting = true
|
||||
go handler.AcceptConnections(listener)
|
||||
@@ -51,12 +55,16 @@ func (handler *VMessInboundHandler) Listen(port uint16) error {
|
||||
|
||||
func (handler *VMessInboundHandler) AcceptConnections(listener *net.TCPListener) error {
|
||||
for handler.accepting {
|
||||
connection, err := listener.AcceptTCP()
|
||||
if err != nil {
|
||||
log.Error("Failed to accpet connection: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
go handler.HandleConnection(connection)
|
||||
retry.Timed(100 /* times */, 100 /* ms */).On(func() error {
|
||||
connection, err := listener.AcceptTCP()
|
||||
if err != nil {
|
||||
log.Error("Failed to accpet connection: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
go handler.HandleConnection(connection)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -76,7 +84,7 @@ func (handler *VMessInboundHandler) HandleConnection(connection *net.TCPConn) er
|
||||
log.Access(connection.RemoteAddr().String(), request.Address.String(), log.AccessAccepted, "")
|
||||
log.Debug("VMessIn: Received request for %s", request.Address.String())
|
||||
|
||||
ray := handler.vPoint.DispatchToOutbound(v2net.NewPacket(request.Destination(), nil, true))
|
||||
ray := handler.dispatcher.DispatchToOutbound(v2net.NewPacket(request.Destination(), nil, true))
|
||||
input := ray.InboundInput()
|
||||
output := ray.InboundOutput()
|
||||
var readFinish, writeFinish sync.Mutex
|
||||
@@ -90,7 +98,8 @@ func (handler *VMessInboundHandler) HandleConnection(connection *net.TCPConn) er
|
||||
|
||||
responseWriter, err := v2io.NewAesEncryptWriter(responseKey[:], responseIV[:], connection)
|
||||
if err != nil {
|
||||
return log.Error("VMessIn: Failed to create encrypt writer: %v", err)
|
||||
log.Error("VMessIn: Failed to create encrypt writer: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Optimize for small response packet
|
||||
@@ -133,21 +142,17 @@ func handleOutput(request *protocol.VMessRequest, writer io.Writer, output <-cha
|
||||
type VMessInboundHandlerFactory struct {
|
||||
}
|
||||
|
||||
func (factory *VMessInboundHandlerFactory) Create(vp *core.Point, rawConfig interface{}) (core.InboundConnectionHandler, error) {
|
||||
config := rawConfig.(*VMessInboundConfig)
|
||||
func (factory *VMessInboundHandlerFactory) Create(dispatcher app.PacketDispatcher, rawConfig interface{}) (proxy.InboundConnectionHandler, error) {
|
||||
config := rawConfig.(config.Inbound)
|
||||
|
||||
allowedClients := user.NewTimedUserSet()
|
||||
for _, client := range config.AllowedClients {
|
||||
user, err := client.ToUser()
|
||||
if err != nil {
|
||||
panic(log.Error("VMessIn: Failed to parse user id %s: %v", client.Id, err))
|
||||
}
|
||||
for _, user := range config.AllowedUsers() {
|
||||
allowedClients.AddUser(user)
|
||||
}
|
||||
|
||||
return NewVMessInboundHandler(vp, allowedClients, config.UDPEnabled), nil
|
||||
return NewVMessInboundHandler(dispatcher, allowedClients, config.UDPEnabled()), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
core.RegisterInboundConnectionHandlerFactory("vmess", &VMessInboundHandlerFactory{})
|
||||
proxy.RegisterInboundConnectionHandlerFactory("vmess", &VMessInboundHandlerFactory{})
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (handler *VMessInboundHandler) AcceptPackets(conn *net.UDPConn) {
|
||||
}
|
||||
|
||||
func (handler *VMessInboundHandler) handlePacket(conn *net.UDPConn, request *protocol.VMessRequest, packet v2net.Packet, clientAddr *net.UDPAddr) {
|
||||
ray := handler.vPoint.DispatchToOutbound(packet)
|
||||
ray := handler.dispatcher.DispatchToOutbound(packet)
|
||||
close(ray.InboundInput())
|
||||
|
||||
responseKey := md5.Sum(request.RequestKey)
|
||||
|
||||
@@ -8,40 +8,34 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"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"
|
||||
"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."
|
||||
)
|
||||
|
||||
// VNext is the next Point server in the connection chain.
|
||||
type VNextServer struct {
|
||||
Destination v2net.Destination // Address of VNext server
|
||||
Users []user.User // User accounts for accessing VNext.
|
||||
}
|
||||
|
||||
type VMessOutboundHandler struct {
|
||||
vPoint *core.Point
|
||||
vNextList []VNextServer
|
||||
vNextListUDP []VNextServer
|
||||
vNextList []*config.OutboundTarget
|
||||
vNextListUDP []*config.OutboundTarget
|
||||
}
|
||||
|
||||
func NewVMessOutboundHandler(vp *core.Point, vNextList, vNextListUDP []VNextServer) *VMessOutboundHandler {
|
||||
func NewVMessOutboundHandler(vNextList, vNextListUDP []*config.OutboundTarget) *VMessOutboundHandler {
|
||||
return &VMessOutboundHandler{
|
||||
vPoint: vp,
|
||||
vNextList: vNextList,
|
||||
vNextListUDP: vNextListUDP,
|
||||
}
|
||||
}
|
||||
|
||||
func pickVNext(serverList []VNextServer) (v2net.Destination, user.User) {
|
||||
func pickVNext(serverList []*config.OutboundTarget) (v2net.Destination, config.User) {
|
||||
vNextLen := len(serverList)
|
||||
if vNextLen == 0 {
|
||||
panic("VMessOut: Zero vNext is configured.")
|
||||
@@ -52,7 +46,7 @@ func pickVNext(serverList []VNextServer) (v2net.Destination, user.User) {
|
||||
}
|
||||
|
||||
vNext := serverList[vNextIndex]
|
||||
vNextUserLen := len(vNext.Users)
|
||||
vNextUserLen := len(vNext.Accounts)
|
||||
if vNextUserLen == 0 {
|
||||
panic("VMessOut: Zero User account.")
|
||||
}
|
||||
@@ -60,11 +54,11 @@ func pickVNext(serverList []VNextServer) (v2net.Destination, user.User) {
|
||||
if vNextUserLen > 1 {
|
||||
vNextUserIndex = mrand.Intn(vNextUserLen)
|
||||
}
|
||||
vNextUser := vNext.Users[vNextUserIndex]
|
||||
vNextUser := vNext.Accounts[vNextUserIndex]
|
||||
return vNext.Destination, vNextUser
|
||||
}
|
||||
|
||||
func (handler *VMessOutboundHandler) Dispatch(firstPacket v2net.Packet, ray core.OutboundRay) error {
|
||||
func (handler *VMessOutboundHandler) Dispatch(firstPacket v2net.Packet, ray ray.OutboundRay) error {
|
||||
vNextList := handler.vNextList
|
||||
if firstPacket.Destination().IsUDP() {
|
||||
vNextList = handler.vNextListUDP
|
||||
@@ -77,7 +71,7 @@ func (handler *VMessOutboundHandler) Dispatch(firstPacket v2net.Packet, ray core
|
||||
}
|
||||
request := &protocol.VMessRequest{
|
||||
Version: protocol.Version,
|
||||
UserId: vNextUser.Id,
|
||||
UserId: *vNextUser.ID(),
|
||||
Command: command,
|
||||
Address: firstPacket.Destination().Address(),
|
||||
}
|
||||
@@ -91,7 +85,7 @@ func (handler *VMessOutboundHandler) Dispatch(firstPacket v2net.Packet, ray core
|
||||
return startCommunicate(request, vNextAddress, ray, firstPacket)
|
||||
}
|
||||
|
||||
func startCommunicate(request *protocol.VMessRequest, dest v2net.Destination, ray core.OutboundRay, firstPacket v2net.Packet) error {
|
||||
func startCommunicate(request *protocol.VMessRequest, dest v2net.Destination, ray ray.OutboundRay, firstPacket v2net.Packet) error {
|
||||
conn, err := net.Dial(dest.Network(), dest.Address().String())
|
||||
if err != nil {
|
||||
log.Error("Failed to open %s: %v", dest.String(), err)
|
||||
@@ -199,21 +193,21 @@ func handleResponse(conn net.Conn, request *protocol.VMessRequest, output chan<-
|
||||
type VMessOutboundHandlerFactory struct {
|
||||
}
|
||||
|
||||
func (factory *VMessOutboundHandlerFactory) Create(vp *core.Point, rawConfig interface{}) (core.OutboundConnectionHandler, error) {
|
||||
config := rawConfig.(*VMessOutboundConfig)
|
||||
servers := make([]VNextServer, 0, len(config.VNextList))
|
||||
udpServers := make([]VNextServer, 0, len(config.VNextList))
|
||||
for _, server := range config.VNextList {
|
||||
if server.HasNetwork("tcp") {
|
||||
servers = append(servers, server.ToVNextServer("tcp"))
|
||||
func (factory *VMessOutboundHandlerFactory) Create(rawConfig interface{}) (proxy.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 server.HasNetwork("udp") {
|
||||
udpServers = append(udpServers, server.ToVNextServer("udp"))
|
||||
if target.Destination.IsUDP() {
|
||||
udpServers = append(udpServers, target)
|
||||
}
|
||||
}
|
||||
return NewVMessOutboundHandler(vp, servers, udpServers), nil
|
||||
return NewVMessOutboundHandler(servers, udpServers), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
core.RegisterOutboundConnectionHandlerFactory("vmess", &VMessOutboundHandlerFactory{})
|
||||
proxy.RegisterOutboundConnectionHandlerFactory("vmess", &VMessOutboundHandlerFactory{})
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"auth": "noauth",
|
||||
"udp": false
|
||||
"udp": false,
|
||||
"ip": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
"outbound": {
|
||||
|
||||
@@ -30,6 +30,7 @@ if [ -z "$GOPATH" ]; then
|
||||
export GOPATH=/v2ray
|
||||
fi
|
||||
|
||||
go get github.com/v2ray/v2ray-core
|
||||
go build -o $GOPATH/bin/v2ray -compiler gc github.com/v2ray/v2ray-core/release/server
|
||||
|
||||
go get -u github.com/v2ray/v2ray-core
|
||||
rm $GOPATH/bin/build
|
||||
go install github.com/v2ray/v2ray-core/tools/build
|
||||
$GOPATH/bin/build
|
||||
|
||||
@@ -1,49 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
GIT_REV=$(git rev-parse HEAD)
|
||||
VERSION=$(git name-rev --tags --name-only $GIT_REV)
|
||||
go install github.com/v2ray/v2ray-core/tools/build
|
||||
|
||||
LD_FLAGS="-s"
|
||||
|
||||
if [ "$VERSION" != "undefined" ]; then
|
||||
VERSION=${VERSION%^0}
|
||||
TODAY="$(date -u +%Y%m%d)"
|
||||
LD_FLAGS="${LD_FLAGS} -X github.com/v2ray/v2ray-core.version=${VERSION} -X github.com/v2ray/v2ray-core.build=${TODAY}"
|
||||
else
|
||||
VERSION="custom"
|
||||
fi
|
||||
|
||||
BIN_PATH=$GOPATH/bin
|
||||
mkdir -p $BIN_PATH
|
||||
|
||||
function build {
|
||||
local GOOS=$1
|
||||
local GOARCH=$2
|
||||
local SUFFIX=$3
|
||||
local EXT=$4
|
||||
|
||||
local REL_PATH=$BIN_PATH/v2ray_${VERSION}${SUFFIX}
|
||||
local TARGET=$REL_PATH/v2ray${EXT}
|
||||
if [ -d "$REL_PATH" ]; then
|
||||
rm -rf "$REL_PATH"
|
||||
fi
|
||||
mkdir -p $REL_PATH/config
|
||||
cp -R $GOPATH/src/github.com/v2ray/v2ray-core/release/config/* $REL_PATH/config/
|
||||
GOOS=${GOOS} GOARCH=${GOARCH} go build -o ${TARGET} -compiler gc -ldflags "${LD_FLAGS}" github.com/v2ray/v2ray-core/release/server
|
||||
|
||||
ZIP_FILE=$BIN_PATH/v2ray${SUFFIX}.zip
|
||||
if [ -f $ZIP_FILE ]; then
|
||||
rm -f $ZIP_FILE
|
||||
fi
|
||||
|
||||
pushd $BIN_PATH
|
||||
zip -r $ZIP_FILE ./v2ray_${VERSION}${SUFFIX}/*
|
||||
popd
|
||||
}
|
||||
|
||||
build "darwin" "amd64" "-macos" "-macos"
|
||||
build "windows" "amd64" "-windows-64" "-windows-64.exe"
|
||||
build "windows" "386" "-windows-32" "-windows-32.exe"
|
||||
build "linux" "amd64" "-linux-64" "-linux-64"
|
||||
build "linux" "386" "-linux-32" "-linux-32"
|
||||
build "linux" "arm" "-armv6" "-armv6"
|
||||
$GOPATH/bin/build --os=windows --arch=x86 --zip
|
||||
$GOPATH/bin/build --os=windows --arch=x64 --zip
|
||||
$GOPATH/bin/build --os=macos --arch=x64 --zip
|
||||
$GOPATH/bin/build --os=linux --arch=x86 --zip
|
||||
$GOPATH/bin/build --os=linux --arch=x64 --zip
|
||||
$GOPATH/bin/build --os=linux --arch=arm --zip
|
||||
$GOPATH/bin/build --os=linux --arch=arm64 --zip
|
||||
|
||||
@@ -3,8 +3,11 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"github.com/v2ray/v2ray-core/app/point"
|
||||
"github.com/v2ray/v2ray-core/common/log"
|
||||
jsonconf "github.com/v2ray/v2ray-core/config/json"
|
||||
|
||||
@@ -13,21 +16,30 @@ import (
|
||||
_ "github.com/v2ray/v2ray-core/proxy/freedom/config/json"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/socks"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/vmess"
|
||||
_ "github.com/v2ray/v2ray-core/proxy/vmess/config/json"
|
||||
)
|
||||
|
||||
var (
|
||||
configFile = flag.String("config", "", "Config file for this Point server.")
|
||||
configFile string
|
||||
logLevel = flag.String("loglevel", "warning", "Level of log info to be printed to console, available value: debug, info, warning, error")
|
||||
version = flag.Bool("version", false, "Show current version of V2Ray.")
|
||||
)
|
||||
|
||||
func init() {
|
||||
defaultConfigFile := ""
|
||||
workingDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
if err == nil {
|
||||
defaultConfigFile = filepath.Join(workingDir, "config.json")
|
||||
}
|
||||
flag.StringVar(&configFile, "config", defaultConfigFile, "Config file for this Point server.")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
core.PrintVersion()
|
||||
|
||||
if *version {
|
||||
fmt.Printf("V2Ray %s (%s) %s", core.Version(), core.Codename, core.Build())
|
||||
fmt.Println()
|
||||
fmt.Println(core.Intro)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -45,13 +57,13 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if configFile == nil || len(*configFile) == 0 {
|
||||
if len(configFile) == 0 {
|
||||
log.Error("Config file is not set.")
|
||||
return
|
||||
}
|
||||
config, err := jsonconf.LoadConfig(*configFile)
|
||||
config, err := jsonconf.LoadConfig(configFile)
|
||||
if err != nil {
|
||||
log.Error("Failed to read config file (%s): %v", *configFile, err)
|
||||
log.Error("Failed to read config file (%s): %v", configFile, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -59,7 +71,7 @@ func main() {
|
||||
log.InitAccessLogger(config.LogConfig().AccessLog())
|
||||
}
|
||||
|
||||
vPoint, err := core.NewPoint(config)
|
||||
vPoint, err := point.NewPoint(config)
|
||||
if err != nil {
|
||||
log.Error("Failed to create Point server: %v", err)
|
||||
return
|
||||
|
||||
@@ -7,49 +7,4 @@
|
||||
|
||||
## 架构
|
||||
|
||||
### 术语
|
||||
* Point:一个 V2Ray 服务器称为 Point Server
|
||||
* Set:一组 Point Server,包含多个 Point 进程,由一个 Master 进程统一管理。
|
||||
* SuperSet:多机环境中的多个 Set
|
||||
|
||||
### 工作流程
|
||||
Point 可接收来自用户或其它 Point 的请求,并将请求转发至配置中的下一个 Point(或 Set 或 SuperSet) 或目标网站,然后将所得到的应答回复给请求来源。
|
||||
Point 采用白名单机制,只接受已认证帐号的请求。
|
||||
|
||||
### 通信协议
|
||||
* Point 之间默认使用自有 VMess 协议,或第三方自定义协议。
|
||||
* Point 和客户端之间可使用以下协议:
|
||||
* HTTP Proxy
|
||||
* SOCKS Proxy
|
||||
* PPTP / L2TP / SSTP 等 VPN 隧道
|
||||
* 其它自定义协议
|
||||
* Point 和目标网站之间使用以下协议:
|
||||
* HTTP / HTTPS
|
||||
* UDP (DNS)
|
||||
|
||||
#### VMess
|
||||
VMess 为 V2Ray 的原生协议,设计用于两个 Point 之间的通信。[详细设计](https://github.com/V2Ray/v2ray-core/blob/master/spec/vmess.md)
|
||||
|
||||
### Point
|
||||
* 每个 Point 有一个 ID,运行时生成
|
||||
* 每个 Point 可使用独立的配置文件,或从 Set 继承
|
||||
* 一个 Point 监听主机上的一个特定端口(可配置),用于接收和发送数据
|
||||
* 一个 Point 运行于一个独立的进程,可设定其使用的系统帐户
|
||||
|
||||
### Set
|
||||
TODO
|
||||
|
||||
### SuperSet
|
||||
TODO
|
||||
|
||||
## Point 详细设计
|
||||
一个 Point 包含五个部分:
|
||||
* 配置文件处理:读取和解析配置文件
|
||||
* 输入(Inbound):负责与客户端建立连接(如 TCP),接收客户端的消息
|
||||
* 输出(Outbound):负责向客户端发送消息
|
||||
|
||||
### 配置文件
|
||||
配置文件使用 JSON / ProtoBuf 兼容格式
|
||||
|
||||
## 编程语言
|
||||
暂定为 golang。
|
||||

|
||||
|
||||
@@ -10,5 +10,6 @@ V2Ray 支持以下资助方式,收到捐赠之后您的昵称将会显示在
|
||||
|
||||
|
||||
## 感谢您的帮助
|
||||
* 2015.10.02: a*****u $25
|
||||
* 2015.09.30: s*******@gmail.com $10
|
||||
* 2015.10.17: Penn G\*\*\*\*\*\* $25
|
||||
* 2015.10.02: a\*\*\*\*\*u $25
|
||||
* 2015.09.30: s\*\*\*\*\*\*\*@gmail.com $10
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
## 工作机制
|
||||
|
||||
你需要至少两个 Point Server(设为 A、B)才可以正常穿墙。以网页浏览为例,你的浏览器和 A 以 Socks 5 协议通信,B 和目标网站之间以 HTTP 协议通信,A 和 B 之间使用 V2Ray 的自有协议 [VMess](https://github.com/V2Ray/v2ray-core/blob/master/spec/vmess.md) 通信,如下图:
|
||||
你需要至少两个 V2Ray Server(设为 A、B)才可以正常穿墙。以网页浏览为例,你的浏览器和 A 以 Socks 5 协议通信,B 和目标网站之间以 HTTP 协议通信,A 和 B 之间使用 V2Ray 的自有协议 [VMess](https://github.com/V2Ray/v2ray-core/blob/master/spec/vmess.md) 通信,如下图:
|
||||
|
||||

|
||||
|
||||
通常 Point A 运行在你自己的电脑,Point B 运行在一台海外的 VPS 中。
|
||||
通常 Server A 运行在你自己的电脑,Server B 运行在一台海外的 VPS 中。
|
||||
|
||||
## 安装 V2Ray Point Server
|
||||
## 安装 V2Ray Server
|
||||
[安装 V2Ray](https://github.com/V2Ray/v2ray-core/blob/master/spec/install.md)
|
||||
|
||||
## 配置 V2Ray Point Server
|
||||
### Point A
|
||||
## 配置 V2Ray Server
|
||||
### Server A
|
||||
示例配置保存于 [vpoint_socks_vmess.json](https://github.com/v2ray/v2ray-core/blob/master/release/config/vpoint_socks_vmess.json) 文件中,格式如下:
|
||||
```javascript
|
||||
{
|
||||
@@ -32,10 +32,10 @@
|
||||
"settings": {
|
||||
"vnext": [
|
||||
{
|
||||
"address": "127.0.0.1", // Point B 的 IP 地址,IPv4 或 IPv6,不支持域名
|
||||
"port": 27183, // Point B 的监听端口,请更换成其它的值
|
||||
"address": "127.0.0.1", // Server B 的 IP 地址,IPv4 或 IPv6,不支持域名
|
||||
"port": 27183, // Server B 的监听端口,请更换成其它的值
|
||||
"users": [
|
||||
// 用户 ID,必须包含在 Point B 的配置文件中。此 ID 将被用于通信的认证,请自行更换随机的 ID,可以使用 https://www.uuidgenerator.net/ 来生成新的 ID。
|
||||
// 用户 ID,必须包含在 Server B 的配置文件中。此 ID 将被用于通信的认证,请自行更换随机的 ID,可以使用 https://www.uuidgenerator.net/ 来生成新的 ID。
|
||||
{"id": "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"}
|
||||
],
|
||||
"network": "tcp" // 如果要使用 UDP 转发,请改成 "tcp,udp"
|
||||
@@ -46,11 +46,11 @@
|
||||
}
|
||||
```
|
||||
|
||||
### Point B
|
||||
### Server B
|
||||
示例配置保存于 [vpoint_vmess_freedom.json](https://github.com/v2ray/v2ray-core/blob/master/release/config/vpoint_vmess_freedom.json) 文件中,格式如下:
|
||||
```javascript
|
||||
{
|
||||
"port": 27183, // 监听端口,必须和 Point A 中指定的一致
|
||||
"port": 27183, // 监听端口,必须和 Server A 中指定的一致
|
||||
"log" : {
|
||||
"access": "access.log" // 访问记录
|
||||
},
|
||||
@@ -58,7 +58,7 @@
|
||||
"protocol": "vmess", // 中继协议,不用改
|
||||
"settings": {
|
||||
"clients": [
|
||||
// 认可的用户 ID,必须包含 Point A 中的用户 ID
|
||||
// 认可的用户 ID,必须包含 Server A 中的用户 ID
|
||||
{"id": "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"}
|
||||
],
|
||||
"udp": false // 如果要使用 UDP 转发,请改成 true
|
||||
@@ -77,11 +77,11 @@
|
||||
|
||||
## 运行
|
||||
|
||||
Point Server A
|
||||
Server A
|
||||
|
||||
./server --config="vpoint_socks_vmess.json 的绝对路径"
|
||||
|
||||
Point Server B
|
||||
Server B
|
||||
|
||||
./server --config="vpoint_vmess_freedom.json 的绝对路径"
|
||||
|
||||
|
||||
14
spec/id.md
14
spec/id.md
@@ -1,14 +0,0 @@
|
||||
# ID 的定义和使用
|
||||
|
||||
ID 等价于 [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier),是一个 16 字节长的随机数,它的作用相当于一个令牌(Token)。
|
||||
|
||||
## 设计
|
||||
一个 ID 形如:de305d54-75b4-431b-adb2-eb6b9e546014,几乎完全随机,可以使用任何的 UUID 生成器来生成,比如[这个](https://www.uuidgenerator.net/)。
|
||||
|
||||
## 使用
|
||||
ID 在消息传递过程中用于验证客户端的有效性,只有当服务器认可当前 ID 时,才进行后续操作,否则关闭连接甚至加入黑名单。
|
||||
|
||||
在多用户环境中,用户帐号应与 ID 分开存放,即用户帐号和 ID 有一对一或一对多的关系,在 Point 系统中,只负责管理 ID,用户帐号(及权限、费用等)由另外的系统管理。
|
||||
|
||||
在后续版本中,Point 之间应有能力进行沟通而生成新的临时 ID,从而减少通讯的可探测性。
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
## 预编译程序
|
||||
发布于 [Release](https://github.com/v2ray/v2ray-core/releases) 中,每周更新,[更新周期见此](https://github.com/V2Ray/v2ray-core/blob/master/spec/roadmap.md)。
|
||||
|
||||
其中:
|
||||
* v2ray-linux-32.zip: 适用于 32 位 Linux,各种发行版均可。
|
||||
* v2ray-linux-64.zip: 适用于 64 位 Linux,各种发行版均可。
|
||||
* v2ray-linux-arm.zip: 适用于 ARMv6 及之后平台的 Linux,如 Raspberry Pi。
|
||||
* v2ray-linux-arm64.zip: 适用于 ARMv8 及之后平台的 Linux。
|
||||
* v2ray-linux-macos.zip: 适用于 Mac OS X 10.7 以及之后版本。
|
||||
* v2ray-windows-32.zip: 适用于 32 位 Windows,Vista 及之后版本。
|
||||
* v2ray-windows-64.zip: 适用于 64 位 Windows,Vista 及之后版本。
|
||||
|
||||
## 编译源文件
|
||||
|
||||
大概流程,请根据实际情况修改
|
||||
@@ -15,15 +24,18 @@
|
||||
2. sudo tar -C /usr/local -xzf go_latest.tar.gz
|
||||
3. export PATH=$PATH:/usr/local/go/bin
|
||||
4. export GOPATH=$HOME/work
|
||||
3. go get github.com/v2ray/v2ray-core
|
||||
4. go build github.com/v2ray/v2ray-core/release/server
|
||||
3. 下载 V2Ray 源文件:go get -u github.com/v2ray/v2ray-core
|
||||
4. 生成编译脚本:go install github.com/v2ray/v2ray-core/tools/build
|
||||
5. 编译 V2Ray:$GOPATH/bin/build
|
||||
6. V2Ray 程序及配置文件会被放在 $GOPATH/bin/v2ray-XXX 文件夹下(XXX 视平台不同而不同)
|
||||
|
||||
### Arch Linux
|
||||
1. 安装 Git: sudo pacman -S git
|
||||
2. 安装 golang:sudo pacman -S go
|
||||
1. export GOPATH=$HOME/work
|
||||
3. go get github.com/v2ray/v2ray-core
|
||||
4. go build -o $GOPATH/bin/v2ray -compiler gc github.com/v2ray/v2ray-core/release/server
|
||||
3. go get -u github.com/v2ray/v2ray-core
|
||||
4. go install github.com/v2ray/v2ray-core/tools/build
|
||||
5. $GOPATH/bin/build
|
||||
|
||||
### Debian / Ubuntu
|
||||
bash <(curl -s https://raw.githubusercontent.com/v2ray/v2ray-core/master/release/install.sh)
|
||||
|
||||
BIN
spec/v2ray.png
BIN
spec/v2ray.png
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.7 KiB |
BIN
spec/v2ray_design.png
Normal file
BIN
spec/v2ray_design.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -5,7 +5,7 @@
|
||||
## 格式
|
||||
### 数据请求
|
||||
认证部分:
|
||||
* 16 字节:基于时间的 hash(用户 [ID](https://github.com/V2Ray/v2ray-core/blob/master/spec/id.md)),见下文
|
||||
* 16 字节:基于时间的 hash(用户 ID),见下文
|
||||
|
||||
指令部分:
|
||||
* 1 字节:版本号,目前为 0x1
|
||||
@@ -43,6 +43,17 @@
|
||||
|
||||
其中数据部分使用 AES-128-CFB 加密,IV 为 md5(请求数据 IV),Key 为 md5(请求数据 Key)
|
||||
|
||||
## 用户 ID
|
||||
ID 等价于 [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier),是一个 16 字节长的随机数,它的作用相当于一个令牌(Token)。
|
||||
|
||||
一个 ID 形如:de305d54-75b4-431b-adb2-eb6b9e546014,几乎完全随机,可以使用任何的 UUID 生成器来生成,比如[这个](https://www.uuidgenerator.net/)。
|
||||
|
||||
ID 在消息传递过程中用于验证客户端的有效性,只有当服务器认可当前 ID 时,才进行后续操作,否则关闭连接甚至加入黑名单。
|
||||
|
||||
在多用户环境中,用户帐号应与 ID 分开存放,即用户帐号和 ID 有一对一或一对多的关系,在 V2Ray Server 中,只负责管理 ID,用户帐号(及权限、费用等)由另外的系统管理。
|
||||
|
||||
在后续版本中,V2Ray Server 之间应有能力进行沟通而生成新的临时 ID,从而减少通讯的可探测性。
|
||||
|
||||
## 基于时间的用户 ID Hash
|
||||
|
||||
* H = MD5
|
||||
|
||||
@@ -3,16 +3,17 @@ package mocks
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"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
|
||||
Server *core.Point
|
||||
Dispatcher app.PacketDispatcher
|
||||
}
|
||||
|
||||
func (handler *InboundConnectionHandler) Listen(port uint16) error {
|
||||
@@ -21,7 +22,7 @@ func (handler *InboundConnectionHandler) Listen(port uint16) error {
|
||||
}
|
||||
|
||||
func (handler *InboundConnectionHandler) Communicate(packet v2net.Packet) error {
|
||||
ray := handler.Server.DispatchToOutbound(packet)
|
||||
ray := handler.Dispatcher.DispatchToOutbound(packet)
|
||||
|
||||
input := ray.InboundInput()
|
||||
output := ray.InboundOutput()
|
||||
@@ -36,7 +37,7 @@ func (handler *InboundConnectionHandler) Communicate(packet v2net.Packet) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (handler *InboundConnectionHandler) Create(point *core.Point, config interface{}) (core.InboundConnectionHandler, error) {
|
||||
handler.Server = point
|
||||
func (handler *InboundConnectionHandler) Create(dispatcher app.PacketDispatcher, config interface{}) (proxy.InboundConnectionHandler, error) {
|
||||
handler.Dispatcher = dispatcher
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
|
||||
"github.com/v2ray/v2ray-core/proxy/vmess/config"
|
||||
)
|
||||
|
||||
type MockUserSet struct {
|
||||
UserIds []user.ID
|
||||
UserIds []*config.ID
|
||||
UserHashes map[string]int
|
||||
Timestamps map[string]int64
|
||||
}
|
||||
|
||||
func (us *MockUserSet) AddUser(user user.User) error {
|
||||
us.UserIds = append(us.UserIds, user.Id)
|
||||
func (us *MockUserSet) AddUser(user config.User) error {
|
||||
us.UserIds = append(us.UserIds, user.ID())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (us *MockUserSet) GetUser(userhash []byte) (*user.ID, int64, bool) {
|
||||
func (us *MockUserSet) GetUser(userhash []byte) (*config.ID, int64, bool) {
|
||||
idx, found := us.UserHashes[string(userhash)]
|
||||
if found {
|
||||
return &us.UserIds[idx], us.Timestamps[string(userhash)], true
|
||||
return us.UserIds[idx], us.Timestamps[string(userhash)], true
|
||||
}
|
||||
return nil, 0, false
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@ package mocks
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/v2ray/v2ray-core"
|
||||
"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 {
|
||||
@@ -14,7 +15,7 @@ type OutboundConnectionHandler struct {
|
||||
Destination v2net.Destination
|
||||
}
|
||||
|
||||
func (handler *OutboundConnectionHandler) Dispatch(packet v2net.Packet, ray core.OutboundRay) error {
|
||||
func (handler *OutboundConnectionHandler) Dispatch(packet v2net.Packet, ray ray.OutboundRay) error {
|
||||
input := ray.OutboundInput()
|
||||
output := ray.OutboundOutput()
|
||||
|
||||
@@ -42,6 +43,6 @@ func (handler *OutboundConnectionHandler) Dispatch(packet v2net.Packet, ray core
|
||||
return nil
|
||||
}
|
||||
|
||||
func (handler *OutboundConnectionHandler) Create(point *core.Point, config interface{}) (core.OutboundConnectionHandler, error) {
|
||||
func (handler *OutboundConnectionHandler) Create(config interface{}) (proxy.OutboundConnectionHandler, error) {
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
package unit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/errors"
|
||||
)
|
||||
|
||||
type ErrorSubject struct {
|
||||
*Subject
|
||||
value error
|
||||
@@ -43,8 +37,8 @@ func (subject *ErrorSubject) IsNil() {
|
||||
}
|
||||
}
|
||||
|
||||
func (subject *ErrorSubject) HasCode(code int) {
|
||||
if !errors.HasCode(subject.value, code) {
|
||||
subject.FailWithMessage(fmt.Sprintf("Not ture that %s has error code 0x%04X.", subject.DisplayString(), code))
|
||||
func (subject *ErrorSubject) IsNotNil() {
|
||||
if subject.value == nil {
|
||||
subject.FailWithMessage("Not true that the error is not nil.")
|
||||
}
|
||||
}
|
||||
|
||||
62
tools/build/archive.go
Normal file
62
tools/build/archive.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type ZipWorker struct {
|
||||
zipWriter *zip.Writer
|
||||
root string
|
||||
}
|
||||
|
||||
func NewZipWorker(zipFile io.Writer, root string) *ZipWorker {
|
||||
return &ZipWorker{
|
||||
zipWriter: zip.NewWriter(zipFile),
|
||||
root: root,
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *ZipWorker) run() error {
|
||||
defer worker.close()
|
||||
return filepath.Walk(worker.root, worker.zipAllFiles)
|
||||
}
|
||||
|
||||
func (worker *ZipWorker) zipAllFiles(path string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
fileWriter, err := worker.zipWriter.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileReader, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(fileWriter, fileReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (worker *ZipWorker) close() {
|
||||
worker.zipWriter.Close()
|
||||
}
|
||||
|
||||
func zipFolder(folder string, file string) error {
|
||||
if _, err := os.Stat(file); err == nil {
|
||||
os.Remove(file)
|
||||
}
|
||||
|
||||
zipFile, err := os.Create(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer zipFile.Close()
|
||||
|
||||
return NewZipWorker(zipFile, folder).run()
|
||||
}
|
||||
86
tools/build/build.go
Normal file
86
tools/build/build.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/v2ray/v2ray-core/tools/git"
|
||||
)
|
||||
|
||||
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.")
|
||||
)
|
||||
|
||||
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)
|
||||
if version != "custom" {
|
||||
os.RemoveAll(targetDir)
|
||||
}
|
||||
err := os.MkdirAll(targetDir, os.ModeDir|0777)
|
||||
return targetDir, err
|
||||
}
|
||||
|
||||
func getTargetFile(goOS GoOS) string {
|
||||
suffix := ""
|
||||
if goOS == Windows {
|
||||
suffix += ".exe"
|
||||
}
|
||||
return "v2ray" + suffix
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
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
|
||||
}
|
||||
fmt.Printf("Building V2Ray (%s) for %s %s\n", version, v2rayOS, v2rayArch)
|
||||
|
||||
targetDir, err := createTargetDirectory(version, v2rayOS, v2rayArch)
|
||||
if err != nil {
|
||||
fmt.Println("Unable to create directory " + targetDir + ": " + err.Error())
|
||||
}
|
||||
|
||||
targetFile := getTargetFile(v2rayOS)
|
||||
err = buildV2Ray(filepath.Join(targetDir, targetFile), version, v2rayOS, v2rayArch)
|
||||
if err != nil {
|
||||
fmt.Println("Unable to build V2Ray: " + err.Error())
|
||||
}
|
||||
|
||||
err = copyConfigFiles(targetDir, v2rayOS)
|
||||
if err != nil {
|
||||
fmt.Println("Unable to copy config files: " + err.Error())
|
||||
}
|
||||
|
||||
if *archive {
|
||||
GOPATH := os.Getenv("GOPATH")
|
||||
binPath := filepath.Join(GOPATH, "bin")
|
||||
err := os.Chdir(binPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to switch to directory (%s): %v\n", binPath, err)
|
||||
}
|
||||
suffix := getSuffix(v2rayOS, v2rayArch)
|
||||
zipFile := "v2ray" + suffix + ".zip"
|
||||
root := filepath.Base(targetDir)
|
||||
err = zipFolder(root, zipFile)
|
||||
if err != nil {
|
||||
fmt.Println("Unable to create archive (%s): %v\n", zipFile, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
43
tools/build/config.go
Normal file
43
tools/build/config.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func copyConfigFile(src, dest string, goOS GoOS) error {
|
||||
content, err := ioutil.ReadFile(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
str := string(content)
|
||||
str = strings.Replace(str, "\r\n", "\n", -1)
|
||||
if goOS == Windows {
|
||||
str = strings.Replace(str, "\n", "\r\n", -1)
|
||||
}
|
||||
return ioutil.WriteFile(dest, []byte(str), 0777)
|
||||
}
|
||||
|
||||
func copyConfigFiles(dir string, goOS GoOS) error {
|
||||
GOPATH := os.Getenv("GOPATH")
|
||||
srcDir := filepath.Join(GOPATH, "src", "github.com", "v2ray", "v2ray-core", "release", "config")
|
||||
src := filepath.Join(srcDir, "vpoint_socks_vmess.json")
|
||||
dest := filepath.Join(dir, "vpoint_socks_vmess.json")
|
||||
if goOS == Windows || goOS == MacOS {
|
||||
dest = filepath.Join(dir, "config.json")
|
||||
}
|
||||
err := copyConfigFile(src, dest, goOS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if goOS == Windows || goOS == MacOS {
|
||||
return nil
|
||||
}
|
||||
|
||||
src = filepath.Join(srcDir, "vpoint_vmess_freedom.json")
|
||||
dest = filepath.Join(dir, "vpoint_vmess_freedom.json")
|
||||
return copyConfigFile(src, dest, goOS)
|
||||
}
|
||||
83
tools/build/env.go
Normal file
83
tools/build/env.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type GoOS string
|
||||
|
||||
const (
|
||||
Windows = GoOS("windows")
|
||||
MacOS = GoOS("darwin")
|
||||
Linux = GoOS("linux")
|
||||
UnknownOS = GoOS("unknown")
|
||||
)
|
||||
|
||||
type GoArch string
|
||||
|
||||
const (
|
||||
X86 = GoArch("386")
|
||||
Amd64 = GoArch("amd64")
|
||||
Arm = GoArch("arm")
|
||||
Arm64 = GoArch("arm64")
|
||||
UnknownArch = GoArch("unknown")
|
||||
)
|
||||
|
||||
func parseOS(rawOS string) GoOS {
|
||||
osStr := strings.ToLower(rawOS)
|
||||
if osStr == "windows" || osStr == "win" {
|
||||
return Windows
|
||||
}
|
||||
if osStr == "darwin" || osStr == "mac" || osStr == "macos" || osStr == "osx" {
|
||||
return MacOS
|
||||
}
|
||||
if osStr == "linux" || osStr == "debian" || osStr == "ubuntu" || osStr == "redhat" || osStr == "centos" {
|
||||
return Linux
|
||||
}
|
||||
return UnknownOS
|
||||
}
|
||||
|
||||
func parseArch(rawArch string) GoArch {
|
||||
archStr := strings.ToLower(rawArch)
|
||||
if archStr == "x86" || archStr == "386" || archStr == "i386" {
|
||||
return X86
|
||||
}
|
||||
if archStr == "amd64" || archStr == "x86-64" || archStr == "x64" {
|
||||
return Amd64
|
||||
}
|
||||
if archStr == "arm" {
|
||||
return Arm
|
||||
}
|
||||
if archStr == "arm64" {
|
||||
return Arm64
|
||||
}
|
||||
return UnknownArch
|
||||
}
|
||||
|
||||
func getSuffix(os GoOS, arch GoArch) string {
|
||||
suffix := "-custom"
|
||||
switch os {
|
||||
case Windows:
|
||||
switch arch {
|
||||
case X86:
|
||||
suffix = "-windows-32"
|
||||
case Amd64:
|
||||
suffix = "-windows-64"
|
||||
}
|
||||
case MacOS:
|
||||
suffix = "-macos"
|
||||
case Linux:
|
||||
switch arch {
|
||||
case X86:
|
||||
suffix = "-linux-32"
|
||||
case Amd64:
|
||||
suffix = "-linux-64"
|
||||
case Arm:
|
||||
suffix = "-linux-arm"
|
||||
case Arm64:
|
||||
suffix = "-linux-arm64"
|
||||
}
|
||||
|
||||
}
|
||||
return suffix
|
||||
}
|
||||
26
tools/build/env_test.go
Normal file
26
tools/build/env_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core/testing/unit"
|
||||
)
|
||||
|
||||
func TestParseOS(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
|
||||
assert.Pointer(parseOS("windows")).Equals(Windows)
|
||||
assert.Pointer(parseOS("macos")).Equals(MacOS)
|
||||
assert.Pointer(parseOS("linux")).Equals(Linux)
|
||||
assert.Pointer(parseOS("test")).Equals(UnknownOS)
|
||||
}
|
||||
|
||||
func TestParseArch(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
|
||||
assert.Pointer(parseArch("x86")).Equals(X86)
|
||||
assert.Pointer(parseArch("x64")).Equals(Amd64)
|
||||
assert.Pointer(parseArch("arm")).Equals(Arm)
|
||||
assert.Pointer(parseArch("arm64")).Equals(Arm64)
|
||||
assert.Pointer(parseArch("test")).Equals(UnknownArch)
|
||||
}
|
||||
25
tools/build/go.go
Normal file
25
tools/build/go.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
func buildV2Ray(targetFile string, version string, goOS GoOS, goArch GoArch) error {
|
||||
ldFlags := "-s"
|
||||
if version != "custom" {
|
||||
year, month, day := time.Now().UTC().Date()
|
||||
today := fmt.Sprintf("%04d%02d%02d", year, int(month), day)
|
||||
ldFlags = ldFlags + " -X github.com/v2ray/v2ray-core.version=" + version + " -X github.com/v2ray/v2ray-core.build=" + today
|
||||
}
|
||||
cmd := exec.Command("go", "build", "-o", targetFile, "-compiler", "gc", "-ldflags", ldFlags, "github.com/v2ray/v2ray-core/release/server")
|
||||
cmd.Env = append(cmd.Env, "GOOS="+string(goOS), "GOARCH="+string(goArch))
|
||||
cmd.Env = append(cmd.Env, os.Environ()...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if len(output) > 0 {
|
||||
fmt.Println(string(output))
|
||||
}
|
||||
return err
|
||||
}
|
||||
46
tools/build/go_test.go
Normal file
46
tools/build/go_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/v2ray/v2ray-core/testing/unit"
|
||||
)
|
||||
|
||||
func TestBuildAndRun(t *testing.T) {
|
||||
assert := unit.Assert(t)
|
||||
|
||||
gopath := os.Getenv("GOPATH")
|
||||
target := filepath.Join(gopath, "src", "v2ray_test")
|
||||
fmt.Println(target)
|
||||
goOS := parseOS(runtime.GOOS)
|
||||
goArch := parseArch(runtime.GOARCH)
|
||||
err := buildV2Ray(target, "v1.0", goOS, goArch)
|
||||
assert.Error(err).IsNil()
|
||||
|
||||
outBuffer := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
errBuffer := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
configFile := filepath.Join(gopath, "src", "github.com", "v2ray", "v2ray-core", "release", "config", "vpoint_socks_vmess.json")
|
||||
cmd := exec.Command(target, "--config="+configFile)
|
||||
cmd.Stdout = outBuffer
|
||||
cmd.Stderr = errBuffer
|
||||
cmd.Start()
|
||||
|
||||
<-time.After(1 * time.Second)
|
||||
cmd.Process.Kill()
|
||||
|
||||
outStr := string(outBuffer.Bytes())
|
||||
errStr := string(errBuffer.Bytes())
|
||||
|
||||
assert.Bool(strings.Contains(outStr, "v1.0")).IsTrue()
|
||||
assert.Int(len(errStr)).Equals(0)
|
||||
|
||||
os.Remove(target)
|
||||
}
|
||||
58
tools/git/git.go
Normal file
58
tools/git/git.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
VersionUndefined = "undefined"
|
||||
)
|
||||
|
||||
func getRepoRoot() string {
|
||||
GOPATH := os.Getenv("GOPATH")
|
||||
return filepath.Join(GOPATH, "src", "github.com", "v2ray", "v2ray-core")
|
||||
}
|
||||
|
||||
func RevParse(args ...string) (string, error) {
|
||||
args = append([]string{"rev-parse"}, args...)
|
||||
cmd := exec.Command("git", args...)
|
||||
cmd.Dir = getRepoRoot()
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(output)), nil
|
||||
}
|
||||
|
||||
func NameRev(args ...string) (string, error) {
|
||||
args = append([]string{"name-rev"}, args...)
|
||||
cmd := exec.Command("git", args...)
|
||||
cmd.Dir = getRepoRoot()
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(output)), nil
|
||||
}
|
||||
|
||||
func RepoVersion(rev string) (string, error) {
|
||||
rev, err := RevParse(rev)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
version, err := NameRev("name-rev", "--tags", "--name-only", rev)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if strings.HasSuffix(version, "^0") {
|
||||
version = version[:len(version)-2]
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func RepoVersionHead() (string, error) {
|
||||
return RepoVersion("HEAD")
|
||||
}
|
||||
31
tools/git/git_test.go
Normal file
31
tools/git/git_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core/testing/unit"
|
||||
)
|
||||
|
||||
func TestRevParse(t *testing.T) {
|
||||
assert := unit.Assert(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) {
|
||||
assert := unit.Assert(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")
|
||||
}
|
||||
9
transport/errors.go
Normal file
9
transport/errors.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
CorruptedPacket = errors.New("Packet is corrupted.")
|
||||
)
|
||||
38
transport/ray/direct.go
Normal file
38
transport/ray/direct.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package ray
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
)
|
||||
|
||||
const (
|
||||
bufferSize = 16
|
||||
)
|
||||
|
||||
// NewRay creates a new Ray for direct traffic transport.
|
||||
func NewRay() Ray {
|
||||
return &directRay{
|
||||
Input: make(chan *alloc.Buffer, bufferSize),
|
||||
Output: make(chan *alloc.Buffer, bufferSize),
|
||||
}
|
||||
}
|
||||
|
||||
type directRay struct {
|
||||
Input chan *alloc.Buffer
|
||||
Output chan *alloc.Buffer
|
||||
}
|
||||
|
||||
func (ray *directRay) OutboundInput() <-chan *alloc.Buffer {
|
||||
return ray.Input
|
||||
}
|
||||
|
||||
func (ray *directRay) OutboundOutput() chan<- *alloc.Buffer {
|
||||
return ray.Output
|
||||
}
|
||||
|
||||
func (ray *directRay) InboundInput() chan<- *alloc.Buffer {
|
||||
return ray.Input
|
||||
}
|
||||
|
||||
func (ray *directRay) InboundOutput() <-chan *alloc.Buffer {
|
||||
return ray.Output
|
||||
}
|
||||
@@ -1,26 +1,9 @@
|
||||
package core
|
||||
package ray
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/alloc"
|
||||
)
|
||||
|
||||
const (
|
||||
bufferSize = 16
|
||||
)
|
||||
|
||||
// Ray is an internal tranport channel bewteen inbound and outbound connection.
|
||||
type Ray struct {
|
||||
Input chan *alloc.Buffer
|
||||
Output chan *alloc.Buffer
|
||||
}
|
||||
|
||||
func NewRay() *Ray {
|
||||
return &Ray{
|
||||
Input: make(chan *alloc.Buffer, bufferSize),
|
||||
Output: make(chan *alloc.Buffer, bufferSize),
|
||||
}
|
||||
}
|
||||
|
||||
// OutboundRay is a transport interface for outbound connections.
|
||||
type OutboundRay interface {
|
||||
// OutboundInput provides a stream for the input of the outbound connection.
|
||||
@@ -46,18 +29,8 @@ type InboundRay interface {
|
||||
InboundOutput() <-chan *alloc.Buffer
|
||||
}
|
||||
|
||||
func (ray *Ray) OutboundInput() <-chan *alloc.Buffer {
|
||||
return ray.Input
|
||||
}
|
||||
|
||||
func (ray *Ray) OutboundOutput() chan<- *alloc.Buffer {
|
||||
return ray.Output
|
||||
}
|
||||
|
||||
func (ray *Ray) InboundInput() chan<- *alloc.Buffer {
|
||||
return ray.Input
|
||||
}
|
||||
|
||||
func (ray *Ray) InboundOutput() <-chan *alloc.Buffer {
|
||||
return ray.Output
|
||||
// Ray is an internal tranport channel bewteen inbound and outbound connection.
|
||||
type Ray interface {
|
||||
InboundRay
|
||||
OutboundRay
|
||||
}
|
||||
Reference in New Issue
Block a user