From 588c43f291246cfbdcdb710f2245e4a45c0a4e64 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 3 Jan 2017 22:46:22 +0100 Subject: [PATCH 1/3] kcp header as wechat video --- main/distro/all/all.go | 1 + tools/conf/transport_authenticators.go | 7 +++ tools/conf/transport_internet.go | 7 +-- .../internet/headers/wechat/config.pb.go | 47 +++++++++++++++++++ .../internet/headers/wechat/config.proto | 10 ++++ transport/internet/headers/wechat/wechat.go | 36 ++++++++++++++ .../internet/headers/wechat/wechat_test.go | 20 ++++++++ 7 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 transport/internet/headers/wechat/config.pb.go create mode 100644 transport/internet/headers/wechat/config.proto create mode 100644 transport/internet/headers/wechat/wechat.go create mode 100644 transport/internet/headers/wechat/wechat_test.go diff --git a/main/distro/all/all.go b/main/distro/all/all.go index e220163628..7c6a7686bb 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -27,4 +27,5 @@ import ( _ "v2ray.com/core/transport/internet/headers/noop" _ "v2ray.com/core/transport/internet/headers/srtp" _ "v2ray.com/core/transport/internet/headers/utp" + _ "v2ray.com/core/transport/internet/headers/wechat" ) diff --git a/tools/conf/transport_authenticators.go b/tools/conf/transport_authenticators.go index b8b4ff6d7d..c96c2b4d26 100644 --- a/tools/conf/transport_authenticators.go +++ b/tools/conf/transport_authenticators.go @@ -7,6 +7,7 @@ import ( "v2ray.com/core/transport/internet/headers/noop" "v2ray.com/core/transport/internet/headers/srtp" "v2ray.com/core/transport/internet/headers/utp" + "v2ray.com/core/transport/internet/headers/wechat" ) type NoOpAuthenticator struct{} @@ -33,6 +34,12 @@ func (UTPAuthenticator) Build() (*serial.TypedMessage, error) { return serial.ToTypedMessage(new(utp.Config)), nil } +type WechatVideoAuthenticator struct{} + +func (WechatVideoAuthenticator) Build() (*serial.TypedMessage, error) { + return serial.ToTypedMessage(new(wechat.VideoConfig)), nil +} + type HTTPAuthenticatorRequest struct { Version string `json:"version"` Method string `json:"method"` diff --git a/tools/conf/transport_internet.go b/tools/conf/transport_internet.go index b34f469abc..3daddf278f 100644 --- a/tools/conf/transport_internet.go +++ b/tools/conf/transport_internet.go @@ -17,9 +17,10 @@ import ( var ( kcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{ - "none": func() interface{} { return new(NoOpAuthenticator) }, - "srtp": func() interface{} { return new(SRTPAuthenticator) }, - "utp": func() interface{} { return new(UTPAuthenticator) }, + "none": func() interface{} { return new(NoOpAuthenticator) }, + "srtp": func() interface{} { return new(SRTPAuthenticator) }, + "utp": func() interface{} { return new(UTPAuthenticator) }, + "wechat-video": func() interface{} { return new(WechatVideoAuthenticator) }, }, "type", "") tcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{ diff --git a/transport/internet/headers/wechat/config.pb.go b/transport/internet/headers/wechat/config.pb.go new file mode 100644 index 0000000000..17f8df933f --- /dev/null +++ b/transport/internet/headers/wechat/config.pb.go @@ -0,0 +1,47 @@ +package wechat + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type VideoConfig struct { +} + +func (m *VideoConfig) Reset() { *m = VideoConfig{} } +func (m *VideoConfig) String() string { return proto.CompactTextString(m) } +func (*VideoConfig) ProtoMessage() {} +func (*VideoConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func init() { + proto.RegisterType((*VideoConfig)(nil), "v2ray.core.transport.internet.headers.wechat.VideoConfig") +} + +func init() { + proto.RegisterFile("v2ray.com/core/transport/internet/headers/wechat/config.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 169 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xb2, 0x2d, 0x33, 0x2a, 0x4a, + 0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x2f, 0x29, 0x4a, 0xcc, 0x2b, + 0x2e, 0xc8, 0x2f, 0x2a, 0xd1, 0xcf, 0xcc, 0x2b, 0x49, 0x2d, 0xca, 0x4b, 0x2d, 0xd1, 0xcf, 0x48, + 0x4d, 0x4c, 0x49, 0x2d, 0x2a, 0xd6, 0x2f, 0x4f, 0x4d, 0xce, 0x48, 0x2c, 0xd1, 0x4f, 0xce, 0xcf, + 0x4b, 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xd2, 0x81, 0x69, 0x2f, 0x4a, 0xd5, + 0x83, 0x6b, 0xd5, 0x83, 0x69, 0xd5, 0x83, 0x6a, 0xd5, 0x83, 0x68, 0x55, 0xe2, 0xe5, 0xe2, 0x0e, + 0xcb, 0x4c, 0x49, 0xcd, 0x77, 0x06, 0x1b, 0xe1, 0x54, 0xc6, 0x65, 0x90, 0x9c, 0x9f, 0xab, 0x47, + 0x8a, 0x11, 0x4e, 0xdc, 0x10, 0xbd, 0x01, 0x20, 0xdb, 0xa3, 0xd8, 0x20, 0x82, 0xab, 0x98, 0x74, + 0xc2, 0x8c, 0x82, 0x12, 0x2b, 0xf5, 0x9c, 0x41, 0x66, 0x84, 0xc0, 0xcd, 0xf0, 0x84, 0x99, 0xe1, + 0x01, 0x35, 0x23, 0x1c, 0xac, 0x3c, 0x89, 0x0d, 0xec, 0x76, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x9b, 0x6a, 0x0f, 0x19, 0xfc, 0x00, 0x00, 0x00, +} diff --git a/transport/internet/headers/wechat/config.proto b/transport/internet/headers/wechat/config.proto new file mode 100644 index 0000000000..819f3e6789 --- /dev/null +++ b/transport/internet/headers/wechat/config.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package v2ray.core.transport.internet.headers.wechat; +option csharp_namespace = "V2Ray.Core.Transport.Internet.Headers.Wechat"; +option go_package = "wechat"; +option java_package = "com.v2ray.core.transport.internet.headers.wechat"; +option java_outer_classname = "ConfigProto"; + +message VideoConfig { +} \ No newline at end of file diff --git a/transport/internet/headers/wechat/wechat.go b/transport/internet/headers/wechat/wechat.go new file mode 100644 index 0000000000..0b1542b91b --- /dev/null +++ b/transport/internet/headers/wechat/wechat.go @@ -0,0 +1,36 @@ +package wechat + +import ( + "v2ray.com/core/common" + "v2ray.com/core/common/dice" + "v2ray.com/core/common/serial" + "v2ray.com/core/transport/internet" +) + +type VideoChat struct { + sn int +} + +func (vc *VideoChat) Size() int { + return 13 +} + +func (vc *VideoChat) Write(b []byte) (int, error) { + vc.sn++ + b = append(b[:0], 0xa1, 0x08) + b = serial.IntToBytes(vc.sn, b) + b = append(b, 0x10, 0x11, 0x18, 0x30, 0x22, 0x30) + return 13, nil +} + +type VideoChatFactory struct{} + +func (VideoChatFactory) Create(rawSettings interface{}) internet.PacketHeader { + return &VideoChat{ + sn: dice.Roll(65535), + } +} + +func init() { + common.Must(internet.RegisterPacketHeader(serial.GetMessageType(new(VideoConfig)), VideoChatFactory{})) +} diff --git a/transport/internet/headers/wechat/wechat_test.go b/transport/internet/headers/wechat/wechat_test.go new file mode 100644 index 0000000000..5bdaa04fcf --- /dev/null +++ b/transport/internet/headers/wechat/wechat_test.go @@ -0,0 +1,20 @@ +package wechat_test + +import ( + "testing" + + "v2ray.com/core/common/buf" + "v2ray.com/core/testing/assert" + . "v2ray.com/core/transport/internet/headers/wechat" +) + +func TestUTPWrite(t *testing.T) { + assert := assert.On(t) + + video := VideoChat{} + + payload := buf.NewLocal(2048) + payload.AppendSupplier(video.Write) + + assert.Int(payload.Len()).Equals(video.Size()) +} From aefa53f827cb911fa71bb5e544c846ac517714e0 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 3 Jan 2017 23:09:51 +0100 Subject: [PATCH 2/3] refine http header --- transport/internet/headers/http/http.go | 59 ++++++++++++------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/transport/internet/headers/http/http.go b/transport/internet/headers/http/http.go index d8c6c79f63..61d4242db0 100644 --- a/transport/internet/headers/http/http.go +++ b/transport/internet/headers/http/http.go @@ -109,15 +109,15 @@ type HttpConn struct { readBuffer *buf.Buffer oneTimeReader Reader oneTimeWriter Writer - isServer bool + errorWriter Writer } -func NewHttpConn(conn net.Conn, reader Reader, writer Writer, isServer bool) *HttpConn { +func NewHttpConn(conn net.Conn, reader Reader, writer Writer, errorWriter Writer) *HttpConn { return &HttpConn{ Conn: conn, oneTimeReader: reader, oneTimeWriter: writer, - isServer: isServer, + errorWriter: errorWriter, } } @@ -157,33 +157,10 @@ func (v *HttpConn) Write(b []byte) (int, error) { // Close implements net.Conn.Close(). func (v *HttpConn) Close() error { - if v.isServer && v.oneTimeWriter != nil { + if v.oneTimeWriter != nil && v.errorWriter != nil { // Connection is being closed but header wasn't sent. This means the client request // is probably not valid. Sending back a server error header in this case. - writer := formResponseHeader(&ResponseConfig{ - Version: &Version{ - Value: "1.1", - }, - Status: &Status{ - Code: "500", - Reason: "Internal Server Error", - }, - Header: []*Header{ - { - Name: "Connection", - Value: []string{"close"}, - }, - { - Name: "Cache-Control", - Value: []string{"private"}, - }, - { - Name: "Content-Length", - Value: []string{"0"}, - }, - }, - }) - writer.Write(v.Conn) + v.errorWriter.Write(v.Conn) } return v.Conn.Close() @@ -248,14 +225,36 @@ func (v HttpAuthenticator) Client(conn net.Conn) net.Conn { if v.config.Response != nil { writer = v.GetClientWriter() } - return NewHttpConn(conn, reader, writer, false) + return NewHttpConn(conn, reader, writer, new(NoOpWriter)) } func (v HttpAuthenticator) Server(conn net.Conn) net.Conn { if v.config.Request == nil && v.config.Response == nil { return conn } - return NewHttpConn(conn, new(HeaderReader), v.GetServerWriter(), true) + return NewHttpConn(conn, new(HeaderReader), v.GetServerWriter(), formResponseHeader(&ResponseConfig{ + Version: &Version{ + Value: "1.1", + }, + Status: &Status{ + Code: "500", + Reason: "Internal Server Error", + }, + Header: []*Header{ + { + Name: "Connection", + Value: []string{"close"}, + }, + { + Name: "Cache-Control", + Value: []string{"private"}, + }, + { + Name: "Content-Length", + Value: []string{"0"}, + }, + }, + })) } type HttpAuthenticatorFactory struct{} From c4e2d998595b50f6259785e67e0e541e5a7abe3a Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 4 Jan 2017 00:43:13 +0100 Subject: [PATCH 3/3] refine socks tcp handling --- proxy/socks/protocol.go | 279 ++++++++++++++++++++++++++++++++++++++++ proxy/socks/server.go | 193 ++++----------------------- 2 files changed, 302 insertions(+), 170 deletions(-) create mode 100644 proxy/socks/protocol.go diff --git a/proxy/socks/protocol.go b/proxy/socks/protocol.go new file mode 100644 index 0000000000..0b334a675e --- /dev/null +++ b/proxy/socks/protocol.go @@ -0,0 +1,279 @@ +package socks + +import ( + "io" + + "v2ray.com/core/common/buf" + "v2ray.com/core/common/errors" + v2net "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" + "v2ray.com/core/common/serial" + "v2ray.com/core/proxy" +) + +const ( + socks5Version = 0x05 + socks4Version = 0x04 + + cmdTCPConnect = 0x01 + cmdTCPBind = 0x02 + cmdUDPPort = 0x03 + + socks4RequestGranted = 90 + socks4RequestRejected = 91 + + authNotRequired = 0x00 + authGssAPI = 0x01 + authPassword = 0x02 + authNoMatchingMethod = 0xFF + + addrTypeIPv4 = 0x01 + addrTypeIPv6 = 0x04 + addrTypeDomain = 0x03 + + statusSuccess = 0x00 + statusCmdNotSupport = 0x07 +) + +type ServerSession struct { + config *ServerConfig + meta *proxy.InboundHandlerMeta +} + +func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) { + buffer := buf.NewLocal(512) + request := new(protocol.RequestHeader) + + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil { + return nil, errors.Base(err).Message("Socks|Server: Insufficient header.") + } + + version := buffer.Byte(0) + if version == socks4Version { + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 6)); err != nil { + return nil, errors.Base(err).Message("Socks|Server: Insufficient header.") + } + port := v2net.PortFromBytes(buffer.BytesRange(2, 4)) + address := v2net.IPAddress(buffer.BytesRange(4, 8)) + _, err := readUntilNull(reader) // user id + if err != nil { + return nil, err + } + if address.IP()[0] == 0x00 { + domain, err := readUntilNull(reader) + if err != nil { + return nil, errors.Base(err).Message("Socks|Server: Failed to read domain for socks 4a.") + } + address = v2net.DomainAddress(domain) + } + + switch buffer.Byte(1) { + case cmdTCPConnect: + request.Command = protocol.RequestCommandTCP + request.Address = address + request.Port = port + request.Version = socks4Version + if err := writeSocks4Response(writer, socks4RequestGranted, address, port); err != nil { + return nil, err + } + return request, nil + default: + writeSocks4Response(writer, socks4RequestRejected, address, port) + return nil, errors.New("Socks|Server: Unsupported command: ", buffer.Byte(1)) + } + } + + if version == socks5Version { + nMethod := int(buffer.Byte(1)) + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, nMethod)); err != nil { + return nil, err + } + + var expectedAuth byte = authNotRequired + if len(s.config.Accounts) > 0 { + expectedAuth = authPassword + } + + if !hasAuthMethod(expectedAuth, buffer.BytesRange(2, 2+nMethod)) { + writeSocks5AuthenticationResponse(writer, authNoMatchingMethod) + return nil, errors.New("Socks|Server: No matching auth method.") + } + + if expectedAuth == authPassword { + username, password, err := readUsernamePassword(reader) + if err != nil { + return nil, errors.Base(err).Message("Socks|Server: Failed to read username or password.") + } + if !s.validate(username, password) { + writeSocks5AuthenticationResponse(writer, 0xFF) + return nil, errors.Base(err).Message("Socks|Server: Invalid username or password.") + } + } + + if err := writeSocks5AuthenticationResponse(writer, 0x00); err != nil { + return nil, err + } + + buffer.Clear() + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil { + return nil, err + } + + cmd := buffer.Byte(1) + if cmd == cmdTCPBind || (cmd == cmdUDPPort && !s.config.UdpEnabled) { + writeSocks5Response(writer, statusCmdNotSupport, v2net.AnyIP, v2net.Port(0)) + return nil, errors.New("Socks|Server: Unsupported command: ", cmd) + } + + switch cmd { + case cmdTCPConnect: + request.Command = protocol.RequestCommandTCP + case cmdUDPPort: + request.Command = protocol.RequestCommandUDP + } + + addrType := buffer.Byte(3) + + buffer.Clear() + + request.Version = socks5Version + switch addrType { + case addrTypeIPv4: + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil { + return nil, err + } + request.Address = v2net.IPAddress(buffer.Bytes()) + case addrTypeIPv6: + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil { + return nil, err + } + request.Address = v2net.IPAddress(buffer.Bytes()) + case addrTypeDomain: + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { + return nil, err + } + domainLength := int(buffer.Byte(0)) + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil { + return nil, err + } + request.Address = v2net.DomainAddress(string(buffer.BytesFrom(-domainLength))) + default: + return nil, errors.New("Socks|Server: Unknown address type: ", addrType) + } + + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil { + return nil, err + } + request.Port = v2net.PortFromBytes(buffer.BytesFrom(-2)) + + responseAddress := v2net.AnyIP + responsePort := v2net.Port(1717) + if request.Command == protocol.RequestCommandUDP { + addr := s.config.Address.AsAddress() + if addr == nil { + addr = v2net.LocalHostIP + } + responseAddress = addr + responsePort = s.meta.Port + } + if err := writeSocks5Response(writer, statusSuccess, responseAddress, responsePort); err != nil { + return nil, err + } + + return request, nil + } + + return nil, errors.New("Socks|Server: Unknown Socks version: ", version) +} + +func (s *ServerSession) validate(username, password string) bool { + p, found := s.config.Accounts[username] + return found && p == password +} + +func readUsernamePassword(reader io.Reader) (string, string, error) { + buffer := buf.NewLocal(512) + defer buffer.Release() + + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil { + return "", "", err + } + nUsername := int(buffer.Byte(1)) + + buffer.Clear() + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, nUsername)); err != nil { + return "", "", err + } + username := buffer.String() + + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { + return "", "", err + } + nPassword := int(buffer.Byte(0)) + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, nPassword)); err != nil { + return "", "", err + } + password := buffer.String() + return username, password, nil +} + +func readUntilNull(reader io.Reader) (string, error) { + var b [256]byte + size := 0 + for { + _, err := reader.Read(b[size : size+1]) + if err != nil { + return "", err + } + if b[size] == 0x00 { + return string(b[:size]), nil + } + size++ + if size == 256 { + return "", errors.New("Socks|Server: Buffer overrun.") + } + } +} + +func hasAuthMethod(expectedAuth byte, authCandidates []byte) bool { + for _, a := range authCandidates { + if a == expectedAuth { + return true + } + } + return false +} + +func writeSocks5AuthenticationResponse(writer io.Writer, auth byte) error { + _, err := writer.Write([]byte{socks5Version, auth}) + return err +} + +func writeSocks5Response(writer io.Writer, errCode byte, address v2net.Address, port v2net.Port) error { + buffer := buf.NewLocal(64) + buffer.AppendBytes(socks5Version, errCode, 0x00 /* reserved */) + switch address.Family() { + case v2net.AddressFamilyIPv4: + buffer.AppendBytes(0x01) + buffer.Append(address.IP()) + case v2net.AddressFamilyIPv6: + buffer.AppendBytes(0x04) + buffer.Append(address.IP()) + case v2net.AddressFamilyDomain: + buffer.AppendBytes(0x03, byte(len(address.Domain()))) + buffer.AppendSupplier(serial.WriteString(address.Domain())) + } + buffer.AppendSupplier(serial.WriteUint16(port.Value())) + + _, err := writer.Write(buffer.Bytes()) + return err +} + +func writeSocks4Response(writer io.Writer, errCode byte, address v2net.Address, port v2net.Port) error { + buffer := buf.NewLocal(32) + buffer.AppendBytes(0x00, errCode) + buffer.AppendSupplier(serial.WriteUint16(port.Value())) + buffer.Append(address.IP()) + _, err := writer.Write(buffer.Bytes()) + return err +} diff --git a/proxy/socks/server.go b/proxy/socks/server.go index e13b2d8ec4..debf8ec0a5 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -10,23 +10,17 @@ import ( "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/bufio" - "v2ray.com/core/common/crypto" "v2ray.com/core/common/errors" "v2ray.com/core/common/log" v2net "v2ray.com/core/common/net" + proto "v2ray.com/core/common/protocol" "v2ray.com/core/common/serial" "v2ray.com/core/common/signal" "v2ray.com/core/proxy" - "v2ray.com/core/proxy/socks/protocol" "v2ray.com/core/transport/internet" "v2ray.com/core/transport/internet/udp" ) -var ( - ErrUnsupportedSocksCommand = errors.New("Unsupported socks command.") - ErrUnsupportedAuthMethod = errors.New("Unsupported auth method.") -) - // Server is a SOCKS 5 proxy server type Server struct { tcpMutex sync.RWMutex @@ -79,7 +73,7 @@ func (v *Server) Close() { } } -// Listen implements InboundHandler.Listen(). +// Start implements InboundHandler.Start(). func (v *Server) Start() error { if v.accepting { return nil @@ -111,153 +105,41 @@ func (v *Server) handleConnection(connection internet.Connection) { reader := bufio.NewReader(timedReader) defer reader.Release() - writer := bufio.NewWriter(connection) - defer writer.Release() - - auth, auth4, err := protocol.ReadAuthentication(reader) - if err != nil && errors.Cause(err) != protocol.Socks4Downgrade { - if errors.Cause(err) != io.EOF { - log.Warning("Socks: failed to read authentication: ", err) - } - return + session := &ServerSession{ + config: v.config, + meta: v.meta, } clientAddr := v2net.DestinationFromAddr(connection.RemoteAddr()) - if err != nil && err == protocol.Socks4Downgrade { - v.handleSocks4(clientAddr, reader, writer, auth4) - } else { - v.handleSocks5(clientAddr, reader, writer, auth) - } -} - -func (v *Server) handleSocks5(clientAddr v2net.Destination, reader *bufio.BufferedReader, writer *bufio.BufferedWriter, auth protocol.Socks5AuthenticationRequest) error { - expectedAuthMethod := protocol.AuthNotRequired - if v.config.AuthType == AuthType_PASSWORD { - expectedAuthMethod = protocol.AuthUserPass - } - - if !auth.HasAuthMethod(expectedAuthMethod) { - authResponse := protocol.NewAuthenticationResponse(protocol.AuthNoMatchingMethod) - err := protocol.WriteAuthentication(writer, authResponse) - writer.Flush() - if err != nil { - log.Warning("Socks: failed to write authentication: ", err) - return err - } - log.Warning("Socks: client doesn't support any allowed auth methods.") - return ErrUnsupportedAuthMethod - } - - authResponse := protocol.NewAuthenticationResponse(expectedAuthMethod) - protocol.WriteAuthentication(writer, authResponse) - err := writer.Flush() - if err != nil { - log.Error("Socks: failed to write authentication: ", err) - return err - } - if v.config.AuthType == AuthType_PASSWORD { - upRequest, err := protocol.ReadUserPassRequest(reader) - if err != nil { - log.Warning("Socks: failed to read username and password: ", err) - return err - } - status := byte(0) - if !v.config.HasAccount(upRequest.Username(), upRequest.Password()) { - status = byte(0xFF) - } - upResponse := protocol.NewSocks5UserPassResponse(status) - err = protocol.WriteUserPassResponse(writer, upResponse) - writer.Flush() - if err != nil { - log.Error("Socks: failed to write user pass response: ", err) - return err - } - if status != byte(0) { - log.Warning("Socks: Invalid user account: ", upRequest.AuthDetail()) - log.Access(clientAddr, "", log.AccessRejected, crypto.ErrAuthenticationFailed) - return crypto.ErrAuthenticationFailed - } - } - request, err := protocol.ReadRequest(reader) + request, err := session.Handshake(reader, connection) if err != nil { - log.Warning("Socks: failed to read request: ", err) - return err - } - - if request.Command == protocol.CmdUdpAssociate && v.config.UdpEnabled { - return v.handleUDP(reader, writer) + log.Access(clientAddr, "", log.AccessRejected, err) + log.Info("Socks|Server: Failed to read request: ", err) + return } - if request.Command == protocol.CmdBind || request.Command == protocol.CmdUdpAssociate { - response := protocol.NewSocks5Response() - response.Error = protocol.ErrorCommandNotSupported - response.Port = v2net.Port(0) - response.SetIPv4([]byte{0, 0, 0, 0}) - - response.Write(writer) - writer.Flush() - if err != nil { - log.Error("Socks: failed to write response: ", err) - return err + if request.Command == proto.RequestCommandTCP { + dest := request.Destination() + session := &proxy.SessionInfo{ + Source: clientAddr, + Destination: dest, + Inbound: v.meta, } - log.Warning("Socks: Unsupported socks command ", request.Command) - return ErrUnsupportedSocksCommand - } - - response := protocol.NewSocks5Response() - response.Error = protocol.ErrorSuccess + log.Info("Socks|Server: TCP Connect request to ", dest) + log.Access(clientAddr, dest, log.AccessAccepted, "") - // Some SOCKS software requires a value other than dest. Let's fake one: - response.Port = v2net.Port(1717) - response.SetIPv4([]byte{0, 0, 0, 0}) - - response.Write(writer) - if err != nil { - log.Error("Socks: failed to write response: ", err) - return err + v.transport(reader, connection, session) + return } - reader.SetBuffered(false) - writer.SetBuffered(false) - - dest := request.Destination() - session := &proxy.SessionInfo{ - Source: clientAddr, - Destination: dest, - Inbound: v.meta, + if request.Command == proto.RequestCommandUDP { + v.handleUDP() + return } - log.Info("Socks: TCP Connect request to ", dest) - log.Access(clientAddr, dest, log.AccessAccepted, "") - - v.transport(reader, writer, session) - return nil } -func (v *Server) handleUDP(reader io.Reader, writer *bufio.BufferedWriter) error { - response := protocol.NewSocks5Response() - response.Error = protocol.ErrorSuccess - - udpAddr := v.udpAddress - - response.Port = udpAddr.Port - switch udpAddr.Address.Family() { - case v2net.AddressFamilyIPv4: - response.SetIPv4(udpAddr.Address.IP()) - case v2net.AddressFamilyIPv6: - response.SetIPv6(udpAddr.Address.IP()) - case v2net.AddressFamilyDomain: - response.SetDomain(udpAddr.Address.Domain()) - } - - response.Write(writer) - err := writer.Flush() - - if err != nil { - log.Error("Socks: failed to write response: ", err) - return err - } - +func (v *Server) handleUDP() error { // The TCP connection closes after v method returns. We need to wait until // the client closes it. // TODO: get notified from UDP part @@ -266,35 +148,6 @@ func (v *Server) handleUDP(reader io.Reader, writer *bufio.BufferedWriter) error return nil } -func (v *Server) handleSocks4(clientAddr v2net.Destination, reader *bufio.BufferedReader, writer *bufio.BufferedWriter, auth protocol.Socks4AuthenticationRequest) error { - result := protocol.Socks4RequestGranted - if auth.Command == protocol.CmdBind { - result = protocol.Socks4RequestRejected - } - socks4Response := protocol.NewSocks4AuthenticationResponse(result, auth.Port, auth.IP[:]) - - socks4Response.Write(writer) - - if result == protocol.Socks4RequestRejected { - log.Warning("Socks: Unsupported socks 4 command ", auth.Command) - log.Access(clientAddr, "", log.AccessRejected, ErrUnsupportedSocksCommand) - return ErrUnsupportedSocksCommand - } - - reader.SetBuffered(false) - writer.SetBuffered(false) - - dest := v2net.TCPDestination(v2net.IPAddress(auth.IP[:]), auth.Port) - session := &proxy.SessionInfo{ - Source: clientAddr, - Destination: dest, - Inbound: v.meta, - } - log.Access(clientAddr, dest, log.AccessAccepted, "") - v.transport(reader, writer, session) - return nil -} - func (v *Server) transport(reader io.Reader, writer io.Writer, session *proxy.SessionInfo) { ray := v.packetDispatcher.DispatchToOutbound(session) input := ray.InboundInput()