[tor-commits] [goptlib/master] Merge remote-tracking branch 'yawning/bug12535_v2'

dcf at torproject.org dcf at torproject.org
Mon May 2 07:21:43 UTC 2016


commit 15d8df1cd780ddb0bb1710b08a5546f2a8f027bf
Merge: f17a5f2 93bcaf4
Author: David Fifield <david at bamsoftware.com>
Date:   Sun May 1 23:29:42 2016 -0700

    Merge remote-tracking branch 'yawning/bug12535_v2'

 pt.go         |   5 +-
 socks.go      | 386 +++++++++++++++++++++++++++++++++++++--------
 socks_test.go | 495 +++++++++++++++++++++++++++++++++++++++++-----------------
 3 files changed, 677 insertions(+), 209 deletions(-)

diff --cc pt.go
index 28e5bb7,45cf67f..82c42d5
--- a/pt.go
+++ b/pt.go
@@@ -125,13 -119,11 +125,14 @@@
  // Extended ORPort Authentication:
  // https://gitweb.torproject.org/torspec.git/tree/proposals/217-ext-orport-auth.txt.
  //
 +// 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 (
diff --cc socks.go
index 9a764b8,e4488e8..29827d9
--- a/socks.go
+++ b/socks.go
@@@ -137,13 -175,12 +176,13 @@@ retry
  	conn.Conn = c
  	err = conn.SetDeadline(time.Now().Add(socksRequestTimeout))
  	if err != nil {
 -		return nil, err
 +		conn.Close()
 +		goto retry
  	}
- 	conn.Req, err = readSocks4aConnect(conn)
+ 	conn.Req, err = socks5Handshake(conn)
  	if err != nil {
  		conn.Close()
 -		return nil, err
 +		goto retry
  	}
  	err = conn.SetDeadline(time.Time{})
  	if err != nil {
diff --cc socks_test.go
index 7fee46a,aa27d4c..d82e823
--- a/socks_test.go
+++ b/socks_test.go
@@@ -1,129 -1,78 +1,80 @@@
  package pt
  
  import (
+ 	"bufio"
  	"bytes"
 +	"errors"
+ 	"encoding/hex"
  	"io"
  	"net"
  	"testing"
 +	"time"
  )
  
- 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)
- 		}
+ // 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)
+ }
  
- 	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)
- 		}
+ 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")
  	}
+ }
  
- 	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)
- 		}
+ // 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)
  	}
  }
  
@@@ -164,106 -114,255 +116,359 @@@ func TestAuthUsernamePassword(t *testin
  	}
  }
  
 +var fakeListenerDistinguishedError = errors.New("distinguished error")
 +
 +// fakeListener is a fake dummy net.Listener that returns the given net.Conn and
 +// error the first time Accept is called. After the first call, it returns
 +// (nil, fakeListenerDistinguishedError).
 +type fakeListener struct {
 +	c   net.Conn
 +	err error
 +}
 +
 +func (ln *fakeListener) Accept() (net.Conn, error) {
 +	c := ln.c
 +	err := ln.err
 +	ln.c = nil
 +	ln.err = fakeListenerDistinguishedError
 +	return c, err
 +}
 +
 +func (ln *fakeListener) Close() error {
 +	return nil
 +}
 +
 +func (ln *fakeListener) Addr() net.Addr {
 +	return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0, Zone: ""}
 +}
 +
 +// A trivial net.Error that lets you control whether it is considered Temporary.
 +type netError struct {
 +	errString string
 +	temporary bool
 +}
 +
 +func (e *netError) Error() string {
 +	return e.errString
 +}
 +
 +func (e *netError) Temporary() bool {
 +	return e.temporary
 +}
 +
 +func (e *netError) Timeout() bool {
 +	return false
 +}
 +
 +// The purpose of ignoreDeadlineConn is to wrap net.Pipe so that the deadline
 +// functions don't return an error ("net.Pipe does not support deadlines").
 +type ignoreDeadlineConn struct {
 +	net.Conn
 +}
 +
 +func (c *ignoreDeadlineConn) SetDeadline(t time.Time) error {
 +	return nil
 +}
 +
 +func (c *ignoreDeadlineConn) SetReadDeadline(t time.Time) error {
 +	return nil
 +}
 +
 +func (c *ignoreDeadlineConn) SetWriteDeadline(t time.Time) error {
 +	return nil
 +}
 +
 +func TestAcceptErrors(t *testing.T) {
 +	// Check that AcceptSocks accurately reflects net.Errors returned by the
 +	// underlying call to Accept. This is important for the handling of
 +	// Temporary and non-Temporary errors. The loop iterates over
 +	// non-net.Error, non-Temporary net.Error, and Temporary net.Error.
 +	for _, expectedErr := range []error{io.EOF, &netError{"non-temp", false}, &netError{"temp", true}} {
 +		ln := NewSocksListener(&fakeListener{nil, expectedErr})
 +		_, err := ln.AcceptSocks()
 +		if expectedNerr, ok := expectedErr.(net.Error); ok {
 +			nerr, ok := err.(net.Error)
 +			if !ok {
 +				t.Errorf("AcceptSocks returned non-net.Error %v", nerr)
 +			} else {
 +				if expectedNerr.Temporary() != expectedNerr.Temporary() {
 +					t.Errorf("AcceptSocks did not keep Temporary status of net.Error: %v", nerr)
 +				}
 +			}
 +		}
 +	}
 +
 +	c1, c2 := net.Pipe()
 +	go func() {
 +		// Bogus request: SOCKS 5 then EOF.
 +		c2.Write([]byte("\x05\x01\x00"))
 +		c2.Close()
 +	}()
 +	ln := NewSocksListener(&fakeListener{c: &ignoreDeadlineConn{c1}, err: nil})
 +	_, err := ln.AcceptSocks()
 +	// The error in parsing the SOCKS request must be either silently
 +	// ignored, or else must be a Temporary net.Error. I.e., it must not be
 +	// the io.ErrUnexpectedEOF caused by the short request.
 +	if err == fakeListenerDistinguishedError {
 +		// Was silently ignored.
 +	} else if nerr, ok := err.(net.Error); ok {
 +		if !nerr.Temporary() {
 +			t.Errorf("AcceptSocks returned non-Temporary net.Error: %v", nerr)
 +		}
 +	} else {
 +		t.Errorf("AcceptSocks returned non-net.Error: %v", err)
 +	}
 +}
++
+ // 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)



More information about the tor-commits mailing list