Loading pt.go +3 −2 Original line number Diff line number Diff line Loading @@ -128,10 +128,11 @@ // Pluggable Transport through SOCKS proxy: // https://gitweb.torproject.org/torspec.git/tree/proposals/232-pluggable-transports-through-proxy.txt // // The package implements a SOCKS4a server sufficient for a Tor client transport // The package implements a SOCKS5 server sufficient for a Tor client transport // plugin. // // http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol // https://www.ietf.org/rfc/rfc1928.txt // https://www.ietf.org/rfc/rfc1929.txt package pt import ( Loading socks.go +324 −62 Original line number Diff line number Diff line Loading @@ -9,11 +9,40 @@ import ( ) const ( socksVersion = 0x04 socksVersion = 0x05 socksAuthNoneRequired = 0x00 socksAuthUsernamePassword = 0x02 socksAuthNoAcceptableMethods = 0xff socksCmdConnect = 0x01 socksResponseVersion = 0x00 socksRequestGranted = 0x5a socksRequestRejected = 0x5b socksRsv = 0x00 socksAtypeV4 = 0x01 socksAtypeDomainName = 0x03 socksAtypeV6 = 0x04 socksAuthRFC1929Ver = 0x01 socksAuthRFC1929Success = 0x00 socksAuthRFC1929Fail = 0x01 socksRepSucceeded = 0x00 // "general SOCKS server failure" SocksRepGeneralFailure = 0x01 // "connection not allowed by ruleset" SocksRepConnectionNotAllowed = 0x02 // "Network unreachable" SocksRepNetworkUnreachable = 0x03 // "Host unreachable" SocksRepHostUnreachable = 0x04 // "Connection refused" SocksRepConnectionRefused = 0x05 // "TTL expired" SocksRepTTLExpired = 0x06 // "Command not supported" SocksRepCommandNotSupported = 0x07 // "Address type not supported" SocksRepAddressNotSupported = 0x08 ) // Put a sanity timeout on how long we wait for a SOCKS request. Loading @@ -25,6 +54,8 @@ type SocksRequest struct { Target string // The userid string sent by the client. Username string // The password string sent by the client. Password string // The parsed contents of Username as a key–value mapping. Args Args } Loading @@ -36,15 +67,23 @@ type SocksConn struct { } // Send a message to the proxy client that access to the given address is // granted. If the IP field inside addr is not an IPv4 address, the IP portion // of the response will be four zero bytes. // granted. Addr is ignored, and "0.0.0.0:0" is always sent back for // BND.ADDR/BND.PORT in the SOCKS response. func (conn *SocksConn) Grant(addr *net.TCPAddr) error { return sendSocks4aResponseGranted(conn, addr) return sendSocks5ResponseGranted(conn) } // Send a message to the proxy client that access was rejected or failed. // Send a message to the proxy client that access was rejected or failed. This // sends back a "General Failure" error code. RejectReason should be used if // more specific error reporting is desired. func (conn *SocksConn) Reject() error { return sendSocks4aResponseRejected(conn) return conn.RejectReason(SocksRepGeneralFailure) } // Send a message to the proxy client that access was rejected, with the // specific error code indicating the reason behind the rejection. func (conn *SocksConn) RejectReason(reason byte) error { return sendSocks5ResponseRejected(conn, reason) } // SocksListener wraps a net.Listener in order to read a SOCKS request on Accept. Loading Loading @@ -140,7 +179,7 @@ retry: conn.Close() goto retry } conn.Req, err = readSocks4aConnect(conn) conn.Req, err = socks5Handshake(conn) if err != nil { conn.Close() goto retry Loading @@ -153,58 +192,251 @@ retry: return conn, nil } // Returns "socks4", suitable to be included in a call to Cmethod. // Returns "socks5", suitable to be included in a call to Cmethod. func (ln *SocksListener) Version() string { return "socks4" return "socks5" } // Read a SOCKS4a connect request. Returns a SocksRequest. func readSocks4aConnect(s io.Reader) (req SocksRequest, err error) { r := bufio.NewReader(s) // socks5handshake conducts the SOCKS5 handshake up to the point where the // client command is read and the proxy must open the outgoing connection. // Returns a SocksRequest. func socks5Handshake(s io.ReadWriter) (req SocksRequest, err error) { rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s)) var h [8]byte _, err = io.ReadFull(r, h[:]) if err != nil { // Negotiate the authentication method. var method byte if method, err = socksNegotiateAuth(rw); err != nil { return } if h[0] != socksVersion { err = fmt.Errorf("SOCKS header had version 0x%02x, not 0x%02x", h[0], socksVersion) // Authenticate the client. if err = socksAuthenticate(rw, method, &req); err != nil { return } if h[1] != socksCmdConnect { err = fmt.Errorf("SOCKS header had command 0x%02x, not 0x%02x", h[1], socksCmdConnect) // Read the command. err = socksReadCommand(rw, &req) return } var usernameBytes []byte usernameBytes, err = r.ReadBytes('\x00') if err != nil { // socksNegotiateAuth negotiates the authentication method and returns the // selected method as a byte. On negotiation failures an error is returned. func socksNegotiateAuth(rw *bufio.ReadWriter) (method byte, err error) { // Validate the version. if err = socksReadByteVerify(rw, "version", socksVersion); err != nil { return } req.Username = string(usernameBytes[:len(usernameBytes)-1]) req.Args, err = parseClientParameters(req.Username) if err != nil { // Read the number of methods. var nmethods byte if nmethods, err = socksReadByte(rw); err != nil { return } var port int var host string // Read the methods. var methods []byte if methods, err = socksReadBytes(rw, int(nmethods)); err != nil { return } port = int(h[2])<<8 | int(h[3])<<0 if h[4] == 0 && h[5] == 0 && h[6] == 0 && h[7] != 0 { var hostBytes []byte hostBytes, err = r.ReadBytes('\x00') if err != nil { // Pick the most "suitable" method. method = socksAuthNoAcceptableMethods for _, m := range methods { switch m { case socksAuthNoneRequired: // Pick Username/Password over None if the client happens to // send both. if method == socksAuthNoAcceptableMethods { method = m } case socksAuthUsernamePassword: method = m } } // Send the negotiated method. var msg [2]byte msg[0] = socksVersion msg[1] = method if _, err = rw.Writer.Write(msg[:]); err != nil { return } if err = socksFlushBuffers(rw); err != nil { return } return } // socksAuthenticate authenticates the client via the chosen authentication // mechanism. func socksAuthenticate(rw *bufio.ReadWriter, method byte, req *SocksRequest) (err error) { switch method { case socksAuthNoneRequired: // Straight into reading the connect. case socksAuthUsernamePassword: if err = socksAuthRFC1929(rw, req); err != nil { return } case socksAuthNoAcceptableMethods: err = fmt.Errorf("SOCKS method select had no compatible methods") return default: err = fmt.Errorf("SOCKS method select picked a unsupported method 0x%02x", method) return } if err = socksFlushBuffers(rw); err != nil { return } return } // socksAuthRFC1929 authenticates the client via RFC 1929 username/password // auth. As a design decision any valid username/password is accepted as this // field is primarily used as an out-of-band argument passing mechanism for // pluggable transports. func socksAuthRFC1929(rw *bufio.ReadWriter, req *SocksRequest) (err error) { sendErrResp := func() { // Swallow the write/flush error here, we are going to close the // connection and the original failure is more useful. resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Fail} rw.Write(resp[:]) socksFlushBuffers(rw) } // Validate the fixed parts of the command message. if err = socksReadByteVerify(rw, "auth version", socksAuthRFC1929Ver); err != nil { sendErrResp() return } // Read the username. var ulen byte if ulen, err = socksReadByte(rw); err != nil { return } if ulen < 1 { sendErrResp() err = fmt.Errorf("RFC1929 username with 0 length") return } var uname []byte if uname, err = socksReadBytes(rw, int(ulen)); err != nil { return } req.Username = string(uname) // Read the password. var plen byte if plen, err = socksReadByte(rw); err != nil { return } if plen < 1 { sendErrResp() err = fmt.Errorf("RFC1929 password with 0 length") return } host = string(hostBytes[:len(hostBytes)-1]) var passwd []byte if passwd, err = socksReadBytes(rw, int(plen)); err != nil { return } if !(plen == 1 && passwd[0] == 0x00) { // tor will set the password to 'NUL' if there are no arguments. req.Password = string(passwd) } // Mash the username/password together and parse it as a pluggable // transport argument string. if req.Args, err = parseClientParameters(req.Username + req.Password); err != nil { sendErrResp() } else { host = net.IPv4(h[4], h[5], h[6], h[7]).String() resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Success} _, err = rw.Write(resp[:]) } return } // socksReadCommand reads a SOCKS5 client command and parses out the relevant // fields into a SocksRequest. Only CMD_CONNECT is supported. func socksReadCommand(rw *bufio.ReadWriter, req *SocksRequest) (err error) { sendErrResp := func(reason byte) { // Swallow errors that occur when writing/flushing the response, // connection will be closed anyway. sendSocks5ResponseRejected(rw, reason) socksFlushBuffers(rw) } // Validate the fixed parts of the command message. if err = socksReadByteVerify(rw, "version", socksVersion); err != nil { sendErrResp(SocksRepGeneralFailure) return } if err = socksReadByteVerify(rw, "command", socksCmdConnect); err != nil { sendErrResp(SocksRepCommandNotSupported) return } if err = socksReadByteVerify(rw, "reserved", socksRsv); err != nil { sendErrResp(SocksRepGeneralFailure) return } // Read the destination address/port. // XXX: This should probably eventually send socks 5 error messages instead // of rudely closing connections on invalid addresses. var atype byte if atype, err = socksReadByte(rw); err != nil { return } var host string switch atype { case socksAtypeV4: var addr []byte if addr, err = socksReadBytes(rw, net.IPv4len); err != nil { return } host = net.IPv4(addr[0], addr[1], addr[2], addr[3]).String() case socksAtypeDomainName: var alen byte if alen, err = socksReadByte(rw); err != nil { return } if alen == 0 { err = fmt.Errorf("SOCKS request had domain name with 0 length") return } var addr []byte if addr, err = socksReadBytes(rw, int(alen)); err != nil { return } host = string(addr) if r.Buffered() != 0 { err = fmt.Errorf("%d bytes left after SOCKS header", r.Buffered()) case socksAtypeV6: var rawAddr []byte if rawAddr, err = socksReadBytes(rw, net.IPv6len); err != nil { return } addr := make(net.IP, net.IPv6len) copy(addr[:], rawAddr[:]) host = fmt.Sprintf("[%s]", addr.String()) default: sendErrResp(SocksRepAddressNotSupported) err = fmt.Errorf("SOCKS request had unsupported address type 0x%02x", atype) return } var rawPort []byte if rawPort, err = socksReadBytes(rw, 2); err != nil { return } port := int(rawPort[0])<<8 | int(rawPort[1])<<0 if err = socksFlushBuffers(rw); err != nil { return } Loading @@ -212,34 +444,64 @@ func readSocks4aConnect(s io.Reader) (req SocksRequest, err error) { return } // Send a SOCKS4a response with the given code and address. If the IP field // inside addr is not an IPv4 address, the IP portion of the response will be // four zero bytes. func sendSocks4aResponse(w io.Writer, code byte, addr *net.TCPAddr) error { var resp [8]byte resp[0] = socksResponseVersion // Send a SOCKS5 response with the given code. BND.ADDR/BND.PORT is always the // IPv4 address/port "0.0.0.0:0". func sendSocks5Response(w io.Writer, code byte) error { resp := make([]byte, 4+4+2) resp[0] = socksVersion resp[1] = code resp[2] = byte((addr.Port >> 8) & 0xff) resp[3] = byte((addr.Port >> 0) & 0xff) ipv4 := addr.IP.To4() if ipv4 != nil { resp[4] = ipv4[0] resp[5] = ipv4[1] resp[6] = ipv4[2] resp[7] = ipv4[3] } resp[2] = socksRsv resp[3] = socksAtypeV4 // BND.ADDR/BND.PORT should be the address and port that the outgoing // connection is bound to on the proxy, but Tor does not use this // information, so all zeroes are sent. _, err := w.Write(resp[:]) return err } var emptyAddr = net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 0} // Send a SOCKS5 response code 0x00. func sendSocks5ResponseGranted(w io.Writer) error { return sendSocks5Response(w, socksRepSucceeded) } // Send a SOCKS5 response with the provided failure reason. func sendSocks5ResponseRejected(w io.Writer, reason byte) error { return sendSocks5Response(w, reason) } func socksFlushBuffers(rw *bufio.ReadWriter) error { if err := rw.Writer.Flush(); err != nil { return err } if rw.Reader.Buffered() > 0 { return fmt.Errorf("%d bytes left after SOCKS message", rw.Reader.Buffered()) } return nil } func socksReadByte(rw *bufio.ReadWriter) (byte, error) { return rw.Reader.ReadByte() } // Send a SOCKS4a response code 0x5a. func sendSocks4aResponseGranted(w io.Writer, addr *net.TCPAddr) error { return sendSocks4aResponse(w, socksRequestGranted, addr) func socksReadBytes(rw *bufio.ReadWriter, n int) ([]byte, error) { ret := make([]byte, n) if _, err := io.ReadFull(rw.Reader, ret); err != nil { return nil, err } return ret, nil } // Send a SOCKS4a response code 0x5b (with an all-zero address). func sendSocks4aResponseRejected(w io.Writer) error { return sendSocks4aResponse(w, socksRequestRejected, &emptyAddr) func socksReadByteVerify(rw *bufio.ReadWriter, descr string, expected byte) error { val, err := socksReadByte(rw) if err != nil { return err } if val != expected { return fmt.Errorf("SOCKS message field %s was 0x%02x, not 0x%02x", descr, val, expected) } return nil } var _ net.Listener = (*SocksListener)(nil) socks_test.go +350 −145 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
pt.go +3 −2 Original line number Diff line number Diff line Loading @@ -128,10 +128,11 @@ // Pluggable Transport through SOCKS proxy: // https://gitweb.torproject.org/torspec.git/tree/proposals/232-pluggable-transports-through-proxy.txt // // The package implements a SOCKS4a server sufficient for a Tor client transport // The package implements a SOCKS5 server sufficient for a Tor client transport // plugin. // // http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol // https://www.ietf.org/rfc/rfc1928.txt // https://www.ietf.org/rfc/rfc1929.txt package pt import ( Loading
socks.go +324 −62 Original line number Diff line number Diff line Loading @@ -9,11 +9,40 @@ import ( ) const ( socksVersion = 0x04 socksVersion = 0x05 socksAuthNoneRequired = 0x00 socksAuthUsernamePassword = 0x02 socksAuthNoAcceptableMethods = 0xff socksCmdConnect = 0x01 socksResponseVersion = 0x00 socksRequestGranted = 0x5a socksRequestRejected = 0x5b socksRsv = 0x00 socksAtypeV4 = 0x01 socksAtypeDomainName = 0x03 socksAtypeV6 = 0x04 socksAuthRFC1929Ver = 0x01 socksAuthRFC1929Success = 0x00 socksAuthRFC1929Fail = 0x01 socksRepSucceeded = 0x00 // "general SOCKS server failure" SocksRepGeneralFailure = 0x01 // "connection not allowed by ruleset" SocksRepConnectionNotAllowed = 0x02 // "Network unreachable" SocksRepNetworkUnreachable = 0x03 // "Host unreachable" SocksRepHostUnreachable = 0x04 // "Connection refused" SocksRepConnectionRefused = 0x05 // "TTL expired" SocksRepTTLExpired = 0x06 // "Command not supported" SocksRepCommandNotSupported = 0x07 // "Address type not supported" SocksRepAddressNotSupported = 0x08 ) // Put a sanity timeout on how long we wait for a SOCKS request. Loading @@ -25,6 +54,8 @@ type SocksRequest struct { Target string // The userid string sent by the client. Username string // The password string sent by the client. Password string // The parsed contents of Username as a key–value mapping. Args Args } Loading @@ -36,15 +67,23 @@ type SocksConn struct { } // Send a message to the proxy client that access to the given address is // granted. If the IP field inside addr is not an IPv4 address, the IP portion // of the response will be four zero bytes. // granted. Addr is ignored, and "0.0.0.0:0" is always sent back for // BND.ADDR/BND.PORT in the SOCKS response. func (conn *SocksConn) Grant(addr *net.TCPAddr) error { return sendSocks4aResponseGranted(conn, addr) return sendSocks5ResponseGranted(conn) } // Send a message to the proxy client that access was rejected or failed. // Send a message to the proxy client that access was rejected or failed. This // sends back a "General Failure" error code. RejectReason should be used if // more specific error reporting is desired. func (conn *SocksConn) Reject() error { return sendSocks4aResponseRejected(conn) return conn.RejectReason(SocksRepGeneralFailure) } // Send a message to the proxy client that access was rejected, with the // specific error code indicating the reason behind the rejection. func (conn *SocksConn) RejectReason(reason byte) error { return sendSocks5ResponseRejected(conn, reason) } // SocksListener wraps a net.Listener in order to read a SOCKS request on Accept. Loading Loading @@ -140,7 +179,7 @@ retry: conn.Close() goto retry } conn.Req, err = readSocks4aConnect(conn) conn.Req, err = socks5Handshake(conn) if err != nil { conn.Close() goto retry Loading @@ -153,58 +192,251 @@ retry: return conn, nil } // Returns "socks4", suitable to be included in a call to Cmethod. // Returns "socks5", suitable to be included in a call to Cmethod. func (ln *SocksListener) Version() string { return "socks4" return "socks5" } // Read a SOCKS4a connect request. Returns a SocksRequest. func readSocks4aConnect(s io.Reader) (req SocksRequest, err error) { r := bufio.NewReader(s) // socks5handshake conducts the SOCKS5 handshake up to the point where the // client command is read and the proxy must open the outgoing connection. // Returns a SocksRequest. func socks5Handshake(s io.ReadWriter) (req SocksRequest, err error) { rw := bufio.NewReadWriter(bufio.NewReader(s), bufio.NewWriter(s)) var h [8]byte _, err = io.ReadFull(r, h[:]) if err != nil { // Negotiate the authentication method. var method byte if method, err = socksNegotiateAuth(rw); err != nil { return } if h[0] != socksVersion { err = fmt.Errorf("SOCKS header had version 0x%02x, not 0x%02x", h[0], socksVersion) // Authenticate the client. if err = socksAuthenticate(rw, method, &req); err != nil { return } if h[1] != socksCmdConnect { err = fmt.Errorf("SOCKS header had command 0x%02x, not 0x%02x", h[1], socksCmdConnect) // Read the command. err = socksReadCommand(rw, &req) return } var usernameBytes []byte usernameBytes, err = r.ReadBytes('\x00') if err != nil { // socksNegotiateAuth negotiates the authentication method and returns the // selected method as a byte. On negotiation failures an error is returned. func socksNegotiateAuth(rw *bufio.ReadWriter) (method byte, err error) { // Validate the version. if err = socksReadByteVerify(rw, "version", socksVersion); err != nil { return } req.Username = string(usernameBytes[:len(usernameBytes)-1]) req.Args, err = parseClientParameters(req.Username) if err != nil { // Read the number of methods. var nmethods byte if nmethods, err = socksReadByte(rw); err != nil { return } var port int var host string // Read the methods. var methods []byte if methods, err = socksReadBytes(rw, int(nmethods)); err != nil { return } port = int(h[2])<<8 | int(h[3])<<0 if h[4] == 0 && h[5] == 0 && h[6] == 0 && h[7] != 0 { var hostBytes []byte hostBytes, err = r.ReadBytes('\x00') if err != nil { // Pick the most "suitable" method. method = socksAuthNoAcceptableMethods for _, m := range methods { switch m { case socksAuthNoneRequired: // Pick Username/Password over None if the client happens to // send both. if method == socksAuthNoAcceptableMethods { method = m } case socksAuthUsernamePassword: method = m } } // Send the negotiated method. var msg [2]byte msg[0] = socksVersion msg[1] = method if _, err = rw.Writer.Write(msg[:]); err != nil { return } if err = socksFlushBuffers(rw); err != nil { return } return } // socksAuthenticate authenticates the client via the chosen authentication // mechanism. func socksAuthenticate(rw *bufio.ReadWriter, method byte, req *SocksRequest) (err error) { switch method { case socksAuthNoneRequired: // Straight into reading the connect. case socksAuthUsernamePassword: if err = socksAuthRFC1929(rw, req); err != nil { return } case socksAuthNoAcceptableMethods: err = fmt.Errorf("SOCKS method select had no compatible methods") return default: err = fmt.Errorf("SOCKS method select picked a unsupported method 0x%02x", method) return } if err = socksFlushBuffers(rw); err != nil { return } return } // socksAuthRFC1929 authenticates the client via RFC 1929 username/password // auth. As a design decision any valid username/password is accepted as this // field is primarily used as an out-of-band argument passing mechanism for // pluggable transports. func socksAuthRFC1929(rw *bufio.ReadWriter, req *SocksRequest) (err error) { sendErrResp := func() { // Swallow the write/flush error here, we are going to close the // connection and the original failure is more useful. resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Fail} rw.Write(resp[:]) socksFlushBuffers(rw) } // Validate the fixed parts of the command message. if err = socksReadByteVerify(rw, "auth version", socksAuthRFC1929Ver); err != nil { sendErrResp() return } // Read the username. var ulen byte if ulen, err = socksReadByte(rw); err != nil { return } if ulen < 1 { sendErrResp() err = fmt.Errorf("RFC1929 username with 0 length") return } var uname []byte if uname, err = socksReadBytes(rw, int(ulen)); err != nil { return } req.Username = string(uname) // Read the password. var plen byte if plen, err = socksReadByte(rw); err != nil { return } if plen < 1 { sendErrResp() err = fmt.Errorf("RFC1929 password with 0 length") return } host = string(hostBytes[:len(hostBytes)-1]) var passwd []byte if passwd, err = socksReadBytes(rw, int(plen)); err != nil { return } if !(plen == 1 && passwd[0] == 0x00) { // tor will set the password to 'NUL' if there are no arguments. req.Password = string(passwd) } // Mash the username/password together and parse it as a pluggable // transport argument string. if req.Args, err = parseClientParameters(req.Username + req.Password); err != nil { sendErrResp() } else { host = net.IPv4(h[4], h[5], h[6], h[7]).String() resp := []byte{socksAuthRFC1929Ver, socksAuthRFC1929Success} _, err = rw.Write(resp[:]) } return } // socksReadCommand reads a SOCKS5 client command and parses out the relevant // fields into a SocksRequest. Only CMD_CONNECT is supported. func socksReadCommand(rw *bufio.ReadWriter, req *SocksRequest) (err error) { sendErrResp := func(reason byte) { // Swallow errors that occur when writing/flushing the response, // connection will be closed anyway. sendSocks5ResponseRejected(rw, reason) socksFlushBuffers(rw) } // Validate the fixed parts of the command message. if err = socksReadByteVerify(rw, "version", socksVersion); err != nil { sendErrResp(SocksRepGeneralFailure) return } if err = socksReadByteVerify(rw, "command", socksCmdConnect); err != nil { sendErrResp(SocksRepCommandNotSupported) return } if err = socksReadByteVerify(rw, "reserved", socksRsv); err != nil { sendErrResp(SocksRepGeneralFailure) return } // Read the destination address/port. // XXX: This should probably eventually send socks 5 error messages instead // of rudely closing connections on invalid addresses. var atype byte if atype, err = socksReadByte(rw); err != nil { return } var host string switch atype { case socksAtypeV4: var addr []byte if addr, err = socksReadBytes(rw, net.IPv4len); err != nil { return } host = net.IPv4(addr[0], addr[1], addr[2], addr[3]).String() case socksAtypeDomainName: var alen byte if alen, err = socksReadByte(rw); err != nil { return } if alen == 0 { err = fmt.Errorf("SOCKS request had domain name with 0 length") return } var addr []byte if addr, err = socksReadBytes(rw, int(alen)); err != nil { return } host = string(addr) if r.Buffered() != 0 { err = fmt.Errorf("%d bytes left after SOCKS header", r.Buffered()) case socksAtypeV6: var rawAddr []byte if rawAddr, err = socksReadBytes(rw, net.IPv6len); err != nil { return } addr := make(net.IP, net.IPv6len) copy(addr[:], rawAddr[:]) host = fmt.Sprintf("[%s]", addr.String()) default: sendErrResp(SocksRepAddressNotSupported) err = fmt.Errorf("SOCKS request had unsupported address type 0x%02x", atype) return } var rawPort []byte if rawPort, err = socksReadBytes(rw, 2); err != nil { return } port := int(rawPort[0])<<8 | int(rawPort[1])<<0 if err = socksFlushBuffers(rw); err != nil { return } Loading @@ -212,34 +444,64 @@ func readSocks4aConnect(s io.Reader) (req SocksRequest, err error) { return } // Send a SOCKS4a response with the given code and address. If the IP field // inside addr is not an IPv4 address, the IP portion of the response will be // four zero bytes. func sendSocks4aResponse(w io.Writer, code byte, addr *net.TCPAddr) error { var resp [8]byte resp[0] = socksResponseVersion // Send a SOCKS5 response with the given code. BND.ADDR/BND.PORT is always the // IPv4 address/port "0.0.0.0:0". func sendSocks5Response(w io.Writer, code byte) error { resp := make([]byte, 4+4+2) resp[0] = socksVersion resp[1] = code resp[2] = byte((addr.Port >> 8) & 0xff) resp[3] = byte((addr.Port >> 0) & 0xff) ipv4 := addr.IP.To4() if ipv4 != nil { resp[4] = ipv4[0] resp[5] = ipv4[1] resp[6] = ipv4[2] resp[7] = ipv4[3] } resp[2] = socksRsv resp[3] = socksAtypeV4 // BND.ADDR/BND.PORT should be the address and port that the outgoing // connection is bound to on the proxy, but Tor does not use this // information, so all zeroes are sent. _, err := w.Write(resp[:]) return err } var emptyAddr = net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 0} // Send a SOCKS5 response code 0x00. func sendSocks5ResponseGranted(w io.Writer) error { return sendSocks5Response(w, socksRepSucceeded) } // Send a SOCKS5 response with the provided failure reason. func sendSocks5ResponseRejected(w io.Writer, reason byte) error { return sendSocks5Response(w, reason) } func socksFlushBuffers(rw *bufio.ReadWriter) error { if err := rw.Writer.Flush(); err != nil { return err } if rw.Reader.Buffered() > 0 { return fmt.Errorf("%d bytes left after SOCKS message", rw.Reader.Buffered()) } return nil } func socksReadByte(rw *bufio.ReadWriter) (byte, error) { return rw.Reader.ReadByte() } // Send a SOCKS4a response code 0x5a. func sendSocks4aResponseGranted(w io.Writer, addr *net.TCPAddr) error { return sendSocks4aResponse(w, socksRequestGranted, addr) func socksReadBytes(rw *bufio.ReadWriter, n int) ([]byte, error) { ret := make([]byte, n) if _, err := io.ReadFull(rw.Reader, ret); err != nil { return nil, err } return ret, nil } // Send a SOCKS4a response code 0x5b (with an all-zero address). func sendSocks4aResponseRejected(w io.Writer) error { return sendSocks4aResponse(w, socksRequestRejected, &emptyAddr) func socksReadByteVerify(rw *bufio.ReadWriter, descr string, expected byte) error { val, err := socksReadByte(rw) if err != nil { return err } if val != expected { return fmt.Errorf("SOCKS message field %s was 0x%02x, not 0x%02x", descr, val, expected) } return nil } var _ net.Listener = (*SocksListener)(nil)