commit b45bf02c7559eecebf0924e507a4b2137e7042e6 Author: Mike Perry mikeperry-git@torproject.org Date: Thu Mar 26 23:29:39 2015 -0700
Revert "Use a SOCKS5 listener instead of a SOCKS4a for goptlib."
This reverts commit 9d26e0009fc6a8e89a48db2a976097459f14f45e, which broke the build. --- .../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 deletions(-)
diff --git a/gitian/descriptors/linux/gitian-pluggable-transports.yml b/gitian/descriptors/linux/gitian-pluggable-transports.yml index 6adf16c..1ae9c0c 100644 --- a/gitian/descriptors/linux/gitian-pluggable-transports.yml +++ b/gitian/descriptors/linux/gitian-pluggable-transports.yml @@ -57,7 +57,6 @@ files: - "openssl-linux32-utils.zip" - "openssl-linux64-utils.zip" - "go.net.tar.bz2" -- "bug12535.patch" script: | INSTDIR="$HOME/install" PTDIR="$INSTDIR/Tor/PluggableTransports" @@ -209,11 +208,6 @@ 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 d72ab44..e2693e1 100644 --- a/gitian/descriptors/mac/gitian-pluggable-transports.yml +++ b/gitian/descriptors/mac/gitian-pluggable-transports.yml @@ -53,7 +53,6 @@ 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: | @@ -235,11 +234,6 @@ 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 0b0b5db..0f5ec84 100644 --- a/gitian/descriptors/windows/gitian-pluggable-transports.yml +++ b/gitian/descriptors/windows/gitian-pluggable-transports.yml @@ -60,7 +60,6 @@ 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 { @@ -311,11 +310,6 @@ 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 deleted file mode 100644 index dae6818..0000000 --- a/gitian/patches/bug12535.patch +++ /dev/null @@ -1,1032 +0,0 @@ -From 743ae58b6c33dda837ffb1267867f564597dd59d Mon Sep 17 00:00:00 2001 -From: Yawning Angel yawning@schwanenlied.me -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-orpor.... - // --// 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 -