Explorar el Código

Version 1.0 alpha

V2Ray hace 10 años
padre
commit
cc3fdb6ef4

+ 4 - 4
io/aes.go

@@ -12,8 +12,8 @@ func NewAesDecryptReader(key []byte, iv []byte, reader io.Reader) (io.Reader, er
 		return nil, err
 	}
 
-	aesMode := cipher.NewCBCDecrypter(aesBlock, iv)
-	return NewCryptionReader(aesMode, reader), nil
+	aesStream := cipher.NewCFBDecrypter(aesBlock, iv)
+	return NewCryptionReader(aesStream, reader), nil
 }
 
 func NewAesEncryptWriter(key []byte, iv []byte, writer io.Writer) (io.Writer, error) {
@@ -22,6 +22,6 @@ func NewAesEncryptWriter(key []byte, iv []byte, writer io.Writer) (io.Writer, er
 		return nil, err
 	}
 
-	aesMode := cipher.NewCBCEncrypter(aesBlock, iv)
-	return NewCryptionWriter(aesMode, writer), nil
+	aesStream := cipher.NewCFBEncrypter(aesBlock, iv)
+	return NewCryptionWriter(aesStream, writer), nil
 }

+ 15 - 22
io/encryption.go

@@ -3,18 +3,20 @@ package io
 import (
 	"crypto/cipher"
 	"io"
+
+	"github.com/v2ray/v2ray-core/log"
 )
 
 // CryptionReader is a general purpose reader that applies
 // block cipher on top of a regular reader.
 type CryptionReader struct {
-	mode   cipher.BlockMode
+	stream cipher.Stream
 	reader io.Reader
 }
 
-func NewCryptionReader(mode cipher.BlockMode, reader io.Reader) *CryptionReader {
+func NewCryptionReader(stream cipher.Stream, reader io.Reader) *CryptionReader {
 	this := new(CryptionReader)
-	this.mode = mode
+	this.stream = stream
 	this.reader = reader
 	return this
 }
@@ -23,32 +25,26 @@ func NewCryptionReader(mode cipher.BlockMode, reader io.Reader) *CryptionReader
 // a multiply of BlockSize()
 func (reader CryptionReader) Read(blocks []byte) (int, error) {
 	nBytes, err := reader.reader.Read(blocks)
-	if err != nil && err != io.EOF {
-		return nBytes, err
+	log.Debug("CryptionReader: Read %d bytes", nBytes)
+	if nBytes > 0 {
+		reader.stream.XORKeyStream(blocks[:nBytes], blocks[:nBytes])
 	}
-	if nBytes < len(blocks) {
-		for i, _ := range blocks[nBytes:] {
-			blocks[i] = 0
-		}
+	if err != nil {
+		log.Error("Error reading blocks: %v", err)
 	}
-	reader.mode.CryptBlocks(blocks, blocks)
 	return nBytes, err
 }
 
-func (reader CryptionReader) BlockSize() int {
-	return reader.mode.BlockSize()
-}
-
 // Cryption writer is a general purpose of byte stream writer that applies
 // block cipher on top of a regular writer.
 type CryptionWriter struct {
-	mode   cipher.BlockMode
+	stream cipher.Stream
 	writer io.Writer
 }
 
-func NewCryptionWriter(mode cipher.BlockMode, writer io.Writer) *CryptionWriter {
+func NewCryptionWriter(stream cipher.Stream, writer io.Writer) *CryptionWriter {
 	this := new(CryptionWriter)
-	this.mode = mode
+	this.stream = stream
 	this.writer = writer
 	return this
 }
@@ -56,10 +52,7 @@ func NewCryptionWriter(mode cipher.BlockMode, writer io.Writer) *CryptionWriter
 // Write writes the give blocks to underlying writer. The length of the blocks
 // must be a multiply of BlockSize()
 func (writer CryptionWriter) Write(blocks []byte) (int, error) {
-	writer.mode.CryptBlocks(blocks, blocks)
+	log.Debug("CryptionWriter writing %d bytes", len(blocks))
+	writer.stream.XORKeyStream(blocks, blocks)
 	return writer.writer.Write(blocks)
 }
-
-func (writer CryptionWriter) BlockSize() int {
-	return writer.mode.BlockSize()
-}

+ 2 - 2
io/vmess/decryptionreader.go

@@ -29,8 +29,8 @@ func NewDecryptionReader(reader io.Reader, key []byte, iv []byte) (*DecryptionRe
 	if err != nil {
 		return nil, err
 	}
-	aesBlockMode := cipher.NewCBCDecrypter(aesCipher, iv)
-	decryptionReader.reader = v2io.NewCryptionReader(aesBlockMode, reader)
+	aesStream := cipher.NewCFBDecrypter(aesCipher, iv)
+	decryptionReader.reader = v2io.NewCryptionReader(aesStream, reader)
 	decryptionReader.buffer = bytes.NewBuffer(make([]byte, 0, 2*blockSize))
 	return decryptionReader, nil
 }

+ 2 - 2
io/vmess/decryptionreader_test.go

@@ -35,10 +35,10 @@ func TestNormalReading(t *testing.T) {
 	aesBlock, err := aes.NewCipher(key)
 	assert.Error(err).IsNil()
 
-	aesMode := cipher.NewCBCEncrypter(aesBlock, iv)
+	aesStream := cipher.NewCFBEncrypter(aesBlock, iv)
 
 	ciphertext := make([]byte, testSize)
-	aesMode.CryptBlocks(ciphertext, plaintext)
+	aesStream.XORKeyStream(ciphertext, plaintext)
 
 	ciphertextcopy := make([]byte, testSize)
 	copy(ciphertextcopy, ciphertext)

+ 2 - 2
io/vmess/vmess.go

@@ -224,8 +224,8 @@ func (w *VMessRequestWriter) Write(writer io.Writer, request *VMessRequest) erro
 	if err != nil {
 		return err
 	}
-	aesMode := cipher.NewCBCEncrypter(aesCipher, make([]byte, blockSize))
-	cWriter := v2io.NewCryptionWriter(aesMode, writer)
+	aesStream := cipher.NewCFBEncrypter(aesCipher, make([]byte, blockSize))
+	cWriter := v2io.NewCryptionWriter(aesStream, writer)
 
 	_, err = writer.Write(buffer[0:encryptionBegin])
 	if err != nil {

+ 14 - 11
log/log.go

@@ -21,30 +21,33 @@ func SetLogLevel(level LogLevel) {
 	logLevel = level
 }
 
-func writeLog(data string, level LogLevel) {
+func writeLog(level LogLevel, prefix, format string, v ...interface{}) string {
 	if level < logLevel {
-		return
+		return ""
 	}
-	log.Print(data)
+	var data string
+	if v == nil || len(v) == 0 {
+		data = format
+	} else {
+		data = fmt.Sprintf(format, v...)
+	}
+	log.Print(prefix + data)
+	return data
 }
 
 func Debug(format string, v ...interface{}) {
-	data := fmt.Sprintf(format, v)
-	writeLog("[Debug]"+data, DebugLevel)
+	writeLog(DebugLevel, "[Debug]", format, v...)
 }
 
 func Info(format string, v ...interface{}) {
-	data := fmt.Sprintf(format, v)
-	writeLog("[Info]"+data, InfoLevel)
+	writeLog(InfoLevel, "[Info]", format, v...)
 }
 
 func Warning(format string, v ...interface{}) {
-	data := fmt.Sprintf(format, v)
-	writeLog("[Warning]"+data, WarningLevel)
+	writeLog(WarningLevel, "[Warning]", format, v...)
 }
 
 func Error(format string, v ...interface{}) error {
-	data := fmt.Sprintf(format, v)
-	writeLog("[Error]"+data, ErrorLevel)
+	data := writeLog(ErrorLevel, "[Error]", format, v...)
 	return errors.New(data)
 }

+ 9 - 7
net/freedom/freedom.go

@@ -2,10 +2,10 @@ package freedom
 
 import (
 	"io"
-	"log"
 	"net"
 
 	"github.com/v2ray/v2ray-core"
+	"github.com/v2ray/v2ray-core/log"
 	v2net "github.com/v2ray/v2ray-core/net"
 )
 
@@ -24,10 +24,9 @@ func (vconn *VFreeConnection) Start(vRay core.OutboundVRay) error {
 	output := vRay.OutboundOutput()
 	conn, err := net.Dial("tcp", vconn.dest.String())
 	if err != nil {
-		log.Print(err)
-		return err
+		return log.Error("Failed to open tcp: %s", vconn.dest.String())
 	}
-	log.Print("Working on tcp:" + vconn.dest.String())
+	log.Debug("Sending outbound tcp: %s", vconn.dest.String())
 
 	finish := make(chan bool, 2)
 	go vconn.DumpInput(conn, input, finish)
@@ -41,22 +40,25 @@ func (vconn *VFreeConnection) DumpInput(conn net.Conn, input <-chan []byte, fini
 		data, open := <-input
 		if !open {
 			finish <- true
+			log.Debug("Freedom finishing input.")
 			break
 		}
-		conn.Write(data)
+		nBytes, err := conn.Write(data)
+		log.Debug("Freedom wrote %d bytes with error %v", nBytes, err)
 	}
 }
 
 func (vconn *VFreeConnection) DumpOutput(conn net.Conn, output chan<- []byte, finish chan<- bool) {
 	for {
-		buffer := make([]byte, 128)
+		buffer := make([]byte, 512)
 		nBytes, err := conn.Read(buffer)
+		log.Debug("Freedom reading %d bytes with error %v", nBytes, err)
 		if err == io.EOF {
 			close(output)
 			finish <- true
+			log.Debug("Freedom finishing output.")
 			break
 		}
-		log.Print(buffer[:nBytes])
 		output <- buffer[:nBytes]
 	}
 }

+ 4 - 0
net/freedom/freedomfactory.go

@@ -11,3 +11,7 @@ type FreedomFactory struct {
 func (factory FreedomFactory) Create(vp *core.VPoint, config []byte, dest v2net.VAddress) (core.OutboundConnectionHandler, error) {
 	return NewVFreeConnection(dest), nil
 }
+
+func init() {
+	core.RegisterOutboundConnectionHandlerFactory("freedom", FreedomFactory{})
+}

+ 16 - 7
net/socks/socks.go

@@ -37,8 +37,10 @@ func NewSocksServer(vp *core.VPoint, rawConfig []byte) *SocksServer {
 func (server *SocksServer) Listen(port uint16) error {
 	listener, err := net.Listen("tcp", ":"+strconv.Itoa(int(port)))
 	if err != nil {
-		return log.Error("Error on listening port %d: %v", port, err)
+		log.Error("Error on listening port %d: %v", port, err)
+		return err
 	}
+	log.Debug("Working on tcp:%d", port)
 	server.accepting = true
 	go server.AcceptConnections(listener)
 	return nil
@@ -48,7 +50,8 @@ func (server *SocksServer) AcceptConnections(listener net.Listener) error {
 	for server.accepting {
 		connection, err := listener.Accept()
 		if err != nil {
-			return log.Error("Error on accepting socks connection: %v", err)
+			log.Error("Error on accepting socks connection: %v", err)
+			return err
 		}
 		go server.HandleConnection(connection)
 	}
@@ -60,7 +63,8 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
 
 	auth, err := socksio.ReadAuthentication(connection)
 	if err != nil {
-		return log.Error("Error on reading authentication: %v", err)
+		log.Error("Error on reading authentication: %v", err)
+		return err
 	}
 
 	expectedAuthMethod := socksio.AuthNotRequired
@@ -76,12 +80,14 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
 		return ErrorAuthenticationFailed
 	}
 
+	log.Debug("Auth accepted, responding auth.")
 	authResponse := socksio.NewAuthenticationResponse(socksio.AuthNotRequired)
 	socksio.WriteAuthentication(connection, authResponse)
 
 	request, err := socksio.ReadRequest(connection)
 	if err != nil {
-		return log.Error("Error on reading socks request: %v", err)
+		log.Error("Error on reading socks request: %v", err)
+		return err
 	}
 
 	response := socksio.NewSocks5Response()
@@ -105,6 +111,7 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
 	case socksio.AddrTypeDomain:
 		response.Domain = request.Domain
 	}
+	log.Debug("Socks response port = %d", response.Port)
 	socksio.WriteResponse(connection, response)
 
 	ray := server.vPoint.NewInboundConnectionAccepted(request.Destination())
@@ -121,12 +128,13 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
 
 func (server *SocksServer) dumpInput(conn net.Conn, input chan<- []byte, finish chan<- bool) {
 	for {
-		buffer := make([]byte, 256)
+		buffer := make([]byte, 512)
 		nBytes, err := conn.Read(buffer)
 		log.Debug("Reading %d bytes, with error %v", nBytes, err)
 		if err == io.EOF {
 			close(input)
 			finish <- true
+			log.Debug("Socks finishing input.")
 			break
 		}
 		input <- buffer[:nBytes]
@@ -138,10 +146,11 @@ func (server *SocksServer) dumpOutput(conn net.Conn, output <-chan []byte, finis
 		buffer, open := <-output
 		if !open {
 			finish <- true
+			log.Debug("Socks finishing output")
 			break
 		}
-		nBytes, _ := conn.Write(buffer)
-		log.Debug("Writing %d bytes", nBytes)
+		nBytes, err := conn.Write(buffer)
+		log.Debug("Writing %d bytes with error %v", nBytes, err)
 	}
 }
 

+ 19 - 19
net/socks/socks_test.go

@@ -11,29 +11,29 @@ import (
 
 func TestSocksTcpConnect(t *testing.T) {
 	t.Skip("Not ready yet.")
-  /*
-	assert := unit.Assert(t)
+	/*
+		assert := unit.Assert(t)
 
-	port := uint16(12384)
+		port := uint16(12384)
 
-	uuid := "2418d087-648d-4990-86e8-19dca1d006d3"
-	vid, err := core.UUIDToVID(uuid)
-	assert.Error(err).IsNil()
+		uuid := "2418d087-648d-4990-86e8-19dca1d006d3"
+		vid, err := core.UUIDToVID(uuid)
+		assert.Error(err).IsNil()
 
-	config := core.VConfig{
-		port,
-		[]core.VUser{core.VUser{vid}},
-		"",
-		[]core.VNext{}}
+		config := core.VConfig{
+			port,
+			[]core.VUser{core.VUser{vid}},
+			"",
+			[]core.VNext{}}
 
-	och := new(mocks.FakeOutboundConnectionHandler)
-	och.Data2Send = bytes.NewBuffer(make([]byte, 1024))
-	och.Data2Return = []byte("The data to be returned to socks server.")
+		och := new(mocks.FakeOutboundConnectionHandler)
+		och.Data2Send = bytes.NewBuffer(make([]byte, 1024))
+		och.Data2Return = []byte("The data to be returned to socks server.")
 
-	vpoint, err := core.NewVPoint(&config, SocksServerFactory{}, och)
-	assert.Error(err).IsNil()
+		vpoint, err := core.NewVPoint(&config, SocksServerFactory{}, och)
+		assert.Error(err).IsNil()
 
-	err = vpoint.Start()
-	assert.Error(err).IsNil()
-  */
+		err = vpoint.Start()
+		assert.Error(err).IsNil()
+	*/
 }

+ 4 - 0
net/socks/socksfactory.go

@@ -10,3 +10,7 @@ type SocksServerFactory struct {
 func (factory SocksServerFactory) Create(vp *core.VPoint, config []byte) (core.InboundConnectionHandler, error) {
 	return NewSocksServer(vp, config), nil
 }
+
+func init() {
+	core.RegisterInboundConnectionHandlerFactory("socks", SocksServerFactory{})
+}

+ 25 - 6
net/vmess/config.go

@@ -4,11 +4,22 @@ import (
 	"encoding/json"
 
 	"github.com/v2ray/v2ray-core"
+	"github.com/v2ray/v2ray-core/log"
 	v2net "github.com/v2ray/v2ray-core/net"
 )
 
+type VMessUser struct {
+	Id    string `json:"id"`
+	Email string `json:"email"`
+}
+
+func (u *VMessUser) ToVUser() (core.VUser, error) {
+	id, err := core.UUIDToVID(u.Id)
+	return core.VUser{id}, err
+}
+
 type VMessInboundConfig struct {
-	AllowedClients []core.VUser `json:"clients"`
+	AllowedClients []VMessUser `json:"clients"`
 }
 
 func loadInboundConfig(rawConfig []byte) (VMessInboundConfig, error) {
@@ -18,19 +29,27 @@ func loadInboundConfig(rawConfig []byte) (VMessInboundConfig, error) {
 }
 
 type VNextConfig struct {
-	Address string       `json:"address"`
-	Port    uint16       `json:"port"`
-	Users   []core.VUser `json:"users"`
+	Address string      `json:"address"`
+	Port    uint16      `json:"port"`
+	Users   []VMessUser `json:"users"`
 }
 
 func (config VNextConfig) ToVNextServer() VNextServer {
+	users := make([]core.VUser, 0, len(config.Users))
+	for _, user := range config.Users {
+		vuser, err := user.ToVUser()
+		if err != nil {
+			panic(log.Error("Failed to convert %v to VUser.", user))
+		}
+		users = append(users, vuser)
+	}
 	return VNextServer{
 		v2net.DomainAddress(config.Address, config.Port),
-		config.Users}
+		users}
 }
 
 type VMessOutboundConfig struct {
-	VNextList []VNextConfig
+	VNextList []VNextConfig `json:"vnext"`
 }
 
 func loadOutboundConfig(rawConfig []byte) (VMessOutboundConfig, error) {

+ 1 - 1
net/vmess/vmess.go

@@ -1,5 +1,5 @@
 package vmess
 
 const (
-	BufferSize = 256
+	BufferSize = 512
 )

+ 29 - 11
net/vmess/vmessin.go

@@ -4,6 +4,7 @@ import (
 	"crypto/md5"
 	"io"
 	"net"
+	"strconv"
 
 	"github.com/v2ray/v2ray-core"
 	v2io "github.com/v2ray/v2ray-core/io"
@@ -24,10 +25,10 @@ func NewVMessInboundHandler(vp *core.VPoint, clients *core.VUserSet) *VMessInbou
 	return handler
 }
 
-func (handler *VMessInboundHandler) Listen(port uint8) error {
-	listener, err := net.Listen("tcp", ":"+string(port))
+func (handler *VMessInboundHandler) Listen(port uint16) error {
+	listener, err := net.Listen("tcp", ":"+strconv.Itoa(int(port)))
 	if err != nil {
-		return err
+		return log.Error("Unable to listen tcp:%d", port)
 	}
 	handler.accepting = true
 	go handler.AcceptConnections(listener)
@@ -39,7 +40,7 @@ func (handler *VMessInboundHandler) AcceptConnections(listener net.Listener) err
 	for handler.accepting {
 		connection, err := listener.Accept()
 		if err != nil {
-			return err
+			return log.Error("Failed to accpet connection: %s", err.Error())
 		}
 		go handler.HandleConnection(connection)
 	}
@@ -54,9 +55,14 @@ func (handler *VMessInboundHandler) HandleConnection(connection net.Conn) error
 	if err != nil {
 		return err
 	}
+	log.Debug("Received request for %s", request.Address.String())
 
 	response := vmessio.NewVMessResponse(request)
-	connection.Write(response[:])
+	nBytes, err := connection.Write(response[:])
+	log.Debug("Writing VMess response %v", response)
+	if err != nil {
+		return log.Error("Failed to write VMess response (%d bytes): %v", nBytes, err)
+	}
 
 	requestKey := request.RequestKey[:]
 	requestIV := request.RequestIV[:]
@@ -65,12 +71,12 @@ func (handler *VMessInboundHandler) HandleConnection(connection net.Conn) error
 
 	requestReader, err := v2io.NewAesDecryptReader(requestKey, requestIV, connection)
 	if err != nil {
-		return err
+		return log.Error("Failed to create decrypt reader: %v", err)
 	}
 
 	responseWriter, err := v2io.NewAesEncryptWriter(responseKey[:], responseIV[:], connection)
 	if err != nil {
-		return err
+		return log.Error("Failed to create encrypt writer: %v", err)
 	}
 
 	ray := handler.vPoint.NewInboundConnectionAccepted(request.Address)
@@ -89,8 +95,10 @@ func (handler *VMessInboundHandler) dumpInput(reader io.Reader, input chan<- []b
 	for {
 		buffer := make([]byte, BufferSize)
 		nBytes, err := reader.Read(buffer)
+		log.Debug("VMessInbound: Reading %d bytes with error %v", nBytes, err)
 		if err == io.EOF {
 			close(input)
+			log.Debug("VMessInbound finishing input.")
 			finish <- true
 			break
 		}
@@ -103,9 +111,11 @@ func (handler *VMessInboundHandler) dumpOutput(writer io.Writer, output <-chan [
 		buffer, open := <-output
 		if !open {
 			finish <- true
+			log.Debug("VMessInbound finishing output.")
 			break
 		}
-		writer.Write(buffer)
+		nBytes, err := writer.Write(buffer)
+		log.Debug("VmessInbound: Wrote %d bytes with error %v", nBytes, err)
 	}
 }
 
@@ -118,14 +128,22 @@ func (handler *VMessInboundHandler) waitForFinish(finish <-chan bool) {
 type VMessInboundHandlerFactory struct {
 }
 
-func (factory *VMessInboundHandlerFactory) Create(vp *core.VPoint, rawConfig []byte) *VMessInboundHandler {
+func (factory *VMessInboundHandlerFactory) Create(vp *core.VPoint, rawConfig []byte) (core.InboundConnectionHandler, error) {
 	config, err := loadInboundConfig(rawConfig)
 	if err != nil {
 		panic(log.Error("Failed to load VMess inbound config: %v", err))
 	}
 	allowedClients := core.NewVUserSet()
-	for _, user := range config.AllowedClients {
+	for _, client := range config.AllowedClients {
+		user, err := client.ToVUser()
+		if err != nil {
+			panic(log.Error("Failed to parse user id %s: %v", client.Id, err))
+		}
 		allowedClients.AddUser(user)
 	}
-	return NewVMessInboundHandler(vp, allowedClients)
+	return NewVMessInboundHandler(vp, allowedClients), nil
+}
+
+func init() {
+	core.RegisterInboundConnectionHandlerFactory("vmess", &VMessInboundHandlerFactory{})
 }

+ 38 - 7
net/vmess/vmessout.go

@@ -62,26 +62,48 @@ func (handler *VMessOutboundHandler) Start(ray core.OutboundVRay) error {
 	request.Command = byte(0x01)
 	request.Address = handler.dest
 
-	conn, err := net.Dial("tcp", vNextAddress.String())
+	go handler.startCommunicate(request, vNextAddress, ray)
+	return nil
+}
+
+func (handler *VMessOutboundHandler) startCommunicate(request *vmessio.VMessRequest, dest v2net.VAddress, ray core.OutboundVRay) error {
+	conn, err := net.Dial("tcp", dest.String())
+	log.Debug("VMessOutbound dialing tcp: %s", dest.String())
 	if err != nil {
+		log.Error("Failed to open tcp (%s): %v", dest.String(), err)
 		return err
 	}
 	defer conn.Close()
 
 	requestWriter := vmessio.NewVMessRequestWriter()
-	requestWriter.Write(conn, request)
+	err = requestWriter.Write(conn, request)
+	if err != nil {
+		log.Error("Failed to write VMess request: %v", err)
+		return err
+	}
 
 	requestKey := request.RequestKey[:]
 	requestIV := request.RequestIV[:]
 	responseKey := md5.Sum(requestKey)
 	responseIV := md5.Sum(requestIV)
 
+	response := vmessio.VMessResponse{}
+	nBytes, err := conn.Read(response[:])
+	if err != nil {
+		log.Error("Failed to read VMess response (%d bytes): %v", nBytes, err)
+		return err
+	}
+	log.Debug("Got response %v", response)
+	// TODO: check response
+
 	encryptRequestWriter, err := v2io.NewAesEncryptWriter(requestKey, requestIV, conn)
 	if err != nil {
+		log.Error("Failed to create encrypt writer: %v", err)
 		return err
 	}
-	responseReader, err := v2io.NewAesDecryptReader(responseKey[:], responseIV[:], conn)
+	decryptResponseReader, err := v2io.NewAesDecryptReader(responseKey[:], responseIV[:], conn)
 	if err != nil {
+		log.Error("Failed to create decrypt reader: %v", err)
 		return err
 	}
 
@@ -90,7 +112,7 @@ func (handler *VMessOutboundHandler) Start(ray core.OutboundVRay) error {
 	finish := make(chan bool, 2)
 
 	go handler.dumpInput(encryptRequestWriter, input, finish)
-	go handler.dumpOutput(responseReader, output, finish)
+	go handler.dumpOutput(decryptResponseReader, output, finish)
 	handler.waitForFinish(finish)
 	return nil
 }
@@ -99,9 +121,11 @@ func (handler *VMessOutboundHandler) dumpOutput(reader io.Reader, output chan<-
 	for {
 		buffer := make([]byte, BufferSize)
 		nBytes, err := reader.Read(buffer)
+		log.Debug("VMessOutbound: Reading %d bytes, with error %v", nBytes, err)
 		if err == io.EOF {
 			close(output)
 			finish <- true
+			log.Debug("VMessOutbound finishing output.")
 			break
 		}
 		output <- buffer[:nBytes]
@@ -113,9 +137,11 @@ func (handler *VMessOutboundHandler) dumpInput(writer io.Writer, input <-chan []
 		buffer, open := <-input
 		if !open {
 			finish <- true
+			log.Debug("VMessOutbound finishing input.")
 			break
 		}
-		writer.Write(buffer)
+		nBytes, err := writer.Write(buffer)
+		log.Debug("VMessOutbound: Wrote %d bytes with error %v", nBytes, err)
 	}
 }
 
@@ -123,12 +149,13 @@ func (handler *VMessOutboundHandler) waitForFinish(finish <-chan bool) {
 	for i := 0; i < 2; i++ {
 		<-finish
 	}
+	log.Debug("Finishing waiting for VMessOutbound ending.")
 }
 
 type VMessOutboundHandlerFactory struct {
 }
 
-func (factory *VMessOutboundHandlerFactory) Create(vp *core.VPoint, rawConfig []byte, destination v2net.VAddress) *VMessOutboundHandler {
+func (factory *VMessOutboundHandlerFactory) Create(vp *core.VPoint, rawConfig []byte, destination v2net.VAddress) (core.OutboundConnectionHandler, error) {
 	config, err := loadOutboundConfig(rawConfig)
 	if err != nil {
 		panic(log.Error("Failed to load VMess outbound config: %v", err))
@@ -137,5 +164,9 @@ func (factory *VMessOutboundHandlerFactory) Create(vp *core.VPoint, rawConfig []
 	for _, server := range config.VNextList {
 		servers = append(servers, server.ToVNextServer())
 	}
-	return NewVMessOutboundHandler(vp, servers, destination)
+	return NewVMessOutboundHandler(vp, servers, destination), nil
+}
+
+func init() {
+	core.RegisterOutboundConnectionHandlerFactory("vmess", &VMessOutboundHandlerFactory{})
 }

+ 3 - 0
release/config/in_socks.json

@@ -0,0 +1,3 @@
+{
+  "auth": "noauth"
+}

+ 5 - 0
release/config/in_vmess.json

@@ -0,0 +1,5 @@
+{
+  "clients": [
+    {"id": "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"}
+  ]
+}

+ 11 - 0
release/config/out_vmess.json

@@ -0,0 +1,11 @@
+{
+  "vnext": [
+    {
+      "address": "127.0.0.1",
+      "port": 27183,
+      "users": [
+        {"id": "ad937d9d-6e23-4a5a-ba23-bce5092a7c51"}
+      ]
+    }
+  ]
+}

+ 11 - 0
release/config/vpoint_socks_vmess.json

@@ -0,0 +1,11 @@
+{
+  "port": 1080,
+  "inbound": {
+    "protocol": "socks",
+    "file": "in_socks.json"
+  },
+  "outbound": {
+    "protocol": "vmess",
+    "file": "out_vmess.json"
+  }
+}

+ 11 - 0
release/config/vpoint_vmess_freedom.json

@@ -0,0 +1,11 @@
+{
+  "port": 27183,
+  "inbound": {
+    "protocol": "vmess",
+    "file": "in_vmess.json"
+  },
+  "outbound": {
+    "protocol": "freedom",
+    "file": ""
+  }
+}

+ 52 - 0
release/server/main.go

@@ -1,5 +1,57 @@
 package main
 
+import (
+	"flag"
+	"io/ioutil"
+	"path"
+
+	"github.com/v2ray/v2ray-core"
+	"github.com/v2ray/v2ray-core/log"
+
+	_ "github.com/v2ray/v2ray-core/net/freedom"
+	_ "github.com/v2ray/v2ray-core/net/socks"
+	_ "github.com/v2ray/v2ray-core/net/vmess"
+)
+
+var (
+	configFile = flag.String("config", "", "Config file for this VPoint server.")
+)
+
 func main() {
+	flag.Parse()
+
+	log.SetLogLevel(log.DebugLevel)
+
+	if configFile == nil || len(*configFile) == 0 {
+		panic(log.Error("Config file is not set."))
+	}
+	rawVConfig, err := ioutil.ReadFile(*configFile)
+	if err != nil {
+		panic(log.Error("Failed to read config file (%s): %v", *configFile, err))
+	}
+	vconfig, err := core.LoadVConfig(rawVConfig)
+	if err != nil {
+		panic(log.Error("Failed to parse VConfig: %v", err))
+	}
+
+	if !path.IsAbs(vconfig.InboundConfig.File) && len(vconfig.InboundConfig.File) > 0 {
+		vconfig.InboundConfig.File = path.Join(path.Dir(*configFile), vconfig.InboundConfig.File)
+	}
+
+	if !path.IsAbs(vconfig.OutboundConfig.File) && len(vconfig.OutboundConfig.File) > 0 {
+		vconfig.OutboundConfig.File = path.Join(path.Dir(*configFile), vconfig.OutboundConfig.File)
+	}
+
+	vPoint, err := core.NewVPoint(vconfig)
+	if err != nil {
+		panic(log.Error("Failed to create VPoint server: %v", err))
+	}
+
+	err = vPoint.Start()
+	if err != nil {
+		log.Error("Error starting VPoint server.")
+	}
 
+	finish := make(chan bool)
+	<-finish
 }