├── README.md ├── go.mod ├── .gitignore ├── examples ├── server │ └── main.go └── client │ └── main.go ├── LICENSE ├── server ├── server.go ├── selector.go └── handler.go ├── client ├── selector.go └── client.go ├── rfc1929.txt ├── conn.go ├── socks5.go └── rfc1928.txt /README.md: -------------------------------------------------------------------------------- 1 | gosocks5 2 | ======== 3 | 4 | golang and SOCKSv5 5 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-gost/gosocks5 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | -------------------------------------------------------------------------------- /examples/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "net" 7 | 8 | "github.com/go-gost/gosocks5/server" 9 | ) 10 | 11 | var ( 12 | laddr string 13 | ) 14 | 15 | func init() { 16 | log.SetFlags(log.Lshortfile | log.LstdFlags) 17 | flag.StringVar(&laddr, "l", ":1080", "SOCKS5 server address") 18 | flag.Parse() 19 | } 20 | 21 | func main() { 22 | ln, err := net.Listen("tcp", laddr) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | srv := &server.Server{ 27 | Listener: ln, 28 | } 29 | 30 | log.Fatal(srv.Serve(server.DefaultHandler)) 31 | } 32 | -------------------------------------------------------------------------------- /examples/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | 7 | "github.com/go-gost/gosocks5" 8 | 9 | "github.com/go-gost/gosocks5/client" 10 | ) 11 | 12 | var ( 13 | server string 14 | ) 15 | 16 | func init() { 17 | log.SetFlags(log.Lshortfile | log.LstdFlags) 18 | flag.StringVar(&server, "p", "", "SOCKS5 server address") 19 | flag.Parse() 20 | } 21 | 22 | func main() { 23 | addr, err := gosocks5.NewAddr(flag.Arg(0)) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | conn, err := client.Dial(server) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | defer conn.Close() 33 | 34 | if err := gosocks5.NewRequest(gosocks5.CmdConnect, addr).Write(conn); err != nil { 35 | log.Fatal(err) 36 | } 37 | reply, err := gosocks5.ReadReply(conn) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | log.Println("reply:", reply) 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 郑锐 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | // Server is a SOCKS5 server. 9 | type Server struct { 10 | Listener net.Listener 11 | } 12 | 13 | // Addr returns the address of the server 14 | func (s *Server) Addr() net.Addr { 15 | return s.Listener.Addr() 16 | } 17 | 18 | // Serve serves incoming requests. 19 | func (s *Server) Serve(h Handler, options ...ServerOption) error { 20 | if s.Listener == nil { 21 | ln, err := net.ListenTCP("tcp", nil) 22 | if err != nil { 23 | return err 24 | } 25 | s.Listener = ln 26 | } 27 | if h == nil { 28 | h = DefaultHandler 29 | } 30 | 31 | l := s.Listener 32 | var tempDelay time.Duration 33 | for { 34 | conn, e := l.Accept() 35 | if e != nil { 36 | if ne, ok := e.(net.Error); ok && ne.Temporary() { 37 | if tempDelay == 0 { 38 | tempDelay = 5 * time.Millisecond 39 | } else { 40 | tempDelay *= 2 41 | } 42 | if max := 1 * time.Second; tempDelay > max { 43 | tempDelay = max 44 | } 45 | time.Sleep(tempDelay) 46 | continue 47 | } 48 | return e 49 | } 50 | tempDelay = 0 51 | 52 | go h.Handle(conn) 53 | } 54 | } 55 | 56 | // Close closes the socks5 server 57 | func (s *Server) Close() error { 58 | return s.Listener.Close() 59 | } 60 | 61 | // ServerOptions is options for server. 62 | type ServerOptions struct { 63 | } 64 | 65 | // ServerOption allows a common way to set server options. 66 | type ServerOption func(opts *ServerOptions) 67 | -------------------------------------------------------------------------------- /client/selector.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "net" 5 | "net/url" 6 | 7 | "github.com/go-gost/gosocks5" 8 | ) 9 | 10 | var ( 11 | // DefaultSelector is the default client selector. 12 | DefaultSelector gosocks5.Selector = &clientSelector{} 13 | ) 14 | 15 | type clientSelector struct { 16 | methods []uint8 17 | user *url.Userinfo 18 | } 19 | 20 | func NewClientSelector(user *url.Userinfo, methods ...uint8) gosocks5.Selector { 21 | return &clientSelector{ 22 | methods: methods, 23 | user: user, 24 | } 25 | } 26 | 27 | func (selector *clientSelector) Methods() []uint8 { 28 | return selector.methods 29 | } 30 | 31 | func (selector *clientSelector) AddMethod(methods ...uint8) { 32 | selector.methods = append(selector.methods, methods...) 33 | } 34 | 35 | func (selector *clientSelector) Select(methods ...uint8) (method uint8) { 36 | return 37 | } 38 | 39 | func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (string, net.Conn, error) { 40 | switch method { 41 | case gosocks5.MethodUserPass: 42 | var username, password string 43 | if selector.user != nil { 44 | username = selector.user.Username() 45 | password, _ = selector.user.Password() 46 | } 47 | 48 | req := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password) 49 | if err := req.Write(conn); err != nil { 50 | return "", nil, err 51 | } 52 | resp, err := gosocks5.ReadUserPassResponse(conn) 53 | if err != nil { 54 | return "", nil, err 55 | } 56 | if resp.Status != gosocks5.Succeeded { 57 | return "", nil, gosocks5.ErrAuthFailure 58 | } 59 | return "", conn, nil 60 | 61 | case gosocks5.MethodNoAcceptable: 62 | return "", nil, gosocks5.ErrBadMethod 63 | default: 64 | return "", nil, gosocks5.ErrBadFormat 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "time" 7 | 8 | "github.com/go-gost/gosocks5" 9 | ) 10 | 11 | // Dial connects to the SOCKS5 server. 12 | func Dial(addr string, options ...DialOption) (net.Conn, error) { 13 | opts := &DialOptions{} 14 | for _, o := range options { 15 | o(opts) 16 | } 17 | 18 | conn, err := net.DialTimeout("tcp", addr, opts.Timeout) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | selector := opts.Selector 24 | if selector == nil { 25 | selector = DefaultSelector 26 | } 27 | 28 | cc := gosocks5.ClientConn(conn, selector) 29 | if err := cc.Handleshake(); err != nil { 30 | conn.Close() 31 | return nil, err 32 | } 33 | return cc, nil 34 | } 35 | 36 | // DialContext connects to the SOCKS5 server with the given context. 37 | func DialContext(ctx context.Context, addr string, options ...DialOption) (net.Conn, error) { 38 | opts := &DialOptions{} 39 | for _, o := range options { 40 | o(opts) 41 | } 42 | 43 | conn, err := (&net.Dialer{Timeout: opts.Timeout}).DialContext(ctx, "tcp", addr) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | selector := opts.Selector 49 | if selector == nil { 50 | selector = DefaultSelector 51 | } 52 | 53 | cc := gosocks5.ClientConn(conn, selector) 54 | if err := cc.Handleshake(); err != nil { 55 | conn.Close() 56 | return nil, err 57 | } 58 | return cc, nil 59 | } 60 | 61 | // DialOptions describes the options for Transporter.Dial. 62 | type DialOptions struct { 63 | Selector gosocks5.Selector 64 | Timeout time.Duration 65 | } 66 | 67 | // DialOption allows a common way to set dial options. 68 | type DialOption func(opts *DialOptions) 69 | 70 | func SelectorDialOption(selector gosocks5.Selector) DialOption { 71 | return func(opts *DialOptions) { 72 | opts.Selector = selector 73 | } 74 | } 75 | 76 | func TimeoutDialOption(timeout time.Duration) DialOption { 77 | return func(opts *DialOptions) { 78 | opts.Timeout = timeout 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /server/selector.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "net" 5 | "net/url" 6 | 7 | "github.com/go-gost/gosocks5" 8 | ) 9 | 10 | var ( 11 | // DefaultSelector is the default server selector. 12 | // It only supports No-Auth Method. 13 | DefaultSelector gosocks5.Selector = &serverSelector{} 14 | ) 15 | 16 | type serverSelector struct { 17 | methods []uint8 18 | users []*url.Userinfo 19 | } 20 | 21 | func NewServerSelector(users []*url.Userinfo, methods ...uint8) gosocks5.Selector { 22 | return &serverSelector{ 23 | methods: methods, 24 | users: users, 25 | } 26 | } 27 | 28 | func (selector *serverSelector) Methods() []uint8 { 29 | return selector.methods 30 | } 31 | 32 | func (selector *serverSelector) AddMethod(methods ...uint8) { 33 | selector.methods = append(selector.methods, methods...) 34 | } 35 | 36 | func (selector *serverSelector) Select(methods ...uint8) (method uint8) { 37 | method = gosocks5.MethodNoAuth 38 | 39 | // when user/pass is set, auth is mandatory 40 | if len(selector.users) > 0 { 41 | method = gosocks5.MethodUserPass 42 | } 43 | 44 | return 45 | } 46 | 47 | func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (string, net.Conn, error) { 48 | switch method { 49 | case gosocks5.MethodUserPass: 50 | req, err := gosocks5.ReadUserPassRequest(conn) 51 | if err != nil { 52 | return "", nil, err 53 | } 54 | 55 | valid := false 56 | for _, user := range selector.users { 57 | username := user.Username() 58 | password, _ := user.Password() 59 | if (req.Username == username && req.Password == password) || 60 | (req.Username == username && password == "") || 61 | (username == "" && req.Password == password) { 62 | valid = true 63 | break 64 | } 65 | } 66 | if len(selector.users) > 0 && !valid { 67 | resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure) 68 | if err := resp.Write(conn); err != nil { 69 | return "", nil, err 70 | } 71 | return "", nil, gosocks5.ErrAuthFailure 72 | } 73 | 74 | resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded) 75 | if err := resp.Write(conn); err != nil { 76 | return "", nil, err 77 | } 78 | return "", conn, nil 79 | 80 | case gosocks5.MethodNoAcceptable: 81 | return "", nil, gosocks5.ErrBadMethod 82 | default: 83 | return "", nil, gosocks5.ErrBadFormat 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /rfc1929.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Network Working Group M. Leech 8 | Request for Comments: 1929 Bell-Northern Research Ltd 9 | Category: Standards Track March 1996 10 | 11 | 12 | Username/Password Authentication for SOCKS V5 13 | 14 | Status of this Memo 15 | 16 | This document specifies an Internet standards track protocol for the 17 | Internet community, and requests discussion and suggestions for 18 | improvements. Please refer to the current edition of the "Internet 19 | Official Protocol Standards" (STD 1) for the standardization state 20 | and status of this protocol. Distribution of this memo is unlimited. 21 | 22 | 1. Introduction 23 | 24 | The protocol specification for SOCKS Version 5 specifies a 25 | generalized framework for the use of arbitrary authentication 26 | protocols in the initial socks connection setup. This document 27 | describes one of those protocols, as it fits into the SOCKS Version 5 28 | authentication "subnegotiation". 29 | 30 | Note: 31 | 32 | Unless otherwise noted, the decimal numbers appearing in packet- 33 | format diagrams represent the length of the corresponding field, in 34 | octets. Where a given octet must take on a specific value, the 35 | syntax X'hh' is used to denote the value of the single octet in that 36 | field. When the word 'Variable' is used, it indicates that the 37 | corresponding field has a variable length defined either by an 38 | associated (one or two octet) length field, or by a data type field. 39 | 40 | 2. Initial negotiation 41 | 42 | Once the SOCKS V5 server has started, and the client has selected the 43 | Username/Password Authentication protocol, the Username/Password 44 | subnegotiation begins. This begins with the client producing a 45 | Username/Password request: 46 | 47 | +----+------+----------+------+----------+ 48 | |VER | ULEN | UNAME | PLEN | PASSWD | 49 | +----+------+----------+------+----------+ 50 | | 1 | 1 | 1 to 255 | 1 | 1 to 255 | 51 | +----+------+----------+------+----------+ 52 | 53 | 54 | 55 | 56 | 57 | 58 | Leech Standards Track [Page 1] 59 | 60 | RFC 1929 Username Authentication for SOCKS V5 March 1996 61 | 62 | 63 | The VER field contains the current version of the subnegotiation, 64 | which is X'01'. The ULEN field contains the length of the UNAME field 65 | that follows. The UNAME field contains the username as known to the 66 | source operating system. The PLEN field contains the length of the 67 | PASSWD field that follows. The PASSWD field contains the password 68 | association with the given UNAME. 69 | 70 | The server verifies the supplied UNAME and PASSWD, and sends the 71 | following response: 72 | 73 | +----+--------+ 74 | |VER | STATUS | 75 | +----+--------+ 76 | | 1 | 1 | 77 | +----+--------+ 78 | 79 | A STATUS field of X'00' indicates success. If the server returns a 80 | `failure' (STATUS value other than X'00') status, it MUST close the 81 | connection. 82 | 83 | 3. Security Considerations 84 | 85 | This document describes a subnegotiation that provides authentication 86 | services to the SOCKS protocol. Since the request carries the 87 | password in cleartext, this subnegotiation is not recommended for 88 | environments where "sniffing" is possible and practical. 89 | 90 | 4. Author's Address 91 | 92 | Marcus Leech 93 | Bell-Northern Research Ltd 94 | P.O. Box 3511, Station C 95 | Ottawa, ON 96 | CANADA K1Y 4H7 97 | 98 | Phone: +1 613 763 9145 99 | EMail: mleech@bnr.ca 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | Leech Standards Track [Page 2] 115 | 116 | -------------------------------------------------------------------------------- /conn.go: -------------------------------------------------------------------------------- 1 | package gosocks5 2 | 3 | import ( 4 | "io" 5 | //"log" 6 | "net" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | type Selector interface { 12 | // return supported methods 13 | Methods() []uint8 14 | // select method 15 | Select(methods ...uint8) (method uint8) 16 | // on method selected 17 | OnSelected(method uint8, conn net.Conn) (string, net.Conn, error) 18 | } 19 | 20 | type Conn struct { 21 | c net.Conn 22 | selector Selector 23 | method uint8 24 | isClient bool 25 | clientID string 26 | handshaked bool 27 | handshakeMutex sync.Mutex 28 | handshakeErr error 29 | } 30 | 31 | func ClientConn(conn net.Conn, selector Selector) *Conn { 32 | return &Conn{ 33 | c: conn, 34 | selector: selector, 35 | isClient: true, 36 | } 37 | } 38 | 39 | func ServerConn(conn net.Conn, selector Selector) *Conn { 40 | return &Conn{ 41 | c: conn, 42 | selector: selector, 43 | } 44 | } 45 | 46 | func (conn *Conn) Handleshake() error { 47 | conn.handshakeMutex.Lock() 48 | defer conn.handshakeMutex.Unlock() 49 | 50 | if err := conn.handshakeErr; err != nil { 51 | return err 52 | } 53 | if conn.handshaked { 54 | return nil 55 | } 56 | 57 | if conn.isClient { 58 | conn.handshakeErr = conn.clientHandshake() 59 | } else { 60 | conn.handshakeErr = conn.serverHandshake() 61 | } 62 | 63 | return conn.handshakeErr 64 | } 65 | 66 | func (conn *Conn) clientHandshake() error { 67 | var methods []uint8 68 | var nm int 69 | 70 | if conn.selector != nil { 71 | methods = conn.selector.Methods() 72 | } 73 | nm = len(methods) 74 | if nm == 0 { 75 | nm = 1 76 | } 77 | 78 | b := make([]byte, 2+nm) 79 | b[0] = Ver5 80 | b[1] = uint8(nm) 81 | copy(b[2:], methods) 82 | 83 | if _, err := conn.c.Write(b); err != nil { 84 | return err 85 | } 86 | 87 | if _, err := io.ReadFull(conn.c, b[:2]); err != nil { 88 | return err 89 | } 90 | 91 | if b[0] != Ver5 { 92 | return ErrBadVersion 93 | } 94 | 95 | if conn.selector != nil { 96 | _, c, err := conn.selector.OnSelected(b[1], conn.c) 97 | if err != nil { 98 | return err 99 | } 100 | conn.c = c 101 | } 102 | conn.method = b[1] 103 | //log.Println("method:", conn.method) 104 | conn.handshaked = true 105 | return nil 106 | } 107 | 108 | func (conn *Conn) serverHandshake() error { 109 | methods, err := ReadMethods(conn.c) 110 | if err != nil { 111 | return err 112 | } 113 | 114 | method := MethodNoAuth 115 | if conn.selector != nil { 116 | method = conn.selector.Select(methods...) 117 | } 118 | 119 | if _, err := conn.c.Write([]byte{Ver5, method}); err != nil { 120 | return err 121 | } 122 | 123 | if conn.selector != nil { 124 | id, c, err := conn.selector.OnSelected(method, conn.c) 125 | if err != nil { 126 | return err 127 | } 128 | conn.c = c 129 | conn.clientID = id 130 | } 131 | conn.method = method 132 | //log.Println("method:", method) 133 | conn.handshaked = true 134 | return nil 135 | } 136 | 137 | func (conn *Conn) ID() string { 138 | conn.handshakeMutex.Lock() 139 | defer conn.handshakeMutex.Unlock() 140 | 141 | return conn.clientID 142 | } 143 | 144 | func (conn *Conn) Read(b []byte) (n int, err error) { 145 | if err = conn.Handleshake(); err != nil { 146 | return 147 | } 148 | return conn.c.Read(b) 149 | } 150 | 151 | func (conn *Conn) Write(b []byte) (n int, err error) { 152 | if err = conn.Handleshake(); err != nil { 153 | return 154 | } 155 | return conn.c.Write(b) 156 | } 157 | 158 | func (conn *Conn) Close() error { 159 | return conn.c.Close() 160 | } 161 | 162 | func (conn *Conn) LocalAddr() net.Addr { 163 | return conn.c.LocalAddr() 164 | } 165 | 166 | func (conn *Conn) RemoteAddr() net.Addr { 167 | return conn.c.RemoteAddr() 168 | } 169 | 170 | func (conn *Conn) SetDeadline(t time.Time) error { 171 | return conn.c.SetDeadline(t) 172 | } 173 | 174 | func (conn *Conn) SetReadDeadline(t time.Time) error { 175 | return conn.c.SetReadDeadline(t) 176 | } 177 | 178 | func (conn *Conn) SetWriteDeadline(t time.Time) error { 179 | return conn.c.SetWriteDeadline(t) 180 | } 181 | -------------------------------------------------------------------------------- /server/handler.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net" 7 | "strconv" 8 | "sync" 9 | 10 | "github.com/go-gost/gosocks5" 11 | ) 12 | 13 | var ( 14 | // DefaultHandler is the default server handler. 15 | DefaultHandler Handler 16 | ) 17 | 18 | func init() { 19 | DefaultHandler = &serverHandler{ 20 | selector: DefaultSelector, 21 | } 22 | } 23 | 24 | // Handler is interface for server handler. 25 | type Handler interface { 26 | Handle(conn net.Conn) error 27 | } 28 | 29 | type serverHandler struct { 30 | selector gosocks5.Selector 31 | } 32 | 33 | func (h *serverHandler) Handle(conn net.Conn) error { 34 | conn = gosocks5.ServerConn(conn, h.selector) 35 | req, err := gosocks5.ReadRequest(conn) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | switch req.Cmd { 41 | case gosocks5.CmdConnect: 42 | return h.handleConnect(conn, req) 43 | 44 | case gosocks5.CmdBind: 45 | return h.handleBind(conn, req) 46 | 47 | // case gosocks5.CmdUdp: 48 | // h.handleUDPRelay(conn, req) 49 | 50 | default: 51 | return fmt.Errorf("%d: unsupported command", gosocks5.CmdUnsupported) 52 | } 53 | } 54 | 55 | func (h *serverHandler) handleConnect(conn net.Conn, req *gosocks5.Request) error { 56 | cc, err := net.Dial("tcp", req.Addr.String()) 57 | if err != nil { 58 | rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil) 59 | rep.Write(conn) 60 | return err 61 | } 62 | defer cc.Close() 63 | 64 | rep := gosocks5.NewReply(gosocks5.Succeeded, nil) 65 | if err := rep.Write(conn); err != nil { 66 | return err 67 | } 68 | 69 | return transport(conn, cc) 70 | } 71 | 72 | var ( 73 | trPool = sync.Pool{ 74 | New: func() interface{} { 75 | return make([]byte, 1500) 76 | }, 77 | } 78 | ) 79 | 80 | func (h *serverHandler) handleBind(conn net.Conn, req *gosocks5.Request) error { 81 | addr := req.Addr.String() 82 | bindAddr, _ := net.ResolveTCPAddr("tcp", addr) 83 | ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error 84 | if err != nil { 85 | gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) 86 | return err 87 | } 88 | 89 | socksAddr := toSocksAddr(ln.Addr()) 90 | // Issue: may not reachable when host has multi-interface 91 | socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) 92 | reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr) 93 | if err := reply.Write(conn); err != nil { 94 | ln.Close() 95 | return err 96 | } 97 | 98 | var pconn net.Conn 99 | accept := func() <-chan error { 100 | errc := make(chan error, 1) 101 | 102 | go func() { 103 | defer close(errc) 104 | defer ln.Close() 105 | 106 | c, err := ln.AcceptTCP() 107 | if err != nil { 108 | errc <- err 109 | return 110 | } 111 | pconn = c 112 | }() 113 | 114 | return errc 115 | } 116 | 117 | pc1, pc2 := net.Pipe() 118 | pipe := func() <-chan error { 119 | errc := make(chan error, 1) 120 | 121 | go func() { 122 | defer close(errc) 123 | defer pc1.Close() 124 | 125 | errc <- transport(conn, pc1) 126 | }() 127 | 128 | return errc 129 | } 130 | 131 | defer pc2.Close() 132 | 133 | for { 134 | select { 135 | case err := <-accept(): 136 | if err != nil || pconn == nil { 137 | return err 138 | } 139 | defer pconn.Close() 140 | 141 | reply := gosocks5.NewReply(gosocks5.Succeeded, toSocksAddr(pconn.RemoteAddr())) 142 | if err := reply.Write(pc2); err != nil { 143 | return err 144 | } 145 | 146 | if err = transport(pc2, pconn); err != nil { 147 | } 148 | 149 | return err 150 | case err := <-pipe(): 151 | ln.Close() 152 | return err 153 | } 154 | } 155 | } 156 | 157 | func transport(rw1, rw2 io.ReadWriter) error { 158 | errc := make(chan error, 1) 159 | go func() { 160 | buf := trPool.Get().([]byte) 161 | defer trPool.Put(buf) 162 | 163 | _, err := io.CopyBuffer(rw1, rw2, buf) 164 | errc <- err 165 | }() 166 | 167 | go func() { 168 | buf := trPool.Get().([]byte) 169 | defer trPool.Put(buf) 170 | 171 | _, err := io.CopyBuffer(rw2, rw1, buf) 172 | errc <- err 173 | }() 174 | 175 | err := <-errc 176 | if err != nil && err == io.EOF { 177 | err = nil 178 | } 179 | return err 180 | } 181 | 182 | func toSocksAddr(addr net.Addr) *gosocks5.Addr { 183 | host := "0.0.0.0" 184 | port := 0 185 | if addr != nil { 186 | h, p, _ := net.SplitHostPort(addr.String()) 187 | host = h 188 | port, _ = strconv.Atoi(p) 189 | } 190 | return &gosocks5.Addr{ 191 | Type: gosocks5.AddrIPv4, 192 | Host: host, 193 | Port: uint16(port), 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /socks5.go: -------------------------------------------------------------------------------- 1 | // SOCKS Protocol Version 5 2 | // http://tools.ietf.org/html/rfc1928 3 | // http://tools.ietf.org/html/rfc1929 4 | package gosocks5 5 | 6 | import ( 7 | "bytes" 8 | "encoding/binary" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "net" 13 | "strconv" 14 | ) 15 | 16 | const ( 17 | Ver5 = 5 18 | UserPassVer = 1 19 | ) 20 | 21 | const ( 22 | MethodNoAuth uint8 = iota 23 | MethodGSSAPI 24 | MethodUserPass 25 | // X'03' to X'7F' IANA ASSIGNED 26 | // X'80' to X'FE' RESERVED FOR PRIVATE METHODS 27 | MethodNoAcceptable = 0xFF 28 | ) 29 | 30 | const ( 31 | CmdConnect uint8 = 1 32 | CmdBind uint8 = 2 33 | CmdUdp uint8 = 3 34 | ) 35 | 36 | const ( 37 | AddrIPv4 uint8 = 1 38 | AddrDomain uint8 = 3 39 | AddrIPv6 uint8 = 4 40 | ) 41 | 42 | const ( 43 | Succeeded uint8 = iota 44 | Failure 45 | NotAllowed 46 | NetUnreachable 47 | HostUnreachable 48 | ConnRefused 49 | TTLExpired 50 | CmdUnsupported 51 | AddrUnsupported 52 | ) 53 | 54 | var ( 55 | ErrBadVersion = errors.New("bad version") 56 | ErrBadFormat = errors.New("bad format") 57 | ErrBadAddrType = errors.New("bad address type") 58 | ErrShortBuffer = errors.New("short buffer") 59 | ErrBadMethod = errors.New("bad method") 60 | ErrAuthFailure = errors.New("auth failure") 61 | ) 62 | 63 | /* 64 | Method selection 65 | 66 | +----+----------+----------+ 67 | |VER | NMETHODS | METHODS | 68 | +----+----------+----------+ 69 | | 1 | 1 | 1 to 255 | 70 | +----+----------+----------+ 71 | */ 72 | func ReadMethods(r io.Reader) ([]uint8, error) { 73 | var b [257]byte 74 | 75 | if _, err := io.ReadFull(r, b[:2]); err != nil { 76 | return nil, err 77 | } 78 | 79 | if b[0] != Ver5 { 80 | return nil, ErrBadVersion 81 | } 82 | 83 | if b[1] == 0 { 84 | return nil, ErrBadMethod 85 | } 86 | 87 | if _, err := io.ReadFull(r, b[2:2+int(b[1])]); err != nil { 88 | return nil, err 89 | } 90 | 91 | methods := make([]byte, int(b[1])) 92 | copy(methods, b[2:]) 93 | 94 | return methods, nil 95 | } 96 | 97 | func WriteMethod(method uint8, w io.Writer) error { 98 | _, err := w.Write([]byte{Ver5, method}) 99 | return err 100 | } 101 | 102 | /* 103 | Username/Password authentication request 104 | 105 | +----+------+----------+------+----------+ 106 | |VER | ULEN | UNAME | PLEN | PASSWD | 107 | +----+------+----------+------+----------+ 108 | | 1 | 1 | 1 to 255 | 1 | 1 to 255 | 109 | +----+------+----------+------+----------+ 110 | */ 111 | type UserPassRequest struct { 112 | Version byte 113 | Username string 114 | Password string 115 | } 116 | 117 | func NewUserPassRequest(ver byte, u, p string) *UserPassRequest { 118 | return &UserPassRequest{ 119 | Version: ver, 120 | Username: u, 121 | Password: p, 122 | } 123 | } 124 | 125 | func ReadUserPassRequest(r io.Reader) (*UserPassRequest, error) { 126 | var b [255]byte 127 | 128 | if _, err := io.ReadFull(r, b[:2]); err != nil { 129 | return nil, err 130 | } 131 | 132 | if b[0] != UserPassVer { 133 | return nil, ErrBadVersion 134 | } 135 | 136 | req := &UserPassRequest{ 137 | Version: b[0], 138 | } 139 | 140 | ulen := int(b[1]) 141 | if ulen > 0 { 142 | if _, err := io.ReadFull(r, b[:ulen]); err != nil { 143 | return nil, err 144 | } 145 | } 146 | req.Username = string(b[:ulen]) 147 | 148 | if _, err := io.ReadFull(r, b[:1]); err != nil { 149 | return nil, err 150 | } 151 | plen := int(b[0]) 152 | if plen > 0 { 153 | if _, err := io.ReadFull(r, b[:plen]); err != nil { 154 | return nil, err 155 | } 156 | } 157 | req.Password = string(b[:plen]) 158 | return req, nil 159 | } 160 | 161 | func (req *UserPassRequest) Write(w io.Writer) error { 162 | var b [513]byte 163 | 164 | b[0] = req.Version 165 | ulen := len(req.Username) 166 | b[1] = byte(ulen) 167 | length := 2 + ulen 168 | copy(b[2:length], req.Username) 169 | 170 | plen := len(req.Password) 171 | b[length] = byte(plen) 172 | length++ 173 | copy(b[length:length+plen], req.Password) 174 | length += plen 175 | 176 | _, err := w.Write(b[:length]) 177 | return err 178 | } 179 | 180 | func (req *UserPassRequest) String() string { 181 | return fmt.Sprintf("%d %s:%s", 182 | req.Version, req.Username, req.Password) 183 | } 184 | 185 | /* 186 | Username/Password authentication response 187 | 188 | +----+--------+ 189 | |VER | STATUS | 190 | +----+--------+ 191 | | 1 | 1 | 192 | +----+--------+ 193 | */ 194 | type UserPassResponse struct { 195 | Version byte 196 | Status byte 197 | } 198 | 199 | func NewUserPassResponse(ver, status byte) *UserPassResponse { 200 | return &UserPassResponse{ 201 | Version: ver, 202 | Status: status, 203 | } 204 | } 205 | 206 | func ReadUserPassResponse(r io.Reader) (*UserPassResponse, error) { 207 | var b [2]byte 208 | 209 | if _, err := io.ReadFull(r, b[:]); err != nil { 210 | return nil, err 211 | } 212 | 213 | if b[0] != UserPassVer { 214 | return nil, ErrBadVersion 215 | } 216 | 217 | res := &UserPassResponse{ 218 | Version: b[0], 219 | Status: b[1], 220 | } 221 | 222 | return res, nil 223 | } 224 | 225 | func (res *UserPassResponse) Write(w io.Writer) error { 226 | _, err := w.Write([]byte{res.Version, res.Status}) 227 | return err 228 | } 229 | 230 | func (res *UserPassResponse) String() string { 231 | return fmt.Sprintf("%d %d", 232 | res.Version, res.Status) 233 | } 234 | 235 | /* 236 | Address 237 | 238 | +------+----------+----------+ 239 | | ATYP | ADDR | PORT | 240 | +------+----------+----------+ 241 | | 1 | Variable | 2 | 242 | +------+----------+----------+ 243 | */ 244 | type Addr struct { 245 | Type uint8 246 | Host string 247 | Port uint16 248 | } 249 | 250 | func NewAddr(sa string) (addr *Addr, err error) { 251 | addr = &Addr{} 252 | err = addr.ParseFrom(sa) 253 | return 254 | } 255 | 256 | func (addr *Addr) ParseFrom(saddr string) error { 257 | host, sport, err := net.SplitHostPort(saddr) 258 | if err != nil { 259 | return err 260 | } 261 | port, err := strconv.Atoi(sport) 262 | if err != nil { 263 | return err 264 | } 265 | 266 | addr.Host = host 267 | addr.Port = uint16(port) 268 | addr.checkType() 269 | 270 | return nil 271 | } 272 | 273 | func (addr *Addr) ReadFrom(r io.Reader) (n int64, err error) { 274 | var b [255]byte 275 | 276 | _, err = io.ReadFull(r, b[:1]) 277 | if err != nil { 278 | return 279 | } 280 | addr.Type = b[0] 281 | n++ 282 | 283 | switch addr.Type { 284 | case AddrIPv4: 285 | _, err = io.ReadFull(r, b[:net.IPv4len]) 286 | addr.Host = net.IP(b[:net.IPv4len]).String() 287 | n += net.IPv4len 288 | case AddrIPv6: 289 | _, err = io.ReadFull(r, b[:net.IPv6len]) 290 | addr.Host = net.IP(b[:net.IPv6len]).String() 291 | n += net.IPv6len 292 | case AddrDomain: 293 | if _, err = io.ReadFull(r, b[:1]); err != nil { 294 | return 295 | } 296 | addrlen := int(b[0]) 297 | n++ 298 | 299 | _, err = io.ReadFull(r, b[:addrlen]) 300 | addr.Host = string(b[:addrlen]) 301 | n += int64(addrlen) 302 | default: 303 | err = ErrBadAddrType 304 | return 305 | } 306 | if err != nil { 307 | return 308 | } 309 | 310 | _, err = io.ReadFull(r, b[:2]) 311 | addr.Port = binary.BigEndian.Uint16(b[:2]) 312 | n += 2 313 | 314 | return 315 | } 316 | 317 | func (addr *Addr) WriteTo(w io.Writer) (int64, error) { 318 | var b [259]byte 319 | nn, err := addr.Encode(b[:]) 320 | if err != nil { 321 | return int64(nn), err 322 | } 323 | 324 | nn, err = w.Write(b[:nn]) 325 | return int64(nn), err 326 | } 327 | 328 | func (addr *Addr) Decode(b []byte) error { 329 | _, err := addr.ReadFrom(bytes.NewReader(b)) 330 | return err 331 | } 332 | 333 | func (addr *Addr) Encode(b []byte) (int, error) { 334 | addr.checkType() 335 | 336 | b[0] = addr.Type 337 | pos := 1 338 | switch addr.Type { 339 | case AddrIPv4: 340 | ip4 := net.ParseIP(addr.Host).To4() 341 | if ip4 == nil { 342 | ip4 = net.IPv4zero.To4() 343 | } 344 | pos += copy(b[pos:], ip4) 345 | case AddrIPv6: 346 | ip16 := net.ParseIP(addr.Host).To16() 347 | if ip16 == nil { 348 | ip16 = net.IPv6zero.To16() 349 | } 350 | pos += copy(b[pos:], ip16) 351 | case AddrDomain: 352 | b[pos] = byte(len(addr.Host)) 353 | pos++ 354 | pos += copy(b[pos:], []byte(addr.Host)) 355 | default: 356 | b[0] = AddrIPv4 357 | copy(b[pos:pos+net.IPv4len], net.IPv4zero.To4()) 358 | pos += net.IPv4len 359 | } 360 | binary.BigEndian.PutUint16(b[pos:], addr.Port) 361 | pos += 2 362 | 363 | return pos, nil 364 | } 365 | 366 | func (addr *Addr) checkType() { 367 | switch addr.Type { 368 | case AddrIPv4, AddrIPv6, AddrDomain: 369 | default: 370 | addr.Type = AddrIPv4 371 | if addr.Host != "" { 372 | addr.Type = AddrDomain 373 | if ip := net.ParseIP(addr.Host); ip != nil { 374 | if ip.To4() != nil { 375 | addr.Type = AddrIPv4 376 | } else { 377 | addr.Type = AddrIPv6 378 | } 379 | } 380 | } 381 | } 382 | } 383 | 384 | func (addr *Addr) Length() (n int) { 385 | addr.checkType() 386 | 387 | switch addr.Type { 388 | case AddrIPv4: 389 | n = 7 390 | case AddrIPv6: 391 | n = 19 392 | case AddrDomain: 393 | n = 4 + len(addr.Host) 394 | default: 395 | n = 7 396 | } 397 | return 398 | } 399 | 400 | func (addr *Addr) String() string { 401 | return net.JoinHostPort(addr.Host, strconv.Itoa(int(addr.Port))) 402 | } 403 | 404 | /* 405 | The SOCKSv5 request 406 | 407 | +----+-----+-------+------+----------+----------+ 408 | |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | 409 | +----+-----+-------+------+----------+----------+ 410 | | 1 | 1 | X'00' | 1 | Variable | 2 | 411 | +----+-----+-------+------+----------+----------+ 412 | */ 413 | type Request struct { 414 | Cmd uint8 415 | Addr *Addr 416 | } 417 | 418 | func NewRequest(cmd uint8, addr *Addr) *Request { 419 | return &Request{ 420 | Cmd: cmd, 421 | Addr: addr, 422 | } 423 | } 424 | 425 | func ReadRequest(r io.Reader) (*Request, error) { 426 | var b [262]byte 427 | 428 | n, err := io.ReadAtLeast(r, b[:], 5) 429 | if err != nil { 430 | return nil, err 431 | } 432 | 433 | if b[0] != Ver5 { 434 | return nil, ErrBadVersion 435 | } 436 | 437 | request := &Request{ 438 | Cmd: b[1], 439 | } 440 | 441 | atype := b[3] 442 | length := 0 443 | switch atype { 444 | case AddrIPv4: 445 | length = 10 446 | case AddrIPv6: 447 | length = 22 448 | case AddrDomain: 449 | length = 7 + int(b[4]) 450 | default: 451 | return nil, ErrBadAddrType 452 | } 453 | 454 | if n < length { 455 | if _, err := io.ReadFull(r, b[n:length]); err != nil { 456 | return nil, err 457 | } 458 | } 459 | addr := new(Addr) 460 | if err := addr.Decode(b[3:length]); err != nil { 461 | return nil, err 462 | } 463 | request.Addr = addr 464 | 465 | return request, nil 466 | } 467 | 468 | func (r *Request) Write(w io.Writer) (err error) { 469 | var b [262]byte 470 | 471 | b[0] = Ver5 472 | b[1] = r.Cmd 473 | b[2] = 0 //rsv 474 | b[3] = AddrIPv4 // default 475 | 476 | addr := r.Addr 477 | if addr == nil { 478 | addr = &Addr{} 479 | } 480 | n, _ := addr.Encode(b[3:]) 481 | length := 3 + n 482 | 483 | _, err = w.Write(b[:length]) 484 | return 485 | } 486 | 487 | func (r *Request) String() string { 488 | addr := r.Addr 489 | if addr == nil { 490 | addr = &Addr{} 491 | } 492 | return fmt.Sprintf("5 %d 0 %d %s", 493 | r.Cmd, addr.Type, addr.String()) 494 | } 495 | 496 | /* 497 | The SOCKSv5 reply 498 | 499 | +----+-----+-------+------+----------+----------+ 500 | |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | 501 | +----+-----+-------+------+----------+----------+ 502 | | 1 | 1 | X'00' | 1 | Variable | 2 | 503 | +----+-----+-------+------+----------+----------+ 504 | */ 505 | type Reply struct { 506 | Rep uint8 507 | Addr *Addr 508 | } 509 | 510 | func NewReply(rep uint8, addr *Addr) *Reply { 511 | return &Reply{ 512 | Rep: rep, 513 | Addr: addr, 514 | } 515 | } 516 | 517 | func ReadReply(r io.Reader) (*Reply, error) { 518 | var b [262]byte 519 | 520 | n, err := io.ReadAtLeast(r, b[:], 5) 521 | if err != nil { 522 | return nil, err 523 | } 524 | 525 | if b[0] != Ver5 { 526 | return nil, ErrBadVersion 527 | } 528 | 529 | reply := &Reply{ 530 | Rep: b[1], 531 | } 532 | 533 | atype := b[3] 534 | length := 0 535 | switch atype { 536 | case AddrIPv4: 537 | length = 10 538 | case AddrIPv6: 539 | length = 22 540 | case AddrDomain: 541 | length = 7 + int(b[4]) 542 | default: 543 | return nil, ErrBadAddrType 544 | } 545 | 546 | if n < length { 547 | if _, err := io.ReadFull(r, b[n:length]); err != nil { 548 | return nil, err 549 | } 550 | } 551 | 552 | addr := new(Addr) 553 | if err := addr.Decode(b[3:length]); err != nil { 554 | return nil, err 555 | } 556 | reply.Addr = addr 557 | 558 | return reply, nil 559 | } 560 | 561 | func (r *Reply) Write(w io.Writer) (err error) { 562 | var b [262]byte 563 | 564 | b[0] = Ver5 565 | b[1] = r.Rep 566 | b[2] = 0 //rsv 567 | b[3] = AddrIPv4 // default 568 | length := 10 569 | b[4], b[5], b[6], b[7], b[8], b[9] = 0, 0, 0, 0, 0, 0 // reset address field 570 | 571 | if r.Addr != nil { 572 | n, _ := r.Addr.Encode(b[3:]) 573 | length = 3 + n 574 | } 575 | _, err = w.Write(b[:length]) 576 | 577 | return 578 | } 579 | 580 | func (r *Reply) String() string { 581 | addr := r.Addr 582 | if addr == nil { 583 | addr = &Addr{} 584 | } 585 | return fmt.Sprintf("5 %d 0 %d %s", 586 | r.Rep, addr.Type, addr.String()) 587 | } 588 | 589 | /* 590 | UDP request 591 | 592 | +----+------+------+----------+----------+----------+ 593 | |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | 594 | +----+------+------+----------+----------+----------+ 595 | | 2 | 1 | 1 | Variable | 2 | Variable | 596 | +----+------+------+----------+----------+----------+ 597 | */ 598 | type UDPHeader struct { 599 | Rsv uint16 600 | Frag uint8 601 | Addr *Addr 602 | } 603 | 604 | func NewUDPHeader(rsv uint16, frag uint8, addr *Addr) *UDPHeader { 605 | return &UDPHeader{ 606 | Rsv: rsv, 607 | Frag: frag, 608 | Addr: addr, 609 | } 610 | } 611 | 612 | func (h *UDPHeader) ReadFrom(r io.Reader) (n int64, err error) { 613 | var b [3]byte 614 | 615 | nn, err := io.ReadFull(r, b[:]) 616 | n += int64(nn) 617 | if err != nil { 618 | return 619 | } 620 | 621 | h.Rsv = binary.BigEndian.Uint16(b[:2]) 622 | h.Frag = b[2] 623 | 624 | if h.Addr == nil { 625 | h.Addr = &Addr{} 626 | } 627 | v, err := h.Addr.ReadFrom(r) 628 | n += v 629 | if err != nil { 630 | return 631 | } 632 | 633 | return 634 | } 635 | 636 | func (h *UDPHeader) WriteTo(w io.Writer) (int64, error) { 637 | var b [3]byte 638 | binary.BigEndian.PutUint16(b[:2], h.Rsv) 639 | b[2] = h.Frag 640 | 641 | if n, err := w.Write(b[:]); err != nil { 642 | return int64(n), err 643 | } 644 | 645 | addr := h.Addr 646 | if addr == nil { 647 | addr = &Addr{} 648 | } 649 | nn, err := addr.WriteTo(w) 650 | return 3 + nn, err 651 | } 652 | 653 | func (h *UDPHeader) String() string { 654 | return fmt.Sprintf("%d %d %d %s", 655 | h.Rsv, h.Frag, h.Addr.Type, h.Addr.String()) 656 | } 657 | 658 | type UDPDatagram struct { 659 | Header *UDPHeader 660 | Data []byte 661 | } 662 | 663 | func NewUDPDatagram(header *UDPHeader, data []byte) *UDPDatagram { 664 | return &UDPDatagram{ 665 | Header: header, 666 | Data: data, 667 | } 668 | } 669 | 670 | // ReadFrom reads UDP datagram from r. 671 | func (d *UDPDatagram) ReadFrom(r io.Reader) (n int64, err error) { 672 | if d.Header == nil { 673 | d.Header = &UDPHeader{} 674 | } 675 | n, err = d.Header.ReadFrom(r) 676 | if err != nil { 677 | return 678 | } 679 | 680 | dlen := int64(d.Header.Rsv) 681 | if dlen == 0 { // standard SOCKS5 UDP datagram 682 | buf := bytes.NewBuffer(d.Data[:0]) 683 | if _, err = io.Copy(buf, r); err != nil { 684 | return 685 | } 686 | d.Data = buf.Bytes() 687 | 688 | dlen = int64(len(d.Data)) 689 | } else { // extended feature, for UDP over TCP, using reserved field as data length 690 | if len(d.Data) >= int(dlen) { 691 | d.Data = d.Data[:dlen] 692 | } else { 693 | d.Data = make([]byte, dlen) 694 | } 695 | if _, err = io.ReadFull(r, d.Data[:]); err != nil { 696 | return 697 | } 698 | } 699 | 700 | n += dlen 701 | 702 | return 703 | } 704 | 705 | func (d *UDPDatagram) WriteTo(w io.Writer) (n int64, err error) { 706 | h := d.Header 707 | if h == nil { 708 | h = &UDPHeader{} 709 | } 710 | n, err = h.WriteTo(w) 711 | if err != nil { 712 | return 713 | } 714 | nn, err := w.Write(d.Data) 715 | n += int64(nn) 716 | if err != nil { 717 | return 718 | } 719 | 720 | return 721 | } 722 | -------------------------------------------------------------------------------- /rfc1928.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Network Working Group M. Leech 8 | Request for Comments: 1928 Bell-Northern Research Ltd 9 | Category: Standards Track M. Ganis 10 | International Business Machines 11 | Y. Lee 12 | NEC Systems Laboratory 13 | R. Kuris 14 | Unify Corporation 15 | D. Koblas 16 | Independent Consultant 17 | L. Jones 18 | Hewlett-Packard Company 19 | March 1996 20 | 21 | 22 | SOCKS Protocol Version 5 23 | 24 | Status of this Memo 25 | 26 | This document specifies an Internet standards track protocol for the 27 | Internet community, and requests discussion and suggestions for 28 | improvements. Please refer to the current edition of the "Internet 29 | Official Protocol Standards" (STD 1) for the standardization state 30 | and status of this protocol. Distribution of this memo is unlimited. 31 | 32 | Acknowledgments 33 | 34 | This memo describes a protocol that is an evolution of the previous 35 | version of the protocol, version 4 [1]. This new protocol stems from 36 | active discussions and prototype implementations. The key 37 | contributors are: Marcus Leech: Bell-Northern Research, David Koblas: 38 | Independent Consultant, Ying-Da Lee: NEC Systems Laboratory, LaMont 39 | Jones: Hewlett-Packard Company, Ron Kuris: Unify Corporation, Matt 40 | Ganis: International Business Machines. 41 | 42 | 1. Introduction 43 | 44 | The use of network firewalls, systems that effectively isolate an 45 | organizations internal network structure from an exterior network, 46 | such as the INTERNET is becoming increasingly popular. These 47 | firewall systems typically act as application-layer gateways between 48 | networks, usually offering controlled TELNET, FTP, and SMTP access. 49 | With the emergence of more sophisticated application layer protocols 50 | designed to facilitate global information discovery, there exists a 51 | need to provide a general framework for these protocols to 52 | transparently and securely traverse a firewall. 53 | 54 | 55 | 56 | 57 | 58 | Leech, et al Standards Track [Page 1] 59 | 60 | RFC 1928 SOCKS Protocol Version 5 March 1996 61 | 62 | 63 | There exists, also, a need for strong authentication of such 64 | traversal in as fine-grained a manner as is practical. This 65 | requirement stems from the realization that client-server 66 | relationships emerge between the networks of various organizations, 67 | and that such relationships need to be controlled and often strongly 68 | authenticated. 69 | 70 | The protocol described here is designed to provide a framework for 71 | client-server applications in both the TCP and UDP domains to 72 | conveniently and securely use the services of a network firewall. 73 | The protocol is conceptually a "shim-layer" between the application 74 | layer and the transport layer, and as such does not provide network- 75 | layer gateway services, such as forwarding of ICMP messages. 76 | 77 | 2. Existing practice 78 | 79 | There currently exists a protocol, SOCKS Version 4, that provides for 80 | unsecured firewall traversal for TCP-based client-server 81 | applications, including TELNET, FTP and the popular information- 82 | discovery protocols such as HTTP, WAIS and GOPHER. 83 | 84 | This new protocol extends the SOCKS Version 4 model to include UDP, 85 | and extends the framework to include provisions for generalized 86 | strong authentication schemes, and extends the addressing scheme to 87 | encompass domain-name and V6 IP addresses. 88 | 89 | The implementation of the SOCKS protocol typically involves the 90 | recompilation or relinking of TCP-based client applications to use 91 | the appropriate encapsulation routines in the SOCKS library. 92 | 93 | Note: 94 | 95 | Unless otherwise noted, the decimal numbers appearing in packet- 96 | format diagrams represent the length of the corresponding field, in 97 | octets. Where a given octet must take on a specific value, the 98 | syntax X'hh' is used to denote the value of the single octet in that 99 | field. When the word 'Variable' is used, it indicates that the 100 | corresponding field has a variable length defined either by an 101 | associated (one or two octet) length field, or by a data type field. 102 | 103 | 3. Procedure for TCP-based clients 104 | 105 | When a TCP-based client wishes to establish a connection to an object 106 | that is reachable only via a firewall (such determination is left up 107 | to the implementation), it must open a TCP connection to the 108 | appropriate SOCKS port on the SOCKS server system. The SOCKS service 109 | is conventionally located on TCP port 1080. If the connection 110 | request succeeds, the client enters a negotiation for the 111 | 112 | 113 | 114 | Leech, et al Standards Track [Page 2] 115 | 116 | RFC 1928 SOCKS Protocol Version 5 March 1996 117 | 118 | 119 | authentication method to be used, authenticates with the chosen 120 | method, then sends a relay request. The SOCKS server evaluates the 121 | request, and either establishes the appropriate connection or denies 122 | it. 123 | 124 | Unless otherwise noted, the decimal numbers appearing in packet- 125 | format diagrams represent the length of the corresponding field, in 126 | octets. Where a given octet must take on a specific value, the 127 | syntax X'hh' is used to denote the value of the single octet in that 128 | field. When the word 'Variable' is used, it indicates that the 129 | corresponding field has a variable length defined either by an 130 | associated (one or two octet) length field, or by a data type field. 131 | 132 | The client connects to the server, and sends a version 133 | identifier/method selection message: 134 | 135 | +----+----------+----------+ 136 | |VER | NMETHODS | METHODS | 137 | +----+----------+----------+ 138 | | 1 | 1 | 1 to 255 | 139 | +----+----------+----------+ 140 | 141 | The VER field is set to X'05' for this version of the protocol. The 142 | NMETHODS field contains the number of method identifier octets that 143 | appear in the METHODS field. 144 | 145 | The server selects from one of the methods given in METHODS, and 146 | sends a METHOD selection message: 147 | 148 | +----+--------+ 149 | |VER | METHOD | 150 | +----+--------+ 151 | | 1 | 1 | 152 | +----+--------+ 153 | 154 | If the selected METHOD is X'FF', none of the methods listed by the 155 | client are acceptable, and the client MUST close the connection. 156 | 157 | The values currently defined for METHOD are: 158 | 159 | o X'00' NO AUTHENTICATION REQUIRED 160 | o X'01' GSSAPI 161 | o X'02' USERNAME/PASSWORD 162 | o X'03' to X'7F' IANA ASSIGNED 163 | o X'80' to X'FE' RESERVED FOR PRIVATE METHODS 164 | o X'FF' NO ACCEPTABLE METHODS 165 | 166 | The client and server then enter a method-specific sub-negotiation. 167 | 168 | 169 | 170 | Leech, et al Standards Track [Page 3] 171 | 172 | RFC 1928 SOCKS Protocol Version 5 March 1996 173 | 174 | 175 | Descriptions of the method-dependent sub-negotiations appear in 176 | separate memos. 177 | 178 | Developers of new METHOD support for this protocol should contact 179 | IANA for a METHOD number. The ASSIGNED NUMBERS document should be 180 | referred to for a current list of METHOD numbers and their 181 | corresponding protocols. 182 | 183 | Compliant implementations MUST support GSSAPI and SHOULD support 184 | USERNAME/PASSWORD authentication methods. 185 | 186 | 4. Requests 187 | 188 | Once the method-dependent subnegotiation has completed, the client 189 | sends the request details. If the negotiated method includes 190 | encapsulation for purposes of integrity checking and/or 191 | confidentiality, these requests MUST be encapsulated in the method- 192 | dependent encapsulation. 193 | 194 | The SOCKS request is formed as follows: 195 | 196 | +----+-----+-------+------+----------+----------+ 197 | |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | 198 | +----+-----+-------+------+----------+----------+ 199 | | 1 | 1 | X'00' | 1 | Variable | 2 | 200 | +----+-----+-------+------+----------+----------+ 201 | 202 | Where: 203 | 204 | o VER protocol version: X'05' 205 | o CMD 206 | o CONNECT X'01' 207 | o BIND X'02' 208 | o UDP ASSOCIATE X'03' 209 | o RSV RESERVED 210 | o ATYP address type of following address 211 | o IP V4 address: X'01' 212 | o DOMAINNAME: X'03' 213 | o IP V6 address: X'04' 214 | o DST.ADDR desired destination address 215 | o DST.PORT desired destination port in network octet 216 | order 217 | 218 | The SOCKS server will typically evaluate the request based on source 219 | and destination addresses, and return one or more reply messages, as 220 | appropriate for the request type. 221 | 222 | 223 | 224 | 225 | 226 | Leech, et al Standards Track [Page 4] 227 | 228 | RFC 1928 SOCKS Protocol Version 5 March 1996 229 | 230 | 231 | 5. Addressing 232 | 233 | In an address field (DST.ADDR, BND.ADDR), the ATYP field specifies 234 | the type of address contained within the field: 235 | 236 | o X'01' 237 | 238 | the address is a version-4 IP address, with a length of 4 octets 239 | 240 | o X'03' 241 | 242 | the address field contains a fully-qualified domain name. The first 243 | octet of the address field contains the number of octets of name that 244 | follow, there is no terminating NUL octet. 245 | 246 | o X'04' 247 | 248 | the address is a version-6 IP address, with a length of 16 octets. 249 | 250 | 6. Replies 251 | 252 | The SOCKS request information is sent by the client as soon as it has 253 | established a connection to the SOCKS server, and completed the 254 | authentication negotiations. The server evaluates the request, and 255 | returns a reply formed as follows: 256 | 257 | +----+-----+-------+------+----------+----------+ 258 | |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | 259 | +----+-----+-------+------+----------+----------+ 260 | | 1 | 1 | X'00' | 1 | Variable | 2 | 261 | +----+-----+-------+------+----------+----------+ 262 | 263 | Where: 264 | 265 | o VER protocol version: X'05' 266 | o REP Reply field: 267 | o X'00' succeeded 268 | o X'01' general SOCKS server failure 269 | o X'02' connection not allowed by ruleset 270 | o X'03' Network unreachable 271 | o X'04' Host unreachable 272 | o X'05' Connection refused 273 | o X'06' TTL expired 274 | o X'07' Command not supported 275 | o X'08' Address type not supported 276 | o X'09' to X'FF' unassigned 277 | o RSV RESERVED 278 | o ATYP address type of following address 279 | 280 | 281 | 282 | Leech, et al Standards Track [Page 5] 283 | 284 | RFC 1928 SOCKS Protocol Version 5 March 1996 285 | 286 | 287 | o IP V4 address: X'01' 288 | o DOMAINNAME: X'03' 289 | o IP V6 address: X'04' 290 | o BND.ADDR server bound address 291 | o BND.PORT server bound port in network octet order 292 | 293 | Fields marked RESERVED (RSV) must be set to X'00'. 294 | 295 | If the chosen method includes encapsulation for purposes of 296 | authentication, integrity and/or confidentiality, the replies are 297 | encapsulated in the method-dependent encapsulation. 298 | 299 | CONNECT 300 | 301 | In the reply to a CONNECT, BND.PORT contains the port number that the 302 | server assigned to connect to the target host, while BND.ADDR 303 | contains the associated IP address. The supplied BND.ADDR is often 304 | different from the IP address that the client uses to reach the SOCKS 305 | server, since such servers are often multi-homed. It is expected 306 | that the SOCKS server will use DST.ADDR and DST.PORT, and the 307 | client-side source address and port in evaluating the CONNECT 308 | request. 309 | 310 | BIND 311 | 312 | The BIND request is used in protocols which require the client to 313 | accept connections from the server. FTP is a well-known example, 314 | which uses the primary client-to-server connection for commands and 315 | status reports, but may use a server-to-client connection for 316 | transferring data on demand (e.g. LS, GET, PUT). 317 | 318 | It is expected that the client side of an application protocol will 319 | use the BIND request only to establish secondary connections after a 320 | primary connection is established using CONNECT. In is expected that 321 | a SOCKS server will use DST.ADDR and DST.PORT in evaluating the BIND 322 | request. 323 | 324 | Two replies are sent from the SOCKS server to the client during a 325 | BIND operation. The first is sent after the server creates and binds 326 | a new socket. The BND.PORT field contains the port number that the 327 | SOCKS server assigned to listen for an incoming connection. The 328 | BND.ADDR field contains the associated IP address. The client will 329 | typically use these pieces of information to notify (via the primary 330 | or control connection) the application server of the rendezvous 331 | address. The second reply occurs only after the anticipated incoming 332 | connection succeeds or fails. 333 | 334 | 335 | 336 | 337 | 338 | Leech, et al Standards Track [Page 6] 339 | 340 | RFC 1928 SOCKS Protocol Version 5 March 1996 341 | 342 | 343 | In the second reply, the BND.PORT and BND.ADDR fields contain the 344 | address and port number of the connecting host. 345 | 346 | UDP ASSOCIATE 347 | 348 | The UDP ASSOCIATE request is used to establish an association within 349 | the UDP relay process to handle UDP datagrams. The DST.ADDR and 350 | DST.PORT fields contain the address and port that the client expects 351 | to use to send UDP datagrams on for the association. The server MAY 352 | use this information to limit access to the association. If the 353 | client is not in possesion of the information at the time of the UDP 354 | ASSOCIATE, the client MUST use a port number and address of all 355 | zeros. 356 | 357 | A UDP association terminates when the TCP connection that the UDP 358 | ASSOCIATE request arrived on terminates. 359 | 360 | In the reply to a UDP ASSOCIATE request, the BND.PORT and BND.ADDR 361 | fields indicate the port number/address where the client MUST send 362 | UDP request messages to be relayed. 363 | 364 | Reply Processing 365 | 366 | When a reply (REP value other than X'00') indicates a failure, the 367 | SOCKS server MUST terminate the TCP connection shortly after sending 368 | the reply. This must be no more than 10 seconds after detecting the 369 | condition that caused a failure. 370 | 371 | If the reply code (REP value of X'00') indicates a success, and the 372 | request was either a BIND or a CONNECT, the client may now start 373 | passing data. If the selected authentication method supports 374 | encapsulation for the purposes of integrity, authentication and/or 375 | confidentiality, the data are encapsulated using the method-dependent 376 | encapsulation. Similarly, when data arrives at the SOCKS server for 377 | the client, the server MUST encapsulate the data as appropriate for 378 | the authentication method in use. 379 | 380 | 7. Procedure for UDP-based clients 381 | 382 | A UDP-based client MUST send its datagrams to the UDP relay server at 383 | the UDP port indicated by BND.PORT in the reply to the UDP ASSOCIATE 384 | request. If the selected authentication method provides 385 | encapsulation for the purposes of authenticity, integrity, and/or 386 | confidentiality, the datagram MUST be encapsulated using the 387 | appropriate encapsulation. Each UDP datagram carries a UDP request 388 | header with it: 389 | 390 | 391 | 392 | 393 | 394 | Leech, et al Standards Track [Page 7] 395 | 396 | RFC 1928 SOCKS Protocol Version 5 March 1996 397 | 398 | 399 | +----+------+------+----------+----------+----------+ 400 | |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | 401 | +----+------+------+----------+----------+----------+ 402 | | 2 | 1 | 1 | Variable | 2 | Variable | 403 | +----+------+------+----------+----------+----------+ 404 | 405 | The fields in the UDP request header are: 406 | 407 | o RSV Reserved X'0000' 408 | o FRAG Current fragment number 409 | o ATYP address type of following addresses: 410 | o IP V4 address: X'01' 411 | o DOMAINNAME: X'03' 412 | o IP V6 address: X'04' 413 | o DST.ADDR desired destination address 414 | o DST.PORT desired destination port 415 | o DATA user data 416 | 417 | When a UDP relay server decides to relay a UDP datagram, it does so 418 | silently, without any notification to the requesting client. 419 | Similarly, it will drop datagrams it cannot or will not relay. When 420 | a UDP relay server receives a reply datagram from a remote host, it 421 | MUST encapsulate that datagram using the above UDP request header, 422 | and any authentication-method-dependent encapsulation. 423 | 424 | The UDP relay server MUST acquire from the SOCKS server the expected 425 | IP address of the client that will send datagrams to the BND.PORT 426 | given in the reply to UDP ASSOCIATE. It MUST drop any datagrams 427 | arriving from any source IP address other than the one recorded for 428 | the particular association. 429 | 430 | The FRAG field indicates whether or not this datagram is one of a 431 | number of fragments. If implemented, the high-order bit indicates 432 | end-of-fragment sequence, while a value of X'00' indicates that this 433 | datagram is standalone. Values between 1 and 127 indicate the 434 | fragment position within a fragment sequence. Each receiver will 435 | have a REASSEMBLY QUEUE and a REASSEMBLY TIMER associated with these 436 | fragments. The reassembly queue must be reinitialized and the 437 | associated fragments abandoned whenever the REASSEMBLY TIMER expires, 438 | or a new datagram arrives carrying a FRAG field whose value is less 439 | than the highest FRAG value processed for this fragment sequence. 440 | The reassembly timer MUST be no less than 5 seconds. It is 441 | recommended that fragmentation be avoided by applications wherever 442 | possible. 443 | 444 | Implementation of fragmentation is optional; an implementation that 445 | does not support fragmentation MUST drop any datagram whose FRAG 446 | field is other than X'00'. 447 | 448 | 449 | 450 | Leech, et al Standards Track [Page 8] 451 | 452 | RFC 1928 SOCKS Protocol Version 5 March 1996 453 | 454 | 455 | The programming interface for a SOCKS-aware UDP MUST report an 456 | available buffer space for UDP datagrams that is smaller than the 457 | actual space provided by the operating system: 458 | 459 | o if ATYP is X'01' - 10+method_dependent octets smaller 460 | o if ATYP is X'03' - 262+method_dependent octets smaller 461 | o if ATYP is X'04' - 20+method_dependent octets smaller 462 | 463 | 8. Security Considerations 464 | 465 | This document describes a protocol for the application-layer 466 | traversal of IP network firewalls. The security of such traversal is 467 | highly dependent on the particular authentication and encapsulation 468 | methods provided in a particular implementation, and selected during 469 | negotiation between SOCKS client and SOCKS server. 470 | 471 | Careful consideration should be given by the administrator to the 472 | selection of authentication methods. 473 | 474 | 9. References 475 | 476 | [1] Koblas, D., "SOCKS", Proceedings: 1992 Usenix Security Symposium. 477 | 478 | Author's Address 479 | 480 | Marcus Leech 481 | Bell-Northern Research Ltd 482 | P.O. Box 3511, Stn. C, 483 | Ottawa, ON 484 | CANADA K1Y 4H7 485 | 486 | Phone: (613) 763-9145 487 | EMail: mleech@bnr.ca 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | Leech, et al Standards Track [Page 9] 507 | 508 | --------------------------------------------------------------------------------