[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [tor-browser-bundle/master] Use a SOCKS5 listener instead of a SOCKS4a for goptlib.
commit 9d26e0009fc6a8e89a48db2a976097459f14f45e
Author: Yawning Angel <yawning@xxxxxxxxxxxxxxx>
Date: Mon Mar 23 01:02:39 2015 +0000
Use a SOCKS5 listener instead of a SOCKS4a for goptlib.
This is done in the form of a patch that has yet to be merged into
goptlib. Once the branch is merged, this change should be reverted,
and the goptlib tag updated.
---
.../linux/gitian-pluggable-transports.yml | 6 +
.../mac/gitian-pluggable-transports.yml | 6 +
.../windows/gitian-pluggable-transports.yml | 6 +
gitian/patches/bug12535.patch | 1032 ++++++++++++++++++++
4 files changed, 1050 insertions(+)
diff --git a/gitian/descriptors/linux/gitian-pluggable-transports.yml b/gitian/descriptors/linux/gitian-pluggable-transports.yml
index 9596b7f..2f8e70e 100644
--- a/gitian/descriptors/linux/gitian-pluggable-transports.yml
+++ b/gitian/descriptors/linux/gitian-pluggable-transports.yml
@@ -55,6 +55,7 @@ files:
- "openssl-linux32-utils.zip"
- "openssl-linux64-utils.zip"
- "go.net.tar.bz2"
+- "bug12535.patch"
script: |
INSTDIR="$HOME/install"
PTDIR="$INSTDIR/Tor/PluggableTransports"
@@ -206,6 +207,11 @@ script: |
# Building goptlib
cd goptlib
+ git update-index --refresh -q
+ export GIT_COMMITTER_NAME="nobody"
+ export GIT_COMMITTER_EMAIL="nobody@localhost"
+ export GIT_COMMITTER_DATE="$REFERENCE_DATETIME"
+ git am ~/build/bug12535.patch
find -type f | xargs touch --date="$REFERENCE_DATETIME"
mkdir -p "$GOPATH/src/git.torproject.org/pluggable-transports"
ln -sf "$PWD" "$GOPATH/src/git.torproject.org/pluggable-transports/goptlib.git"
diff --git a/gitian/descriptors/mac/gitian-pluggable-transports.yml b/gitian/descriptors/mac/gitian-pluggable-transports.yml
index a1bdff9..0ed896e 100644
--- a/gitian/descriptors/mac/gitian-pluggable-transports.yml
+++ b/gitian/descriptors/mac/gitian-pluggable-transports.yml
@@ -51,6 +51,7 @@ files:
- "multiarch-darwin11-cctools127.2-gcc42-5666.3-llvmgcc42-2336.1-Linux-120724.tar.xz"
- "dzip.sh"
- "go.net.tar.bz2"
+- "bug12535.patch"
- "gmp-mac64-utils.zip"
- "openssl-mac64-utils.zip"
script: |
@@ -232,6 +233,11 @@ script: |
# Building goptlib
cd goptlib
+ git update-index --refresh -q
+ export GIT_COMMITTER_NAME="nobody"
+ export GIT_COMMITTER_EMAIL="nobody@localhost"
+ export GIT_COMMITTER_DATE="$REFERENCE_DATETIME"
+ git am ~/build/bug12535.patch
find -type f | xargs touch --date="$REFERENCE_DATETIME"
mkdir -p "$GOPATH/src/git.torproject.org/pluggable-transports"
ln -sf "$PWD" "$GOPATH/src/git.torproject.org/pluggable-transports/goptlib.git"
diff --git a/gitian/descriptors/windows/gitian-pluggable-transports.yml b/gitian/descriptors/windows/gitian-pluggable-transports.yml
index 4ca477e..0ea6e6d 100644
--- a/gitian/descriptors/windows/gitian-pluggable-transports.yml
+++ b/gitian/descriptors/windows/gitian-pluggable-transports.yml
@@ -58,6 +58,7 @@ files:
- "gmp-win32-utils.zip"
- "gcclibs-win32-utils.zip"
- "go.net.tar.bz2"
+- "bug12535.patch"
script: |
# Set the timestamp on every .pyc file in a zip file, and re-dzip the zip file.
function py2exe_zip_timestomp {
@@ -308,6 +309,11 @@ script: |
# Building goptlib
cd goptlib
+ git update-index --refresh -q
+ export GIT_COMMITTER_NAME="nobody"
+ export GIT_COMMITTER_EMAIL="nobody@localhost"
+ export GIT_COMMITTER_DATE="$REFERENCE_DATETIME"
+ git am ~/build/bug12535.patch
find -type f | xargs touch --date="$REFERENCE_DATETIME"
mkdir -p "$GOPATH/src/git.torproject.org/pluggable-transports"
ln -sf "$PWD" "$GOPATH/src/git.torproject.org/pluggable-transports/goptlib.git"
diff --git a/gitian/patches/bug12535.patch b/gitian/patches/bug12535.patch
new file mode 100644
index 0000000..dae6818
--- /dev/null
+++ b/gitian/patches/bug12535.patch
@@ -0,0 +1,1032 @@
+From 743ae58b6c33dda837ffb1267867f564597dd59d Mon Sep 17 00:00:00 2001
+From: Yawning Angel <yawning@xxxxxxxxxxxxxxx>
+Date: Mon, 8 Sep 2014 18:24:08 +0000
+Subject: [PATCH] Replace the SOCKS4a listener with a SOCKS5 listener.
+
+The change is designed to be transparent to calling code and implements
+enough of RFC1928/RFC1929 such that pluggable transports can be written.
+
+Notable incompatibilities/limitations:
+ * GSSAPI authentication is not supported.
+ * BND.ADDR/BND.PORT in responses is always "0.0.0.0:0" instead of the
+ bound address/port of the outgoing socket.
+ * RFC1929 Username/Password authentication is setup exclusively for
+ pluggable transport arguments.
+ * The BIND and UDP ASSOCIATE commands are not supported.
+---
+ pt.go | 5 +-
+ socks.go | 386 +++++++++++++++++++++++++++++++++++++-------
+ socks_test.go | 510 +++++++++++++++++++++++++++++++++++++++++-----------------
+ 3 files changed, 685 insertions(+), 216 deletions(-)
+
+diff --git a/pt.go b/pt.go
+index 62dfc38..47f8273 100644
+--- a/pt.go
++++ b/pt.go
+@@ -119,10 +119,11 @@
+ // Extended ORPort Authentication:
+ // https://gitweb.torproject.org/torspec.git/blob/HEAD:/proposals/217-ext-orport-auth.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 (
+diff --git a/socks.go b/socks.go
+index 6ad6542..e4488e8 100644
+--- a/socks.go
++++ b/socks.go
+@@ -9,11 +9,40 @@ import (
+ )
+
+ const (
+- socksVersion = 0x04
+- socksCmdConnect = 0x01
+- socksResponseVersion = 0x00
+- socksRequestGranted = 0x5a
+- socksRequestRejected = 0x5b
++ socksVersion = 0x05
++
++ socksAuthNoneRequired = 0x00
++ socksAuthUsernamePassword = 0x02
++ socksAuthNoAcceptableMethods = 0xff
++
++ socksCmdConnect = 0x01
++ 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.
+@@ -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
+ }
+@@ -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.
+@@ -138,7 +177,7 @@ func (ln *SocksListener) AcceptSocks() (*SocksConn, error) {
+ if err != nil {
+ return nil, err
+ }
+- conn.Req, err = readSocks4aConnect(conn)
++ conn.Req, err = socks5Handshake(conn)
+ if err != nil {
+ conn.Close()
+ return nil, err
+@@ -150,58 +189,251 @@ func (ln *SocksListener) AcceptSocks() (*SocksConn, error) {
+ 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
++}
++
++// 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
+ }
+
+- var usernameBytes []byte
+- usernameBytes, err = r.ReadBytes('\x00')
+- if err != nil {
++ // Read the number of methods.
++ var nmethods byte
++ if nmethods, err = socksReadByte(rw); err != nil {
+ return
+ }
+- req.Username = string(usernameBytes[:len(usernameBytes)-1])
+
+- req.Args, err = parseClientParameters(req.Username)
+- if err != nil {
++ // Read the methods.
++ var methods []byte
++ if methods, err = socksReadBytes(rw, int(nmethods)); err != nil {
+ return
+ }
+
+- var port int
+- var host string
++ // 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.
+
+- 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 {
++ case socksAuthUsernamePassword:
++ if err = socksAuthRFC1929(rw, req); err != nil {
+ return
+ }
+- host = string(hostBytes[:len(hostBytes)-1])
++
++ 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
++ }
++ 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)
++
++ 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 r.Buffered() != 0 {
+- err = fmt.Errorf("%d bytes left after SOCKS header", r.Buffered())
++ if err = socksFlushBuffers(rw); err != nil {
+ return
+ }
+
+@@ -209,34 +441,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 SOCKS4a response code 0x5a.
+-func sendSocks4aResponseGranted(w io.Writer, addr *net.TCPAddr) error {
+- return sendSocks4aResponse(w, socksRequestGranted, addr)
++// Send a SOCKS5 response with the provided failure reason.
++func sendSocks5ResponseRejected(w io.Writer, reason byte) error {
++ return sendSocks5Response(w, reason)
+ }
+
+-// Send a SOCKS4a response code 0x5b (with an all-zero address).
+-func sendSocks4aResponseRejected(w io.Writer) error {
+- return sendSocks4aResponse(w, socksRequestRejected, &emptyAddr)
++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()
++}
++
++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
+ }
++
++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)
+diff --git a/socks_test.go b/socks_test.go
+index 18d141a..aa27d4c 100644
+--- a/socks_test.go
++++ b/socks_test.go
+@@ -1,162 +1,368 @@
+ package pt
+
+ import (
++ "bufio"
+ "bytes"
++ "encoding/hex"
++ "io"
+ "net"
+ "testing"
+ )
+
+-func TestReadSocks4aConnect(t *testing.T) {
+- badTests := [...][]byte{
+- []byte(""),
+- // missing userid
+- []byte("\x04\x01\x12\x34\x01\x02\x03\x04"),
+- // missing \x00 after userid
+- []byte("\x04\x01\x12\x34\x01\x02\x03\x04key=value"),
+- // missing hostname
+- []byte("\x04\x01\x12\x34\x00\x00\x00\x01key=value\x00"),
+- // missing \x00 after hostname
+- []byte("\x04\x01\x12\x34\x00\x00\x00\x01key=value\x00hostname"),
+- // bad nameâ??value mapping
+- []byte("\x04\x01\x12\x34\x00\x00\x00\x01userid\x00hostname\x00"),
+- // bad version number
+- []byte("\x03\x01\x12\x34\x01\x02\x03\x04\x00"),
+- // BIND request
+- []byte("\x04\x02\x12\x34\x01\x02\x03\x04\x00"),
+- // SOCKS5
+- []byte("\x05\x01\x00"),
+- }
+- ipTests := [...]struct {
+- input []byte
+- addr net.TCPAddr
+- userid string
+- }{
+- {
+- []byte("\x04\x01\x12\x34\x01\x02\x03\x04key=value\x00"),
+- net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 0x1234},
+- "key=value",
+- },
+- {
+- []byte("\x04\x01\x12\x34\x01\x02\x03\x04\x00"),
+- net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 0x1234},
+- "",
+- },
+- }
+- hostnameTests := [...]struct {
+- input []byte
+- target string
+- userid string
+- }{
+- {
+- []byte("\x04\x01\x12\x34\x00\x00\x00\x01key=value\x00hostname\x00"),
+- "hostname:4660",
+- "key=value",
+- },
+- {
+- []byte("\x04\x01\x12\x34\x00\x00\x00\x01\x00hostname\x00"),
+- "hostname:4660",
+- "",
+- },
+- {
+- []byte("\x04\x01\x12\x34\x00\x00\x00\x01key=value\x00\x00"),
+- ":4660",
+- "key=value",
+- },
+- {
+- []byte("\x04\x01\x12\x34\x00\x00\x00\x01\x00\x00"),
+- ":4660",
+- "",
+- },
+- }
+-
+- for _, input := range badTests {
+- var buf bytes.Buffer
+- buf.Write(input)
+- _, err := readSocks4aConnect(&buf)
+- if err == nil {
+- t.Errorf("%q unexpectedly succeeded", input)
+- }
+- }
+-
+- for _, test := range ipTests {
+- var buf bytes.Buffer
+- buf.Write(test.input)
+- req, err := readSocks4aConnect(&buf)
+- if err != nil {
+- t.Errorf("%q unexpectedly returned an error: %s", test.input, err)
+- }
+- addr, err := net.ResolveTCPAddr("tcp", req.Target)
+- if err != nil {
+- t.Errorf("%q â?? target %q: cannot resolve: %s", test.input,
+- req.Target, err)
+- }
+- if !tcpAddrsEqual(addr, &test.addr) {
+- t.Errorf("%q â?? address %s (expected %s)", test.input,
+- req.Target, test.addr.String())
+- }
+- if req.Username != test.userid {
+- t.Errorf("%q â?? username %q (expected %q)", test.input,
+- req.Username, test.userid)
+- }
+- if req.Args == nil {
+- t.Errorf("%q â?? unexpected nil Args from username %q", test.input, req.Username)
+- }
+- }
+-
+- for _, test := range hostnameTests {
+- var buf bytes.Buffer
+- buf.Write(test.input)
+- req, err := readSocks4aConnect(&buf)
+- if err != nil {
+- t.Errorf("%q unexpectedly returned an error: %s", test.input, err)
+- }
+- if req.Target != test.target {
+- t.Errorf("%q â?? target %q (expected %q)", test.input,
+- req.Target, test.target)
+- }
+- if req.Username != test.userid {
+- t.Errorf("%q â?? username %q (expected %q)", test.input,
+- req.Username, test.userid)
+- }
+- if req.Args == nil {
+- t.Errorf("%q â?? unexpected nil Args from username %q", test.input, req.Username)
+- }
+- }
+-}
+-
+-func TestSendSocks4aResponse(t *testing.T) {
+- tests := [...]struct {
+- code byte
+- addr net.TCPAddr
+- expected []byte
+- }{
+- {
+- socksRequestGranted,
+- net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 0x1234},
+- []byte("\x00\x5a\x12\x34\x01\x02\x03\x04"),
+- },
+- {
+- socksRequestRejected,
+- net.TCPAddr{IP: net.ParseIP("1:2::3:4"), Port: 0x1234},
+- []byte("\x00\x5b\x12\x34\x00\x00\x00\x00"),
+- },
+- }
+-
+- for _, test := range tests {
+- var buf bytes.Buffer
+- err := sendSocks4aResponse(&buf, test.code, &test.addr)
+- if err != nil {
+- t.Errorf("0x%02x %s unexpectedly returned an error: %s", test.code, &test.addr, err)
+- }
+- p := make([]byte, 1024)
+- n, err := buf.Read(p)
+- if err != nil {
+- t.Fatal(err)
+- }
+- output := p[:n]
+- if !bytes.Equal(output, test.expected) {
+- t.Errorf("0x%02x %s â?? %v (expected %v)",
+- test.code, &test.addr, output, test.expected)
+- }
++// testReadWriter is a bytes.Buffer backed io.ReadWriter used for testing. The
++// Read and Write routines are to be used by the component being tested. Data
++// can be written to and read back via the writeHex and readHex routines.
++type testReadWriter struct {
++ readBuf bytes.Buffer
++ writeBuf bytes.Buffer
++}
++
++func (c *testReadWriter) Read(buf []byte) (n int, err error) {
++ return c.readBuf.Read(buf)
++}
++
++func (c *testReadWriter) Write(buf []byte) (n int, err error) {
++ return c.writeBuf.Write(buf)
++}
++
++func (c *testReadWriter) writeHex(str string) (n int, err error) {
++ var buf []byte
++ if buf, err = hex.DecodeString(str); err != nil {
++ return
++ }
++ return c.readBuf.Write(buf)
++}
++
++func (c *testReadWriter) readHex() string {
++ return hex.EncodeToString(c.writeBuf.Bytes())
++}
++
++func (c *testReadWriter) toBufio() *bufio.ReadWriter {
++ return bufio.NewReadWriter(bufio.NewReader(c), bufio.NewWriter(c))
++}
++
++func (c *testReadWriter) reset() {
++ c.readBuf.Reset()
++ c.writeBuf.Reset()
++}
++
++// TestAuthInvalidVersion tests auth negotiation with an invalid version.
++func TestAuthInvalidVersion(t *testing.T) {
++ c := new(testReadWriter)
++
++ // VER = 03, NMETHODS = 01, METHODS = [00]
++ c.writeHex("030100")
++ if _, err := socksNegotiateAuth(c.toBufio()); err == nil {
++ t.Error("socksNegotiateAuth(InvalidVersion) succeded")
++ }
++}
++
++// TestAuthInvalidNMethods tests auth negotiaton with no methods.
++func TestAuthInvalidNMethods(t *testing.T) {
++ c := new(testReadWriter)
++ var err error
++ var method byte
++
++ // VER = 05, NMETHODS = 00
++ c.writeHex("0500")
++ if method, err = socksNegotiateAuth(c.toBufio()); err != nil {
++ t.Error("socksNegotiateAuth(No Methods) failed:", err)
++ }
++ if method != socksAuthNoAcceptableMethods {
++ t.Error("socksNegotiateAuth(No Methods) picked unexpected method:", method)
++ }
++ if msg := c.readHex(); msg != "05ff" {
++ t.Error("socksNegotiateAuth(No Methods) invalid response:", msg)
++ }
++}
++
++// TestAuthNoneRequired tests auth negotiaton with NO AUTHENTICATION REQUIRED.
++func TestAuthNoneRequired(t *testing.T) {
++ c := new(testReadWriter)
++ var err error
++ var method byte
++
++ // VER = 05, NMETHODS = 01, METHODS = [00]
++ c.writeHex("050100")
++ if method, err = socksNegotiateAuth(c.toBufio()); err != nil {
++ t.Error("socksNegotiateAuth(None) failed:", err)
++ }
++ if method != socksAuthNoneRequired {
++ t.Error("socksNegotiateAuth(None) unexpected method:", method)
++ }
++ if msg := c.readHex(); msg != "0500" {
++ t.Error("socksNegotiateAuth(None) invalid response:", msg)
++ }
++}
++
++// TestAuthUsernamePassword tests auth negotiation with USERNAME/PASSWORD.
++func TestAuthUsernamePassword(t *testing.T) {
++ c := new(testReadWriter)
++ var err error
++ var method byte
++
++ // VER = 05, NMETHODS = 01, METHODS = [02]
++ c.writeHex("050102")
++ if method, err = socksNegotiateAuth(c.toBufio()); err != nil {
++ t.Error("socksNegotiateAuth(UsernamePassword) failed:", err)
++ }
++ if method != socksAuthUsernamePassword {
++ t.Error("socksNegotiateAuth(UsernamePassword) unexpected method:", method)
++ }
++ if msg := c.readHex(); msg != "0502" {
++ t.Error("socksNegotiateAuth(UsernamePassword) invalid response:", msg)
+ }
+ }
++
++// TestAuthBoth tests auth negotiation containing both NO AUTHENTICATION
++// REQUIRED and USERNAME/PASSWORD.
++func TestAuthBoth(t *testing.T) {
++ c := new(testReadWriter)
++ var err error
++ var method byte
++
++ // VER = 05, NMETHODS = 02, METHODS = [00, 02]
++ c.writeHex("05020002")
++ if method, err = socksNegotiateAuth(c.toBufio()); err != nil {
++ t.Error("socksNegotiateAuth(Both) failed:", err)
++ }
++ if method != socksAuthUsernamePassword {
++ t.Error("socksNegotiateAuth(Both) unexpected method:", method)
++ }
++ if msg := c.readHex(); msg != "0502" {
++ t.Error("socksNegotiateAuth(Both) invalid response:", msg)
++ }
++}
++
++// TestAuthUnsupported tests auth negotiation with a unsupported method.
++func TestAuthUnsupported(t *testing.T) {
++ c := new(testReadWriter)
++ var err error
++ var method byte
++
++ // VER = 05, NMETHODS = 01, METHODS = [01] (GSSAPI)
++ c.writeHex("050101")
++ if method, err = socksNegotiateAuth(c.toBufio()); err != nil {
++ t.Error("socksNegotiateAuth(Unknown) failed:", err)
++ }
++ if method != socksAuthNoAcceptableMethods {
++ t.Error("socksNegotiateAuth(Unknown) picked unexpected method:", method)
++ }
++ if msg := c.readHex(); msg != "05ff" {
++ t.Error("socksNegotiateAuth(Unknown) invalid response:", msg)
++ }
++}
++
++// TestAuthUnsupported2 tests auth negotiation with supported and unsupported
++// methods.
++func TestAuthUnsupported2(t *testing.T) {
++ c := new(testReadWriter)
++ var err error
++ var method byte
++
++ // VER = 05, NMETHODS = 03, METHODS = [00,01,02]
++ c.writeHex("0503000102")
++ if method, err = socksNegotiateAuth(c.toBufio()); err != nil {
++ t.Error("socksNegotiateAuth(Unknown2) failed:", err)
++ }
++ if method != socksAuthUsernamePassword {
++ t.Error("socksNegotiateAuth(Unknown2) picked unexpected method:", method)
++ }
++ if msg := c.readHex(); msg != "0502" {
++ t.Error("socksNegotiateAuth(Unknown2) invalid response:", msg)
++ }
++}
++
++// TestRFC1929InvalidVersion tests RFC1929 auth with an invalid version.
++func TestRFC1929InvalidVersion(t *testing.T) {
++ c := new(testReadWriter)
++ var req SocksRequest
++
++ // VER = 03, ULEN = 5, UNAME = "ABCDE", PLEN = 5, PASSWD = "abcde"
++ c.writeHex("03054142434445056162636465")
++ if err := socksAuthenticate(c.toBufio(), socksAuthUsernamePassword, &req); err == nil {
++ t.Error("socksAuthenticate(InvalidVersion) succeded")
++ }
++ if msg := c.readHex(); msg != "0101" {
++ t.Error("socksAuthenticate(InvalidVersion) invalid response:", msg)
++ }
++}
++
++// TestRFC1929InvalidUlen tests RFC1929 auth with an invalid ULEN.
++func TestRFC1929InvalidUlen(t *testing.T) {
++ c := new(testReadWriter)
++ var req SocksRequest
++
++ // VER = 01, ULEN = 0, UNAME = "", PLEN = 5, PASSWD = "abcde"
++ c.writeHex("0100056162636465")
++ if err := socksAuthenticate(c.toBufio(), socksAuthUsernamePassword, &req); err == nil {
++ t.Error("socksAuthenticate(InvalidUlen) succeded")
++ }
++ if msg := c.readHex(); msg != "0101" {
++ t.Error("socksAuthenticate(InvalidUlen) invalid response:", msg)
++ }
++}
++
++// TestRFC1929InvalidPlen tests RFC1929 auth with an invalid PLEN.
++func TestRFC1929InvalidPlen(t *testing.T) {
++ c := new(testReadWriter)
++ var req SocksRequest
++
++ // VER = 01, ULEN = 5, UNAME = "ABCDE", PLEN = 0, PASSWD = ""
++ c.writeHex("0105414243444500")
++ if err := socksAuthenticate(c.toBufio(), socksAuthUsernamePassword, &req); err == nil {
++ t.Error("socksAuthenticate(InvalidPlen) succeded")
++ }
++ if msg := c.readHex(); msg != "0101" {
++ t.Error("socksAuthenticate(InvalidPlen) invalid response:", msg)
++ }
++}
++
++// TestRFC1929InvalidArgs tests RFC1929 auth with invalid pt args.
++func TestRFC1929InvalidPTArgs(t *testing.T) {
++ c := new(testReadWriter)
++ var req SocksRequest
++
++ // VER = 01, ULEN = 5, UNAME = "ABCDE", PLEN = 5, PASSWD = "abcde"
++ c.writeHex("01054142434445056162636465")
++ if err := socksAuthenticate(c.toBufio(), socksAuthUsernamePassword, &req); err == nil {
++ t.Error("socksAuthenticate(InvalidArgs) succeded")
++ }
++ if msg := c.readHex(); msg != "0101" {
++ t.Error("socksAuthenticate(InvalidArgs) invalid response:", msg)
++ }
++}
++
++// TestRFC1929Success tests RFC1929 auth with valid pt args.
++func TestRFC1929Success(t *testing.T) {
++ c := new(testReadWriter)
++ var req SocksRequest
++
++ // VER = 01, ULEN = 9, UNAME = "key=value", PLEN = 1, PASSWD = "\0"
++ c.writeHex("01096b65793d76616c75650100")
++ if err := socksAuthenticate(c.toBufio(), socksAuthUsernamePassword, &req); err != nil {
++ t.Error("socksAuthenticate(Success) failed:", err)
++ }
++ if msg := c.readHex(); msg != "0100" {
++ t.Error("socksAuthenticate(Success) invalid response:", msg)
++ }
++ v, ok := req.Args.Get("key")
++ if v != "value" || !ok {
++ t.Error("RFC1929 k,v parse failure:", v)
++ }
++}
++
++// TestRequestInvalidHdr tests SOCKS5 requests with invalid VER/CMD/RSV/ATYPE
++func TestRequestInvalidHdr(t *testing.T) {
++ c := new(testReadWriter)
++ var req SocksRequest
++
++ // VER = 03, CMD = 01, RSV = 00, ATYPE = 01, DST.ADDR = 127.0.0.1, DST.PORT = 9050
++ c.writeHex("030100017f000001235a")
++ if err := socksReadCommand(c.toBufio(), &req); err == nil {
++ t.Error("socksReadCommand(InvalidVer) succeded")
++ }
++ if msg := c.readHex(); msg != "05010001000000000000" {
++ t.Error("socksReadCommand(InvalidVer) invalid response:", msg)
++ }
++ c.reset()
++
++ // VER = 05, CMD = 05, RSV = 00, ATYPE = 01, DST.ADDR = 127.0.0.1, DST.PORT = 9050
++ c.writeHex("050500017f000001235a")
++ if err := socksReadCommand(c.toBufio(), &req); err == nil {
++ t.Error("socksReadCommand(InvalidCmd) succeded")
++ }
++ if msg := c.readHex(); msg != "05070001000000000000" {
++ t.Error("socksReadCommand(InvalidCmd) invalid response:", msg)
++ }
++ c.reset()
++
++ // VER = 05, CMD = 01, RSV = 30, ATYPE = 01, DST.ADDR = 127.0.0.1, DST.PORT = 9050
++ c.writeHex("050130017f000001235a")
++ if err := socksReadCommand(c.toBufio(), &req); err == nil {
++ t.Error("socksReadCommand(InvalidRsv) succeded")
++ }
++ if msg := c.readHex(); msg != "05010001000000000000" {
++ t.Error("socksReadCommand(InvalidRsv) invalid response:", msg)
++ }
++ c.reset()
++
++ // VER = 05, CMD = 01, RSV = 01, ATYPE = 05, DST.ADDR = 127.0.0.1, DST.PORT = 9050
++ c.writeHex("050100057f000001235a")
++ if err := socksReadCommand(c.toBufio(), &req); err == nil {
++ t.Error("socksReadCommand(InvalidAtype) succeded")
++ }
++ if msg := c.readHex(); msg != "05080001000000000000" {
++ t.Error("socksAuthenticate(InvalidAtype) invalid response:", msg)
++ }
++ c.reset()
++}
++
++// TestRequestIPv4 tests IPv4 SOCKS5 requests.
++func TestRequestIPv4(t *testing.T) {
++ c := new(testReadWriter)
++ var req SocksRequest
++
++ // VER = 05, CMD = 01, RSV = 00, ATYPE = 01, DST.ADDR = 127.0.0.1, DST.PORT = 9050
++ c.writeHex("050100017f000001235a")
++ if err := socksReadCommand(c.toBufio(), &req); err != nil {
++ t.Error("socksReadCommand(IPv4) failed:", err)
++ }
++ addr, err := net.ResolveTCPAddr("tcp", req.Target)
++ if err != nil {
++ t.Error("net.ResolveTCPAddr failed:", err)
++ }
++ if !tcpAddrsEqual(addr, &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 9050}) {
++ t.Error("Unexpected target:", addr)
++ }
++}
++
++// TestRequestIPv6 tests IPv4 SOCKS5 requests.
++func TestRequestIPv6(t *testing.T) {
++ c := new(testReadWriter)
++ var req SocksRequest
++
++ // VER = 05, CMD = 01, RSV = 00, ATYPE = 04, DST.ADDR = 0102:0304:0506:0708:090a:0b0c:0d0e:0f10, DST.PORT = 9050
++ c.writeHex("050100040102030405060708090a0b0c0d0e0f10235a")
++ if err := socksReadCommand(c.toBufio(), &req); err != nil {
++ t.Error("socksReadCommand(IPv6) failed:", err)
++ }
++ addr, err := net.ResolveTCPAddr("tcp", req.Target)
++ if err != nil {
++ t.Error("net.ResolveTCPAddr failed:", err)
++ }
++ if !tcpAddrsEqual(addr, &net.TCPAddr{IP: net.ParseIP("0102:0304:0506:0708:090a:0b0c:0d0e:0f10"), Port: 9050}) {
++ t.Error("Unexpected target:", addr)
++ }
++}
++
++// TestRequestFQDN tests FQDN (DOMAINNAME) SOCKS5 requests.
++func TestRequestFQDN(t *testing.T) {
++ c := new(testReadWriter)
++ var req SocksRequest
++
++ // VER = 05, CMD = 01, RSV = 00, ATYPE = 04, DST.ADDR = example.com, DST.PORT = 9050
++ c.writeHex("050100030b6578616d706c652e636f6d235a")
++ if err := socksReadCommand(c.toBufio(), &req); err != nil {
++ t.Error("socksReadCommand(FQDN) failed:", err)
++ }
++ if req.Target != "example.com:9050" {
++ t.Error("Unexpected target:", req.Target)
++ }
++}
++
++// TestResponseNil tests nil address SOCKS5 responses.
++func TestResponseNil(t *testing.T) {
++ c := new(testReadWriter)
++
++ b := c.toBufio()
++ if err := sendSocks5ResponseGranted(b); err != nil {
++ t.Error("sendSocks5ResponseGranted() failed:", err)
++ }
++ b.Flush()
++ if msg := c.readHex(); msg != "05000001000000000000" {
++ t.Error("sendSocks5ResponseGranted(nil) invalid response:", msg)
++ }
++}
++
++var _ io.ReadWriter = (*testReadWriter)(nil)
+--
+2.3.3
+
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits