[Pkg-privacy-commits] [golang-goptlib] 01/05: Imported Upstream version 0.6
Ximin Luo
infinity0 at debian.org
Thu May 12 15:19:17 UTC 2016
This is an automated email from the git hooks/post-receive script.
infinity0 pushed a commit to branch master
in repository golang-goptlib.
commit b6511499081afafad9bf5ca59de1b2216500bf06
Author: Ximin Luo <infinity0 at debian.org>
Date: Thu May 12 17:03:39 2016 +0200
Imported Upstream version 0.6
---
README | 3 +-
examples/dummy-client/dummy-client.go | 2 +-
examples/dummy-server/dummy-server.go | 2 +-
pt.go | 76 +++---
pt_test.go | 124 +++------
socks.go | 386 +++++++++++++++++++++-----
socks_test.go | 495 ++++++++++++++++++++++++----------
7 files changed, 762 insertions(+), 326 deletions(-)
diff --git a/README b/README
index c4ca377..dc605d1 100644
--- a/README
+++ b/README
@@ -1,8 +1,9 @@
goptlib is a library for writing Tor pluggable transports in Go.
-https://gitweb.torproject.org/torspec.git/tree/pt-spec.txt
+https://spec.torproject.org/pt-spec
https://gitweb.torproject.org/torspec.git/tree/proposals/196-transport-control-ports.txt
https://gitweb.torproject.org/torspec.git/tree/proposals/217-ext-orport-auth.txt
+https://gitweb.torproject.org/torspec.git/tree/proposals/232-pluggable-transports-through-proxy.txt
To download a copy of the library into $GOPATH:
go get git.torproject.org/pluggable-transports/goptlib.git
diff --git a/examples/dummy-client/dummy-client.go b/examples/dummy-client/dummy-client.go
index 045a8f2..74843fd 100644
--- a/examples/dummy-client/dummy-client.go
+++ b/examples/dummy-client/dummy-client.go
@@ -83,7 +83,7 @@ func acceptLoop(ln *pt.SocksListener) error {
func main() {
var err error
- ptInfo, err = pt.ClientSetup([]string{"dummy"})
+ ptInfo, err = pt.ClientSetup(nil)
if err != nil {
os.Exit(1)
}
diff --git a/examples/dummy-server/dummy-server.go b/examples/dummy-server/dummy-server.go
index 70251ea..933630b 100644
--- a/examples/dummy-server/dummy-server.go
+++ b/examples/dummy-server/dummy-server.go
@@ -80,7 +80,7 @@ func acceptLoop(ln net.Listener) error {
func main() {
var err error
- ptInfo, err = pt.ServerSetup([]string{"dummy"})
+ ptInfo, err = pt.ServerSetup(nil)
if err != nil {
os.Exit(1)
}
diff --git a/pt.go b/pt.go
index a8c815e..82c42d5 100644
--- a/pt.go
+++ b/pt.go
@@ -35,7 +35,7 @@
// ...
// func main() {
// var err error
-// ptInfo, err = pt.ClientSetup([]string{"foo"})
+// ptInfo, err = pt.ClientSetup(nil)
// if err != nil {
// os.Exit(1)
// }
@@ -92,7 +92,7 @@
// ...
// func main() {
// var err error
-// ptInfo, err = pt.ServerSetup([]string{"foo"})
+// ptInfo, err = pt.ServerSetup(nil)
// if err != nil {
// os.Exit(1)
// }
@@ -117,7 +117,7 @@
// the example programs dummy-client and dummy-server.
//
// Tor pluggable transports specification:
-// https://gitweb.torproject.org/torspec.git/tree/pt-spec.txt.
+// https://spec.torproject.org/pt-spec
//
// Extended ORPort:
// https://gitweb.torproject.org/torspec.git/tree/proposals/196-transport-control-ports.txt.
@@ -128,10 +128,11 @@
// 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 (
@@ -377,17 +378,13 @@ func MakeStateDir() (string, error) {
return dir, err
}
-// Get the intersection of the method names offered by Tor and those in
-// methodNames. This function reads the environment variable
-// TOR_PT_CLIENT_TRANSPORTS.
-func getClientTransports(star []string) ([]string, error) {
+// Get the list of method names requested by Tor. This function reads the
+// environment variable TOR_PT_CLIENT_TRANSPORTS.
+func getClientTransports() ([]string, error) {
clientTransports, err := getenvRequired("TOR_PT_CLIENT_TRANSPORTS")
if err != nil {
return nil, err
}
- if clientTransports == "*" {
- return star, nil
- }
return strings.Split(clientTransports, ","), nil
}
@@ -436,9 +433,8 @@ type ClientInfo struct {
}
// Check the client pluggable transports environment, emitting an error message
-// and returning a non-nil error if any error is encountered. star is the list
-// of method names to use in case "*" is requested by Tor. Returns a ClientInfo
-// struct.
+// and returning a non-nil error if any error is encountered. Returns a
+// ClientInfo struct.
//
// If your program needs to know whether to call ClientSetup or ServerSetup
// (i.e., if the same program can be run as either a client or a server), check
@@ -448,14 +444,20 @@ type ClientInfo struct {
// } else {
// // Server mode; call pt.ServerSetup.
// }
-func ClientSetup(star []string) (info ClientInfo, err error) {
+//
+// Always pass nil for the unused single parameter. In the past, the parameter
+// was a list of transport names to use in case Tor requested "*". That feature
+// was never implemented and has been removed from the pluggable transports
+// specification.
+// https://trac.torproject.org/projects/tor/ticket/15612
+func ClientSetup(_ []string) (info ClientInfo, err error) {
ver, err := getManagedTransportVer()
if err != nil {
return
}
line("VERSION", ver)
- info.MethodNames, err = getClientTransports(star)
+ info.MethodNames, err = getClientTransports()
if err != nil {
return
}
@@ -537,11 +539,9 @@ func filterBindaddrs(addrs []Bindaddr, methodNames []string) []Bindaddr {
}
// Return an array of Bindaddrs, being the contents of TOR_PT_SERVER_BINDADDR
-// with keys filtered by TOR_PT_SERVER_TRANSPORTS. If TOR_PT_SERVER_TRANSPORTS
-// is "*", then keys are filtered by the entries in star instead.
-// Transport-specific options from TOR_PT_SERVER_TRANSPORT_OPTIONS are assigned
-// to the Options member.
-func getServerBindaddrs(star []string) ([]Bindaddr, error) {
+// with keys filtered by TOR_PT_SERVER_TRANSPORTS. Transport-specific options
+// from TOR_PT_SERVER_TRANSPORT_OPTIONS are assigned to the Options member.
+func getServerBindaddrs() ([]Bindaddr, error) {
var result []Bindaddr
// Parse the list of server transport options.
@@ -578,11 +578,7 @@ func getServerBindaddrs(star []string) ([]Bindaddr, error) {
if err != nil {
return nil, err
}
- if serverTransports == "*" {
- result = filterBindaddrs(result, star)
- } else {
- result = filterBindaddrs(result, strings.Split(serverTransports, ","))
- }
+ result = filterBindaddrs(result, strings.Split(serverTransports, ","))
return result, nil
}
@@ -634,10 +630,9 @@ type ServerInfo struct {
}
// Check the server pluggable transports environment, emitting an error message
-// and returning a non-nil error if any error is encountered. star is the list
-// of method names to use in case "*" is requested by Tor. Resolves the various
-// requested bind addresses, the server ORPort and extended ORPort, and reads
-// the auth cookie file. Returns a ServerInfo struct.
+// and returning a non-nil error if any error is encountered. Resolves the
+// various requested bind addresses, the server ORPort and extended ORPort, and
+// reads the auth cookie file. Returns a ServerInfo struct.
//
// If your program needs to know whether to call ClientSetup or ServerSetup
// (i.e., if the same program can be run as either a client or a server), check
@@ -647,14 +642,20 @@ type ServerInfo struct {
// } else {
// // Server mode; call pt.ServerSetup.
// }
-func ServerSetup(star []string) (info ServerInfo, err error) {
+//
+// Always pass nil for the unused single parameter. In the past, the parameter
+// was a list of transport names to use in case Tor requested "*". That feature
+// was never implemented and has been removed from the pluggable transports
+// specification.
+// https://trac.torproject.org/projects/tor/ticket/15612
+func ServerSetup(_ []string) (info ServerInfo, err error) {
ver, err := getManagedTransportVer()
if err != nil {
return
}
line("VERSION", ver)
- info.Bindaddrs, err = getServerBindaddrs(star)
+ info.Bindaddrs, err = getServerBindaddrs()
if err != nil {
return
}
@@ -668,18 +669,23 @@ func ServerSetup(star []string) (info ServerInfo, err error) {
}
}
+ info.AuthCookiePath = getenv("TOR_PT_AUTH_COOKIE_FILE")
+
extendedOrPort := getenv("TOR_PT_EXTENDED_SERVER_PORT")
if extendedOrPort != "" {
+ if info.AuthCookiePath == "" {
+ err = envError("need TOR_PT_AUTH_COOKIE_FILE environment variable with TOR_PT_EXTENDED_SERVER_PORT")
+ return
+ }
info.ExtendedOrAddr, err = resolveAddr(extendedOrPort)
if err != nil {
err = envError(fmt.Sprintf("cannot resolve TOR_PT_EXTENDED_SERVER_PORT %q: %s", extendedOrPort, err.Error()))
return
}
}
- info.AuthCookiePath = getenv("TOR_PT_AUTH_COOKIE_FILE")
// Need either OrAddr or ExtendedOrAddr.
- if info.OrAddr == nil && (info.ExtendedOrAddr == nil || info.AuthCookiePath == "") {
+ if info.OrAddr == nil && info.ExtendedOrAddr == nil {
err = envError("need TOR_PT_ORPORT or TOR_PT_EXTENDED_SERVER_PORT environment variable")
return
}
diff --git a/pt_test.go b/pt_test.go
index c0540b5..0d641fe 100644
--- a/pt_test.go
+++ b/pt_test.go
@@ -138,82 +138,57 @@ func tcpAddrsEqual(a, b *net.TCPAddr) bool {
func TestGetClientTransports(t *testing.T) {
tests := [...]struct {
- ptServerClientTransports string
- star []string
- expected []string
+ ptClientTransports string
+ expected []string
}{
{
- "*",
- []string{},
- []string{},
- },
- {
- "*",
- []string{"alpha", "beta", "gamma"},
- []string{"alpha", "beta", "gamma"},
- },
- {
- "alpha,beta,gamma",
- []string{"alpha", "beta", "gamma"},
- []string{"alpha", "beta", "gamma"},
+ "alpha",
+ []string{"alpha"},
},
{
"alpha,beta",
- []string{"alpha", "beta", "gamma"},
[]string{"alpha", "beta"},
},
{
- "alpha",
- []string{"alpha", "beta", "gamma"},
- []string{"alpha"},
- },
- {
"alpha,beta,gamma",
- []string{},
[]string{"alpha", "beta", "gamma"},
},
+ // In the past, "*" meant to return all known transport names.
+ // But now it has no special meaning.
+ // https://trac.torproject.org/projects/tor/ticket/15612
{
- "alpha,beta",
- []string{},
- []string{"alpha", "beta"},
- },
- {
- "alpha",
- []string{},
- []string{"alpha"},
+ "*",
+ []string{"*"},
},
- // my reading of pt-spec.txt says that "*" has special meaning
- // only when it is the entirety of the environment variable.
{
"alpha,*,gamma",
- []string{"alpha", "beta", "gamma"},
[]string{"alpha", "*", "gamma"},
},
+ // No escaping is defined for TOR_PT_CLIENT_TRANSPORTS.
{
- "alpha",
- []string{"beta"},
- []string{"alpha"},
+ `alpha\,beta`,
+ []string{`alpha\`, `beta`},
},
}
Stdout = ioutil.Discard
os.Clearenv()
- _, err := getClientTransports([]string{"alpha", "beta", "gamma"})
+ _, err := getClientTransports()
if err == nil {
t.Errorf("empty environment unexpectedly succeeded")
}
for _, test := range tests {
- os.Setenv("TOR_PT_CLIENT_TRANSPORTS", test.ptServerClientTransports)
- output, err := getClientTransports(test.star)
+ os.Setenv("TOR_PT_CLIENT_TRANSPORTS", test.ptClientTransports)
+ output, err := getClientTransports()
if err != nil {
t.Errorf("TOR_PT_CLIENT_TRANSPORTS=%q unexpectedly returned an error: %s",
- test.ptServerClientTransports, err)
+ test.ptClientTransports, err)
}
if !stringSetsEqual(output, test.expected) {
- t.Errorf("TOR_PT_CLIENT_TRANSPORTS=%q %q → %q (expected %q)",
- test.ptServerClientTransports, test.star, output, test.expected)
+ t.Errorf("TOR_PT_CLIENT_TRANSPORTS=%q → %q (expected %q)",
+ test.ptClientTransports, output, test.expected)
}
}
}
@@ -296,48 +271,48 @@ func TestGetServerBindaddrs(t *testing.T) {
ptServerBindaddr string
ptServerTransports string
ptServerTransportOptions string
- star []string
}{
// bad TOR_PT_SERVER_BINDADDR
{
"alpha",
"alpha",
"",
- []string{"alpha", "beta", "gamma"},
},
{
"alpha-1.2.3.4",
"alpha",
"",
- []string{"alpha", "beta", "gamma"},
},
// missing TOR_PT_SERVER_TRANSPORTS
{
"alpha-1.2.3.4:1111",
"",
"alpha:key=value",
- []string{"alpha"},
},
// bad TOR_PT_SERVER_TRANSPORT_OPTIONS
{
"alpha-1.2.3.4:1111",
"alpha",
"key=value",
- []string{"alpha"},
+ },
+ // no escaping is defined for TOR_PT_SERVER_TRANSPORTS or
+ // TOR_PT_SERVER_BINDADDR.
+ {
+ `alpha\,beta-1.2.3.4:1111`,
+ `alpha\,beta`,
+ "",
},
}
goodTests := [...]struct {
ptServerBindaddr string
ptServerTransports string
ptServerTransportOptions string
- star []string
expected []Bindaddr
}{
{
"alpha-1.2.3.4:1111,beta-[1:2::3:4]:2222",
"alpha,beta,gamma",
"alpha:k1=v1,beta:k2=v2,gamma:k3=v3",
- []string{"alpha", "beta"},
[]Bindaddr{
{"alpha", &net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 1111}, Args{"k1": []string{"v1"}}},
{"beta", &net.TCPAddr{IP: net.ParseIP("1:2::3:4"), Port: 2222}, Args{"k2": []string{"v2"}}},
@@ -347,33 +322,12 @@ func TestGetServerBindaddrs(t *testing.T) {
"alpha-1.2.3.4:1111",
"xxx",
"",
- []string{"alpha", "beta", "gamma"},
[]Bindaddr{},
},
{
"alpha-1.2.3.4:1111",
"alpha,beta,gamma",
"",
- []string{},
- []Bindaddr{
- {"alpha", &net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 1111}, Args{}},
- },
- },
- {
- "alpha-1.2.3.4:1111,beta-[1:2::3:4]:2222",
- "*",
- "",
- []string{"alpha", "beta"},
- []Bindaddr{
- {"alpha", &net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 1111}, Args{}},
- {"beta", &net.TCPAddr{IP: net.ParseIP("1:2::3:4"), Port: 2222}, Args{}},
- },
- },
- {
- "alpha-1.2.3.4:1111,beta-[1:2::3:4]:2222",
- "*",
- "",
- []string{"alpha", "gamma"},
[]Bindaddr{
{"alpha", &net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 1111}, Args{}},
},
@@ -382,18 +336,26 @@ func TestGetServerBindaddrs(t *testing.T) {
"trebuchet-127.0.0.1:1984,ballista-127.0.0.1:4891",
"trebuchet,ballista",
"trebuchet:secret=nou;trebuchet:cache=/tmp/cache;ballista:secret=yes",
- []string{"trebuchet", "ballista"},
[]Bindaddr{
{"trebuchet", &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1984}, Args{"secret": []string{"nou"}, "cache": []string{"/tmp/cache"}}},
{"ballista", &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 4891}, Args{"secret": []string{"yes"}}},
},
},
+ // In the past, "*" meant to return all known transport names.
+ // But now it has no special meaning.
+ // https://trac.torproject.org/projects/tor/ticket/15612
+ {
+ "alpha-1.2.3.4:1111,beta-[1:2::3:4]:2222",
+ "*",
+ "",
+ []Bindaddr{},
+ },
}
Stdout = ioutil.Discard
os.Clearenv()
- _, err := getServerBindaddrs([]string{"alpha", "beta", "gamma"})
+ _, err := getServerBindaddrs()
if err == nil {
t.Errorf("empty environment unexpectedly succeeded")
}
@@ -402,10 +364,10 @@ func TestGetServerBindaddrs(t *testing.T) {
os.Setenv("TOR_PT_SERVER_BINDADDR", test.ptServerBindaddr)
os.Setenv("TOR_PT_SERVER_TRANSPORTS", test.ptServerTransports)
os.Setenv("TOR_PT_SERVER_TRANSPORT_OPTIONS", test.ptServerTransportOptions)
- _, err := getServerBindaddrs(test.star)
+ _, err := getServerBindaddrs()
if err == nil {
- t.Errorf("TOR_PT_SERVER_BINDADDR=%q TOR_PT_SERVER_TRANSPORTS=%q TOR_PT_SERVER_TRANSPORT_OPTIONS=%q %q unexpectedly succeeded",
- test.ptServerBindaddr, test.ptServerTransports, test.ptServerTransportOptions, test.star)
+ t.Errorf("TOR_PT_SERVER_BINDADDR=%q TOR_PT_SERVER_TRANSPORTS=%q TOR_PT_SERVER_TRANSPORT_OPTIONS=%q unexpectedly succeeded",
+ test.ptServerBindaddr, test.ptServerTransports, test.ptServerTransportOptions)
}
}
@@ -413,14 +375,14 @@ func TestGetServerBindaddrs(t *testing.T) {
os.Setenv("TOR_PT_SERVER_BINDADDR", test.ptServerBindaddr)
os.Setenv("TOR_PT_SERVER_TRANSPORTS", test.ptServerTransports)
os.Setenv("TOR_PT_SERVER_TRANSPORT_OPTIONS", test.ptServerTransportOptions)
- output, err := getServerBindaddrs(test.star)
+ output, err := getServerBindaddrs()
if err != nil {
- t.Errorf("TOR_PT_SERVER_BINDADDR=%q TOR_PT_SERVER_TRANSPORTS=%q TOR_PT_SERVER_TRANSPORT_OPTIONS=%q %q unexpectedly returned an error: %s",
- test.ptServerBindaddr, test.ptServerTransports, test.ptServerTransportOptions, test.star, err)
+ t.Errorf("TOR_PT_SERVER_BINDADDR=%q TOR_PT_SERVER_TRANSPORTS=%q TOR_PT_SERVER_TRANSPORT_OPTIONS=%q unexpectedly returned an error: %s",
+ test.ptServerBindaddr, test.ptServerTransports, test.ptServerTransportOptions, err)
}
if !bindaddrSetsEqual(output, test.expected) {
- t.Errorf("TOR_PT_SERVER_BINDADDR=%q TOR_PT_SERVER_TRANSPORTS=%q TOR_PT_SERVER_TRANSPORT_OPTIONS=%q %q → %q (expected %q)",
- test.ptServerBindaddr, test.ptServerTransports, test.ptServerTransportOptions, test.star, output, test.expected)
+ t.Errorf("TOR_PT_SERVER_BINDADDR=%q TOR_PT_SERVER_TRANSPORTS=%q TOR_PT_SERVER_TRANSPORT_OPTIONS=%q → %q (expected %q)",
+ test.ptServerBindaddr, test.ptServerTransports, test.ptServerTransportOptions, output, test.expected)
}
}
}
diff --git a/socks.go b/socks.go
index 9a764b8..29827d9 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.
@@ -140,7 +179,7 @@ retry:
conn.Close()
goto retry
}
- conn.Req, err = readSocks4aConnect(conn)
+ conn.Req, err = socks5Handshake(conn)
if err != nil {
conn.Close()
goto retry
@@ -153,58 +192,251 @@ retry:
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
}
@@ -212,34 +444,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 7fee46a..d82e823 100644
--- a/socks_test.go
+++ b/socks_test.go
@@ -1,166 +1,118 @@
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)
}
}
-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)
- }
+// 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)
}
}
@@ -267,3 +219,256 @@ func TestAcceptErrors(t *testing.T) {
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)
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/golang-goptlib.git
More information about the Pkg-privacy-commits
mailing list