[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [goptlib/master] Move socks code into the pt package.
commit 37be077e66cbff6403e054b2351e2eab3037a292
Author: David Fifield <david@xxxxxxxxxxxxxxx>
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)
+ }
+ }
+}
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits