[tor-commits] [goptlib/master] Move socks code into the pt package.

dcf at torproject.org dcf at torproject.org
Mon Dec 9 02:49:51 UTC 2013


commit 37be077e66cbff6403e054b2351e2eab3037a292
Author: David Fifield <david at bamsoftware.com>
Date:   Sat Dec 7 20:50:27 2013 -0800

    Move socks code into the pt package.
---
 examples/dummy-client/dummy-client.go |    7 +-
 pt.go                                 |    9 +-
 socks.go                              |  189 +++++++++++++++++++++++++++++++++
 socks/socks.go                        |  183 -------------------------------
 socks/socks_test.go                   |  113 --------------------
 socks_test.go                         |  109 +++++++++++++++++++
 6 files changed, 308 insertions(+), 302 deletions(-)

diff --git a/examples/dummy-client/dummy-client.go b/examples/dummy-client/dummy-client.go
index 10f7b8b..de56d99 100644
--- a/examples/dummy-client/dummy-client.go
+++ b/examples/dummy-client/dummy-client.go
@@ -20,7 +20,6 @@ import (
 )
 
 import "git.torproject.org/pluggable-transports/goptlib.git"
-import "git.torproject.org/pluggable-transports/goptlib.git/socks"
 
 var ptInfo pt.ClientInfo
 
@@ -44,7 +43,7 @@ func copyLoop(a, b net.Conn) {
 	wg.Wait()
 }
 
-func handleConnection(local *socks.Conn) error {
+func handleConnection(local *pt.SocksConn) error {
 	defer local.Close()
 
 	handlerChan <- 1
@@ -68,7 +67,7 @@ func handleConnection(local *socks.Conn) error {
 	return nil
 }
 
-func acceptLoop(ln *socks.Listener) error {
+func acceptLoop(ln *pt.SocksListener) error {
 	for {
 		conn, err := ln.AcceptSocks()
 		if err != nil {
@@ -80,7 +79,7 @@ func acceptLoop(ln *socks.Listener) error {
 }
 
 func startListener(addr string) (net.Listener, error) {
-	ln, err := socks.Listen("tcp", addr)
+	ln, err := pt.ListenSocks("tcp", addr)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pt.go b/pt.go
index 86632d9..ddecc29 100644
--- a/pt.go
+++ b/pt.go
@@ -8,15 +8,15 @@
 // 		os.Exit(1)
 // 	}
 // 	for _, methodName := range ptInfo.MethodNames {
-// 		ln, err := startSocksListener()
+// 		ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
 // 		if err != nil {
 // 			pt.CmethodError(methodName, err.Error())
 // 			continue
 // 		}
+// 		go acceptLoop(ln)
 // 		pt.Cmethod(methodName, "socks4", ln.Addr())
 // 	}
 // 	pt.CmethodsDone()
-// See the socks package for help with writing a SOCKS listener.
 //
 // Sample server usage:
 // 	func handler(conn net.Conn) {
@@ -48,6 +48,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
+// plugin.
+//
+// http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol
 package pt
 
 import (
diff --git a/socks.go b/socks.go
new file mode 100644
index 0000000..a5889fa
--- /dev/null
+++ b/socks.go
@@ -0,0 +1,189 @@
+package pt
+
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"io"
+	"net"
+)
+
+const (
+	socksVersion         = 0x04
+	socksCmdConnect      = 0x01
+	socksResponseVersion = 0x00
+	socksRequestGranted  = 0x5a
+	socksRequestRejected = 0x5b
+)
+
+// SocksRequest describes a SOCKS request.
+type SocksRequest struct {
+	Username string
+	Target   string
+}
+
+// SocksConn encapsulates a net.Conn and information associated with a SOCKS request.
+type SocksConn struct {
+	net.Conn
+	Req SocksRequest
+}
+
+// Send a message to the proxy client that access to the given address is
+// granted.
+func (conn *SocksConn) Grant(addr *net.TCPAddr) error {
+	return sendSocks4aResponseGranted(conn, addr)
+}
+
+// Send a message to the proxy client that access was rejected or failed.
+func (conn *SocksConn) Reject() error {
+	return sendSocks4aResponseRejected(conn)
+}
+
+// SocksListener wraps a net.Listener in order to read a SOCKS request on Accept.
+//
+// 	func handleConn(conn *pt.SocksConn) error {
+// 		defer conn.Close()
+//
+// 		remote, err := net.Dial("tcp", conn.Req.Target)
+// 		if err != nil {
+// 			conn.Reject()
+// 			return err
+// 		}
+// 		err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
+// 		if err != nil {
+// 			return err
+// 		}
+// 		defer remote.Close()
+//
+// 		// do something with conn and remote
+// 	}
+// 	...
+// 	ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
+// 	if err != nil {
+// 		panic(err.Error())
+// 	}
+// 	for {
+// 		conn, err := ln.AcceptSocks()
+// 		if err != nil {
+// 			break
+// 		}
+// 		go handleConn(conn)
+// 	}
+type SocksListener struct {
+	net.Listener
+}
+
+// Open a net.Listener according to network and laddr, and return it as a
+// SocksListener.
+func ListenSocks(network, laddr string) (*SocksListener, error) {
+	ln, err := net.Listen(network, laddr)
+	if err != nil {
+		return nil, err
+	}
+	return NewSocksListener(ln), nil
+}
+
+// Create a new SocksListener wrapping the given net.Listener.
+func NewSocksListener(ln net.Listener) *SocksListener {
+	return &SocksListener{ln}
+}
+
+// Accept is the same as AcceptSocks, except that it returns a generic net.Conn.
+// It is present for the sake of satisfying the net.Listener interface.
+func (ln *SocksListener) Accept() (net.Conn, error) {
+	return ln.AcceptSocks()
+}
+
+// Call Accept on the wrapped net.Listener, do SOCKS negotiation, and return a
+// SocksConn. After accepting, you must call either conn.Grant or conn.Reject
+// (presumably after trying to connect to conn.Req.Target).
+func (ln *SocksListener) AcceptSocks() (*SocksConn, error) {
+	c, err := ln.Listener.Accept()
+	if err != nil {
+		return nil, err
+	}
+	conn := new(SocksConn)
+	conn.Conn = c
+	conn.Req, err = readSocks4aConnect(conn)
+	if err != nil {
+		conn.Close()
+		return nil, err
+	}
+	return conn, nil
+}
+
+// Read a SOCKS4a connect request. Returns a SocksRequest.
+func readSocks4aConnect(s io.Reader) (req SocksRequest, err error) {
+	r := bufio.NewReader(s)
+
+	var h [8]byte
+	_, err = io.ReadFull(r, h[:])
+	if err != nil {
+		return
+	}
+	if h[0] != socksVersion {
+		err = errors.New(fmt.Sprintf("SOCKS header had version 0x%02x, not 0x%02x", h[0], socksVersion))
+		return
+	}
+	if h[1] != socksCmdConnect {
+		err = errors.New(fmt.Sprintf("SOCKS header had command 0x%02x, not 0x%02x", h[1], socksCmdConnect))
+		return
+	}
+
+	var usernameBytes []byte
+	usernameBytes, err = r.ReadBytes('\x00')
+	if err != nil {
+		return
+	}
+	req.Username = string(usernameBytes[:len(usernameBytes)-1])
+
+	var port int
+	var host string
+
+	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 {
+			return
+		}
+		host = string(hostBytes[:len(hostBytes)-1])
+	} else {
+		host = net.IPv4(h[4], h[5], h[6], h[7]).String()
+	}
+
+	if r.Buffered() != 0 {
+		err = errors.New(fmt.Sprintf("%d bytes left after SOCKS header", r.Buffered()))
+		return
+	}
+
+	req.Target = fmt.Sprintf("%s:%d", host, port)
+	return
+}
+
+// Send a SOCKS4a response with the given code and address.
+func sendSocks4aResponse(w io.Writer, code byte, addr *net.TCPAddr) error {
+	var resp [8]byte
+	resp[0] = socksResponseVersion
+	resp[1] = code
+	resp[2] = byte((addr.Port >> 8) & 0xff)
+	resp[3] = byte((addr.Port >> 0) & 0xff)
+	resp[4] = addr.IP[0]
+	resp[5] = addr.IP[1]
+	resp[6] = addr.IP[2]
+	resp[7] = addr.IP[3]
+	_, err := w.Write(resp[:])
+	return err
+}
+
+var emptyAddr = net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 0}
+
+// Send a SOCKS4a response code 0x5a.
+func sendSocks4aResponseGranted(w io.Writer, addr *net.TCPAddr) error {
+	return sendSocks4aResponse(w, socksRequestGranted, addr)
+}
+
+// Send a SOCKS4a response code 0x5b (with an all-zero address).
+func sendSocks4aResponseRejected(w io.Writer) error {
+	return sendSocks4aResponse(w, socksRequestRejected, &emptyAddr)
+}
diff --git a/socks/socks.go b/socks/socks.go
deleted file mode 100644
index 450c409..0000000
--- a/socks/socks.go
+++ /dev/null
@@ -1,183 +0,0 @@
-// Package socks implements a SOCKS4a server sufficient for a Tor client
-// transport plugin.
-//
-// 	ln, err := socks.Listen("tcp", ":3128")
-// 	if err != nil {
-// 		return err
-// 	}
-// 	conn, err := ln.AcceptSocks()
-// 	if err != nil {
-// 		return err
-// 	}
-// 	defer conn.Close()
-// 	remote, err := net.Dial("tcp", local.Req.Target)
-// 	if err != nil {
-// 		local.Reject()
-// 		return err
-// 	}
-// 	err = local.Grant(remote.RemoteAddr().(*net.TCPAddr))
-// 	if err != nil {
-// 		return err
-// 	}
-//
-// http://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol
-package socks
-
-import (
-	"bufio"
-	"errors"
-	"fmt"
-	"io"
-	"net"
-)
-
-const (
-	socksVersion         = 0x04
-	socksCmdConnect      = 0x01
-	socksResponseVersion = 0x00
-	socksRequestGranted  = 0x5a
-	socksRequestRejected = 0x5b
-)
-
-// Request describes a SOCKS request.
-type Request struct {
-	Username string
-	Target   string
-}
-
-// Conn encapsulates a net.Conn and information associated with a SOCKS request.
-type Conn struct {
-	net.Conn
-	Req Request
-}
-
-// Send a message to the proxy client that access to the given address is
-// granted.
-func (conn *Conn) Grant(addr *net.TCPAddr) error {
-	return sendSocks4aResponseGranted(conn, addr)
-}
-
-// Send a message to the proxy client that access was rejected or failed.
-func (conn *Conn) Reject() error {
-	return sendSocks4aResponseRejected(conn)
-}
-
-// Listener wraps a net.Listener in order to read a SOCKS request on Accept.
-type Listener struct {
-	net.Listener
-}
-
-// Open a net.Listener according to network and laddr, and return it as a
-// Listener.
-func Listen(network, laddr string) (*Listener, error) {
-	ln, err := net.Listen(network, laddr)
-	if err != nil {
-		return nil, err
-	}
-	return NewListener(ln), nil
-}
-
-// Create a new Listener wrapping the given net.Listener.
-func NewListener(ln net.Listener) *Listener {
-	return &Listener{ln}
-}
-
-// Accept is the same as AcceptSocks, except that it returns a generic net.Conn.
-// It is present for the sake of satisfying the net.Listener interface.
-func (ln *Listener) Accept() (net.Conn, error) {
-	return ln.AcceptSocks()
-}
-
-// Call Accept on the wrapped net.Listener, do SOCKS negotiation, and return a
-// Conn. After accepting, you must call either conn.Grant or conn.Reject
-// (presumably after trying to connect to conn.Req.Target).
-func (ln *Listener) AcceptSocks() (*Conn, error) {
-	c, err := ln.Listener.Accept()
-	if err != nil {
-		return nil, err
-	}
-	conn := new(Conn)
-	conn.Conn = c
-	conn.Req, err = readSocks4aConnect(conn)
-	if err != nil {
-		conn.Close()
-		return nil, err
-	}
-	return conn, nil
-}
-
-// Read a SOCKS4a connect request. Returns a Request.
-func readSocks4aConnect(s io.Reader) (req Request, err error) {
-	r := bufio.NewReader(s)
-
-	var h [8]byte
-	_, err = io.ReadFull(r, h[:])
-	if err != nil {
-		return
-	}
-	if h[0] != socksVersion {
-		err = errors.New(fmt.Sprintf("SOCKS header had version 0x%02x, not 0x%02x", h[0], socksVersion))
-		return
-	}
-	if h[1] != socksCmdConnect {
-		err = errors.New(fmt.Sprintf("SOCKS header had command 0x%02x, not 0x%02x", h[1], socksCmdConnect))
-		return
-	}
-
-	var usernameBytes []byte
-	usernameBytes, err = r.ReadBytes('\x00')
-	if err != nil {
-		return
-	}
-	req.Username = string(usernameBytes[:len(usernameBytes)-1])
-
-	var port int
-	var host string
-
-	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 {
-			return
-		}
-		host = string(hostBytes[:len(hostBytes)-1])
-	} else {
-		host = net.IPv4(h[4], h[5], h[6], h[7]).String()
-	}
-
-	if r.Buffered() != 0 {
-		err = errors.New(fmt.Sprintf("%d bytes left after SOCKS header", r.Buffered()))
-		return
-	}
-
-	req.Target = fmt.Sprintf("%s:%d", host, port)
-	return
-}
-
-// Send a SOCKS4a response with the given code and address.
-func sendSocks4aResponse(w io.Writer, code byte, addr *net.TCPAddr) error {
-	var resp [8]byte
-	resp[0] = socksResponseVersion
-	resp[1] = code
-	resp[2] = byte((addr.Port >> 8) & 0xff)
-	resp[3] = byte((addr.Port >> 0) & 0xff)
-	resp[4] = addr.IP[0]
-	resp[5] = addr.IP[1]
-	resp[6] = addr.IP[2]
-	resp[7] = addr.IP[3]
-	_, err := w.Write(resp[:])
-	return err
-}
-
-var emptyAddr = net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 0}
-
-// Send a SOCKS4a response code 0x5a.
-func sendSocks4aResponseGranted(w io.Writer, addr *net.TCPAddr) error {
-	return sendSocks4aResponse(w, socksRequestGranted, addr)
-}
-
-// Send a SOCKS4a response code 0x5b (with an all-zero address).
-func sendSocks4aResponseRejected(w io.Writer) error {
-	return sendSocks4aResponse(w, socksRequestRejected, &emptyAddr)
-}
diff --git a/socks/socks_test.go b/socks/socks_test.go
deleted file mode 100644
index 759bf4a..0000000
--- a/socks/socks_test.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package socks
-
-import (
-	"bytes"
-	"net"
-	"testing"
-)
-
-func tcpAddrsEqual(a, b *net.TCPAddr) bool {
-	return a.IP.Equal(b.IP) && a.Port == b.Port
-}
-
-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\x04userid"),
-		// missing hostname
-		[]byte("\x04\x01\x12\x34\x00\x00\x00\x01userid\x00"),
-		// missing \x00 after hostname
-		[]byte("\x04\x01\x12\x34\x00\x00\x00\x01userid\x00hostname"),
-		// BIND request
-		[]byte("\x04\x02\x12\x34\x01\x02\x03\x04userid\x00"),
-		// SOCKS5
-		[]byte("\x05\x01\x00"),
-	}
-	ipTests := [...]struct {
-		input  []byte
-		userid string
-		addr   net.TCPAddr
-	}{
-		{
-			[]byte("\x04\x01\x12\x34\x01\x02\x03\x04userid\x00"),
-			"userid", net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 0x1234},
-		},
-		{
-			[]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
-		userid string
-		target string
-	}{
-		{
-			[]byte("\x04\x01\x12\x34\x00\x00\x00\x01userid\x00hostname\x00"),
-			"userid", "hostname:4660",
-		},
-		{
-			[]byte("\x04\x01\x12\x34\x00\x00\x00\x01\x00hostname\x00"),
-			"", "hostname:4660",
-		},
-		{
-			[]byte("\x04\x01\x12\x34\x00\x00\x00\x01userid\x00\x00"),
-			"userid", ":4660",
-		},
-		{
-			[]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)
-		}
-		if req.Username != test.userid {
-			t.Errorf("%q → username %q (expected %q)", test.input,
-				req.Username, test.userid)
-		}
-		addr, err := net.ResolveTCPAddr("tcp", req.Target)
-		if err != nil {
-			t.Error("%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())
-		}
-	}
-
-	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.Username != test.userid {
-			t.Errorf("%q → username %q (expected %q)", test.input,
-				req.Username, test.userid)
-		}
-		if req.Target != test.target {
-			t.Errorf("%q → target %q (expected %q)", test.input,
-				req.Target, test.target)
-		}
-	}
-}
diff --git a/socks_test.go b/socks_test.go
new file mode 100644
index 0000000..bb638b5
--- /dev/null
+++ b/socks_test.go
@@ -0,0 +1,109 @@
+package pt
+
+import (
+	"bytes"
+	"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\x04userid"),
+		// missing hostname
+		[]byte("\x04\x01\x12\x34\x00\x00\x00\x01userid\x00"),
+		// missing \x00 after hostname
+		[]byte("\x04\x01\x12\x34\x00\x00\x00\x01userid\x00hostname"),
+		// BIND request
+		[]byte("\x04\x02\x12\x34\x01\x02\x03\x04userid\x00"),
+		// SOCKS5
+		[]byte("\x05\x01\x00"),
+	}
+	ipTests := [...]struct {
+		input  []byte
+		userid string
+		addr   net.TCPAddr
+	}{
+		{
+			[]byte("\x04\x01\x12\x34\x01\x02\x03\x04userid\x00"),
+			"userid", net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 0x1234},
+		},
+		{
+			[]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
+		userid string
+		target string
+	}{
+		{
+			[]byte("\x04\x01\x12\x34\x00\x00\x00\x01userid\x00hostname\x00"),
+			"userid", "hostname:4660",
+		},
+		{
+			[]byte("\x04\x01\x12\x34\x00\x00\x00\x01\x00hostname\x00"),
+			"", "hostname:4660",
+		},
+		{
+			[]byte("\x04\x01\x12\x34\x00\x00\x00\x01userid\x00\x00"),
+			"userid", ":4660",
+		},
+		{
+			[]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)
+		}
+		if req.Username != test.userid {
+			t.Errorf("%q → username %q (expected %q)", test.input,
+				req.Username, test.userid)
+		}
+		addr, err := net.ResolveTCPAddr("tcp", req.Target)
+		if err != nil {
+			t.Error("%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())
+		}
+	}
+
+	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.Username != test.userid {
+			t.Errorf("%q → username %q (expected %q)", test.input,
+				req.Username, test.userid)
+		}
+		if req.Target != test.target {
+			t.Errorf("%q → target %q (expected %q)", test.input,
+				req.Target, test.target)
+		}
+	}
+}





More information about the tor-commits mailing list