From 262eb4f9858f82899301364798853077063a98a3 Mon Sep 17 00:00:00 2001 From: V2Ray Date: Fri, 11 Sep 2015 14:12:09 +0200 Subject: [PATCH] sample socks proxy --- io/socks/socks.go | 20 +++++++++----- net/freedom/freedom.go | 7 ++++- net/freedom/freedomfactory.go | 13 ++++++++++ net/socks/socks.go | 36 ++++++++++++++++++++++--- net/socks/socks_test.go | 39 ++++++++++++++++++++++++++++ net/socks/socksfactory.go | 4 +-- net/vdest.go | 6 ++++- release/server/socks/main.go | 37 ++++++++++++++++++++++++++ testing/mocks/fakeoutboundhandler.go | 33 +++++++++++++++++++++++ vpoint.go | 20 +++----------- 10 files changed, 184 insertions(+), 31 deletions(-) create mode 100644 net/freedom/freedomfactory.go create mode 100644 net/socks/socks_test.go create mode 100644 release/server/socks/main.go create mode 100644 testing/mocks/fakeoutboundhandler.go diff --git a/io/socks/socks.go b/io/socks/socks.go index 9da41b1b7..bbbb73982 100644 --- a/io/socks/socks.go +++ b/io/socks/socks.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "fmt" "io" + _ "log" v2net "github.com/v2ray/v2ray-core/net" ) @@ -25,7 +26,7 @@ type Socks5AuthenticationRequest struct { } func (request *Socks5AuthenticationRequest) HasAuthMethod(method byte) bool { - for i := byte(0); i < request.nMethods; i++ { + for i := 0; i < int(request.nMethods); i++ { if request.authMethods[i] == method { return true } @@ -140,17 +141,22 @@ func ReadRequest(reader io.Reader) (request *Socks5Request, err error) { return } case AddrTypeDomain: - buffer = make([]byte, 257) - nBytes, err = reader.Read(buffer) + buffer = make([]byte, 256) + nBytes, err = reader.Read(buffer[0:1]) + if err != nil { + return + } + domainLength := buffer[0] + nBytes, err = reader.Read(buffer[:domainLength]) if err != nil { return } - domainLength := buffer[0] - if nBytes != int(domainLength)+1 { - err = fmt.Errorf("Unable to read domain") + + if nBytes != int(domainLength) { + err = fmt.Errorf("Unable to read domain with %d bytes, expecting %d bytes", nBytes, domainLength) return } - request.Domain = string(buffer[1 : domainLength+1]) + request.Domain = string(buffer[:domainLength]) case AddrTypeIPv6: nBytes, err = reader.Read(request.IPv6[:]) if err != nil { diff --git a/net/freedom/freedom.go b/net/freedom/freedom.go index aaca4ad6e..48ec23fd5 100644 --- a/net/freedom/freedom.go +++ b/net/freedom/freedom.go @@ -1,8 +1,9 @@ -package tcp +package freedom import ( "io" "net" + "log" "github.com/v2ray/v2ray-core" v2net "github.com/v2ray/v2ray-core/net" @@ -25,8 +26,10 @@ 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 } + log.Print("Working on tcp:" + vconn.dest.String()) finish := make(chan bool, 2) go vconn.DumpInput(conn, input, finish) @@ -51,9 +54,11 @@ func (vconn *VFreeConnection) DumpOutput(conn net.Conn, output chan<- []byte, fi buffer := make([]byte, 128) nBytes, err := conn.Read(buffer) if err == io.EOF { + close(output) finish <- true break } + log.Print(buffer[:nBytes]) output <- buffer[:nBytes] } } diff --git a/net/freedom/freedomfactory.go b/net/freedom/freedomfactory.go new file mode 100644 index 000000000..fa4efe3a9 --- /dev/null +++ b/net/freedom/freedomfactory.go @@ -0,0 +1,13 @@ +package freedom + +import ( + "github.com/v2ray/v2ray-core" + v2net "github.com/v2ray/v2ray-core/net" +) + +type FreedomFactory struct { +} + +func (factory FreedomFactory) Create(vp *core.VPoint, dest v2net.VAddress) (core.OutboundConnectionHandler, error) { + return NewVFreeConnection(vp, dest), nil +} diff --git a/net/socks/socks.go b/net/socks/socks.go index e150e724d..8ba6c4f8a 100644 --- a/net/socks/socks.go +++ b/net/socks/socks.go @@ -3,7 +3,9 @@ package socks import ( "errors" "io" + "log" "net" + "strconv" "github.com/v2ray/v2ray-core" socksio "github.com/v2ray/v2ray-core/io/socks" @@ -26,8 +28,8 @@ func NewSocksServer(vp *core.VPoint) *SocksServer { return server } -func (server *SocksServer) Listen(port uint8) error { - listener, err := net.Listen("tcp", ":"+string(port)) +func (server *SocksServer) Listen(port uint16) error { + listener, err := net.Listen("tcp", ":"+strconv.Itoa(int(port))) if err != nil { return err } @@ -40,6 +42,7 @@ func (server *SocksServer) AcceptConnections(listener net.Listener) error { for server.accepting { connection, err := listener.Accept() if err != nil { + log.Print(err) return err } go server.HandleConnection(connection) @@ -52,10 +55,14 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error { auth, err := socksio.ReadAuthentication(connection) if err != nil { + log.Print(err) return err } + log.Print(auth) - if auth.HasAuthMethod(socksio.AuthNotRequired) { + if !auth.HasAuthMethod(socksio.AuthNotRequired) { + // TODO send response with FF + log.Print(ErrorAuthenticationFailed) return ErrorAuthenticationFailed } @@ -64,15 +71,33 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error { request, err := socksio.ReadRequest(connection) if err != nil { + log.Print(err) return err } + + response := socksio.NewSocks5Response() if request.Command == socksio.CmdBind || request.Command == socksio.CmdUdpAssociate { response := socksio.NewSocks5Response() response.Error = socksio.ErrorCommandNotSupported socksio.WriteResponse(connection, response) + log.Print(ErrorCommandNotSupported) return ErrorCommandNotSupported } + + response.Error = socksio.ErrorSuccess + response.Port = request.Port + response.AddrType = request.AddrType + switch response.AddrType { + case socksio.AddrTypeIPv4: + copy(response.IPv4[:], request.IPv4[:]) + case socksio.AddrTypeIPv6: + copy(response.IPv6[:], request.IPv6[:]) + case socksio.AddrTypeDomain: + response.Domain = request.Domain + } + socksio.WriteResponse(connection, response) + ray := server.vPoint.NewInboundConnectionAccepted(request.Destination()) input := ray.InboundInput() @@ -90,7 +115,9 @@ func (server *SocksServer) dumpInput(conn net.Conn, input chan<- []byte, finish for { buffer := make([]byte, 256) nBytes, err := conn.Read(buffer) + log.Printf("Reading %d bytes, with error %v", nBytes, err) if err == io.EOF { + close(input) finish <- true break } @@ -105,7 +132,8 @@ func (server *SocksServer) dumpOutput(conn net.Conn, output <-chan []byte, finis finish <- true break } - conn.Write(buffer) + nBytes, _ := conn.Write(buffer) + log.Printf("Writing %d bytes", nBytes) } } diff --git a/net/socks/socks_test.go b/net/socks/socks_test.go new file mode 100644 index 000000000..f4350db15 --- /dev/null +++ b/net/socks/socks_test.go @@ -0,0 +1,39 @@ +package socks + +import ( + "bytes" + "testing" + + "github.com/v2ray/v2ray-core" + "github.com/v2ray/v2ray-core/testing/mocks" + "github.com/v2ray/v2ray-core/testing/unit" +) + +func TestSocksTcpConnect(t *testing.T) { + t.Skip("Not ready yet.") + + assert := unit.Assert(t) + + port := 12384 + + uuid := "2418d087-648d-4990-86e8-19dca1d006d3" + vid, err := core.UUIDToVID(uuid) + assert.Error(err).IsNil() + + config := VConfig{ + port, + []core.VUser{VUser{vid}}, + "", + []core.VNext{}} + + och := new(FakeOutboundConnectionHandler) + och.Data2Send = bytes.NewBuffer(make([]byte, 1024)) + och.Data2Return = []byte("The data to be returned to socks server.") + + vpoint, err := NewVPoint(&config, SocksServerFactory{}, och) + assert.Error(err).IsNil() + + err = vpoint.Start() + assert.Error(err).IsNil() + +} diff --git a/net/socks/socksfactory.go b/net/socks/socksfactory.go index 6a6b9d5f5..928bb2c74 100644 --- a/net/socks/socksfactory.go +++ b/net/socks/socksfactory.go @@ -7,6 +7,6 @@ import ( type SocksServerFactory struct { } -func (factory *SocksServerFactory) Create(vp *core.VPoint) *SocksServer { - return NewSocksServer(vp) +func (factory SocksServerFactory) Create(vp *core.VPoint) (core.InboundConnectionHandler, error) { + return NewSocksServer(vp), nil } diff --git a/net/vdest.go b/net/vdest.go index 1dda4f6f3..ea8449282 100644 --- a/net/vdest.go +++ b/net/vdest.go @@ -50,7 +50,11 @@ func (addr VAddress) String() string { var host string switch addr.Type { case AddrTypeIP: - host = addr.IP.String() + host = addr.IP.String() + if len(addr.IP) == net.IPv6len { + host = "[" + host + "]" + } + case AddrTypeDomain: host = addr.Domain default: diff --git a/release/server/socks/main.go b/release/server/socks/main.go new file mode 100644 index 000000000..e728748f1 --- /dev/null +++ b/release/server/socks/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "log" + + "github.com/v2ray/v2ray-core" + "github.com/v2ray/v2ray-core/net/freedom" + "github.com/v2ray/v2ray-core/net/socks" +) + +func main() { + port := uint16(8888) + + uuid := "2418d087-648d-4990-86e8-19dca1d006d3" + vid, err := core.UUIDToVID(uuid) + if err != nil { + log.Fatal(err) + } + + config := core.VConfig{ + port, + []core.VUser{core.VUser{vid}}, + "", + []core.VNext{}} + + vpoint, err := core.NewVPoint(&config, socks.SocksServerFactory{}, freedom.FreedomFactory{}) + if err != nil { + log.Fatal(err) + } + err = vpoint.Start() + if err != nil { + log.Fatal(err) + } + + finish := make(chan bool) + <-finish +} diff --git a/testing/mocks/fakeoutboundhandler.go b/testing/mocks/fakeoutboundhandler.go new file mode 100644 index 000000000..a05412e03 --- /dev/null +++ b/testing/mocks/fakeoutboundhandler.go @@ -0,0 +1,33 @@ +package mocks + +import ( + "bytes" + + "github.com/v2ray/v2ray-core" + v2net "github.com/v2ray/v2ray-core/net" +) + +type FakeOutboundConnectionHandler struct { + Data2Send bytes.Buffer + Data2Return []byte + Destination v2net.VAddress +} + +func (handler *FakeOutboundConnectionHandler) Start(ray core.OutboundVRay) error { + input := ray.OutboundInput() + output := ray.OutboundOutput() + + output <- handler.Data2Return + for { + data, open := <-input + if !open { + break + } + handler.Data2Send.Write(data) + } + return nil +} + +func (handler *FakeOutboundConnectionHandler) Create(vPoint *core.VPoint, dest v2net.VAddress) (core.OutboundConnectionHandler, error) { + return handler, nil +} diff --git a/vpoint.go b/vpoint.go index ea855e175..34ac3495e 100644 --- a/vpoint.go +++ b/vpoint.go @@ -16,7 +16,7 @@ type VPoint struct { // NewVPoint returns a new VPoint server based on given configuration. // The server is not started at this point. -func NewVPoint(config *VConfig) (*VPoint, error) { +func NewVPoint(config *VConfig, ichFactory InboundConnectionHandlerFactory, ochFactory OutboundConnectionHandlerFactory) (*VPoint, error) { var vpoint = new(VPoint) vpoint.Config = *config vpoint.UserSet = NewVUserSet() @@ -24,6 +24,9 @@ func NewVPoint(config *VConfig) (*VPoint, error) { for _, user := range vpoint.Config.AllowedClients { vpoint.UserSet.AddUser(user) } + + vpoint.ichFactory = ichFactory + vpoint.ochFactory = ochFactory return vpoint, nil } @@ -59,21 +62,6 @@ func (vp *VPoint) Start() error { } func (vp *VPoint) NewInboundConnectionAccepted(destination v2net.VAddress) InboundVRay { - /* - vNextLen := len(vp.Config.VNextList) - if vNextLen > 0 { - vNextIndex := rand.Intn(vNextLen) - vNext := vp.Config.VNextList[vNextIndex] - vNextUser := dest.User - vNextUserLen := len(vNext.Users) - if vNextUserLen > 0 { - vNextUserIndex = rand.Intn(vNextUserLen) - vNextUser = vNext.Users[vNextUserIndex] - } - newDest := VDestination{"tcp", vNext.ServerAddress, vNextUser} - dest = newDest - }*/ - ray := NewVRay() // TODO: handle error och, _ := vp.ochFactory.Create(vp, destination)