475 lines
14 KiB
Go
475 lines
14 KiB
Go
package pt
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/hex"
|
|
"errors"
|
|
"io"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// testReadWriter is a bytes.Buffer backed io.ReadWriter used for testing. The
|
|
// Read and Write routines are to be used by the component being tested. Data
|
|
// can be written to and read back via the writeHex and readHex routines.
|
|
type testReadWriter struct {
|
|
readBuf bytes.Buffer
|
|
writeBuf bytes.Buffer
|
|
}
|
|
|
|
func (c *testReadWriter) Read(buf []byte) (n int, err error) {
|
|
return c.readBuf.Read(buf)
|
|
}
|
|
|
|
func (c *testReadWriter) Write(buf []byte) (n int, err error) {
|
|
return c.writeBuf.Write(buf)
|
|
}
|
|
|
|
func (c *testReadWriter) writeHex(str string) (n int, err error) {
|
|
var buf []byte
|
|
if buf, err = hex.DecodeString(str); err != nil {
|
|
return
|
|
}
|
|
return c.readBuf.Write(buf)
|
|
}
|
|
|
|
func (c *testReadWriter) readHex() string {
|
|
return hex.EncodeToString(c.writeBuf.Bytes())
|
|
}
|
|
|
|
func (c *testReadWriter) toBufio() *bufio.ReadWriter {
|
|
return bufio.NewReadWriter(bufio.NewReader(c), bufio.NewWriter(c))
|
|
}
|
|
|
|
func (c *testReadWriter) reset() {
|
|
c.readBuf.Reset()
|
|
c.writeBuf.Reset()
|
|
}
|
|
|
|
// TestAuthInvalidVersion tests auth negotiation with an invalid version.
|
|
func TestAuthInvalidVersion(t *testing.T) {
|
|
c := new(testReadWriter)
|
|
|
|
// VER = 03, NMETHODS = 01, METHODS = [00]
|
|
c.writeHex("030100")
|
|
if _, err := socksNegotiateAuth(c.toBufio()); err == nil {
|
|
t.Error("socksNegotiateAuth(InvalidVersion) succeded")
|
|
}
|
|
}
|
|
|
|
// TestAuthInvalidNMethods tests auth negotiaton with no methods.
|
|
func TestAuthInvalidNMethods(t *testing.T) {
|
|
c := new(testReadWriter)
|
|
var err error
|
|
var method byte
|
|
|
|
// VER = 05, NMETHODS = 00
|
|
c.writeHex("0500")
|
|
if method, err = socksNegotiateAuth(c.toBufio()); err != nil {
|
|
t.Error("socksNegotiateAuth(No Methods) failed:", err)
|
|
}
|
|
if method != socksAuthNoAcceptableMethods {
|
|
t.Error("socksNegotiateAuth(No Methods) picked unexpected method:", method)
|
|
}
|
|
if msg := c.readHex(); msg != "05ff" {
|
|
t.Error("socksNegotiateAuth(No Methods) invalid response:", msg)
|
|
}
|
|
}
|
|
|
|
// TestAuthNoneRequired tests auth negotiaton with NO AUTHENTICATION REQUIRED.
|
|
func TestAuthNoneRequired(t *testing.T) {
|
|
c := new(testReadWriter)
|
|
var err error
|
|
var method byte
|
|
|
|
// VER = 05, NMETHODS = 01, METHODS = [00]
|
|
c.writeHex("050100")
|
|
if method, err = socksNegotiateAuth(c.toBufio()); err != nil {
|
|
t.Error("socksNegotiateAuth(None) failed:", err)
|
|
}
|
|
if method != socksAuthNoneRequired {
|
|
t.Error("socksNegotiateAuth(None) unexpected method:", method)
|
|
}
|
|
if msg := c.readHex(); msg != "0500" {
|
|
t.Error("socksNegotiateAuth(None) invalid response:", msg)
|
|
}
|
|
}
|
|
|
|
// TestAuthUsernamePassword tests auth negotiation with USERNAME/PASSWORD.
|
|
func TestAuthUsernamePassword(t *testing.T) {
|
|
c := new(testReadWriter)
|
|
var err error
|
|
var method byte
|
|
|
|
// VER = 05, NMETHODS = 01, METHODS = [02]
|
|
c.writeHex("050102")
|
|
if method, err = socksNegotiateAuth(c.toBufio()); err != nil {
|
|
t.Error("socksNegotiateAuth(UsernamePassword) failed:", err)
|
|
}
|
|
if method != socksAuthUsernamePassword {
|
|
t.Error("socksNegotiateAuth(UsernamePassword) unexpected method:", method)
|
|
}
|
|
if msg := c.readHex(); msg != "0502" {
|
|
t.Error("socksNegotiateAuth(UsernamePassword) invalid response:", msg)
|
|
}
|
|
}
|
|
|
|
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)
|