aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libpathod/pathoc.py35
-rw-r--r--test/test_pathoc.py25
-rw-r--r--test/tutils.py8
3 files changed, 66 insertions, 2 deletions
diff --git a/libpathod/pathoc.py b/libpathod/pathoc.py
index 4a2ac5cd..39d3c3d6 100644
--- a/libpathod/pathoc.py
+++ b/libpathod/pathoc.py
@@ -11,7 +11,7 @@ import threading
import OpenSSL.crypto
-from netlib import tcp, http, http2, certutils, websockets
+from netlib import tcp, http, http2, certutils, websockets, socks
import language.http
import language.websockets
@@ -254,6 +254,39 @@ class Pathoc(tcp.TCPClient):
)
http.read_headers(self.rfile)
+ def socks_connect(self, connect_to):
+ try:
+ client_greet = socks.ClientGreeting(socks.VERSION.SOCKS5, [socks.METHOD.NO_AUTHENTICATION_REQUIRED])
+ client_greet.to_file(self.wfile)
+ self.wfile.flush()
+
+ server_greet = socks.ServerGreeting.from_file(self.rfile)
+ server_greet.assert_socks5()
+ if server_greet.method != socks.METHOD.NO_AUTHENTICATION_REQUIRED:
+ raise socks.SocksError(
+ socks.METHOD.NO_ACCEPTABLE_METHODS,
+ "pathoc only supports SOCKS without authentication"
+ )
+
+ connect_request = socks.Message(
+ socks.VERSION.SOCKS5,
+ socks.CMD.CONNECT,
+ socks.ATYP.DOMAINNAME,
+ tcp.Address.wrap(connect_to)
+ )
+ connect_request.to_file(self.wfile)
+ self.wfile.flush()
+
+ connect_reply = socks.Message.from_file(self.rfile)
+ connect_reply.assert_socks5()
+ if connect_reply.msg != socks.REP.SUCCEEDED:
+ raise socks.SocksError(
+ connect_reply.msg,
+ "SOCKS server error"
+ )
+ except (socks.SocksError, tcp.NetLibDisconnect) as e:
+ raise PathocError(str(e))
+
def connect(self, connect_to=None, showssl=False, fp=sys.stdout):
"""
connect_to: A (host, port) tuple, which will be connected to with
diff --git a/test/test_pathoc.py b/test/test_pathoc.py
index 6b037bcc..fb8d348a 100644
--- a/test/test_pathoc.py
+++ b/test/test_pathoc.py
@@ -4,7 +4,7 @@ import re
import OpenSSL
from mock import Mock
-from netlib import tcp, http, http2
+from netlib import tcp, http, http2, socks
from libpathod import pathoc, test, version, pathod, language
import tutils
@@ -230,6 +230,29 @@ class TestDaemon(_TestDaemon):
)
c.http_connect(to)
+ def test_socks_connect(self):
+ to = ("foobar", 80)
+ c = pathoc.Pathoc(("127.0.0.1", self.d.port), fp=None)
+ c.rfile, c.wfile = tutils.treader(""), cStringIO.StringIO()
+ tutils.raises(pathoc.PathocError, c.socks_connect, to)
+
+ c.rfile = tutils.treader(
+ "\x05\xEE"
+ )
+ tutils.raises("SOCKS without authentication", c.socks_connect, ("example.com", 0xDEAD))
+
+ c.rfile = tutils.treader(
+ "\x05\x00" +
+ "\x05\xEE\x00\x03\x0bexample.com\xDE\xAD"
+ )
+ tutils.raises("SOCKS server error", c.socks_connect, ("example.com", 0xDEAD))
+
+ c.rfile = tutils.treader(
+ "\x05\x00" +
+ "\x05\x00\x00\x03\x0bexample.com\xDE\xAD"
+ )
+ c.socks_connect(("example.com", 0xDEAD))
+
class TestDaemonHTTP2(_TestDaemon):
ssl = True
diff --git a/test/tutils.py b/test/tutils.py
index 9d9b687d..f7fcc312 100644
--- a/test/tutils.py
+++ b/test/tutils.py
@@ -5,8 +5,16 @@ import shutil
import cStringIO
from contextlib import contextmanager
from libpathod import utils, test, pathoc, pathod, language
+from netlib import tcp
import requests
+def treader(bytes):
+ """
+ Construct a tcp.Read object from bytes.
+ """
+ fp = cStringIO.StringIO(bytes)
+ return tcp.Reader(fp)
+
class DaemonTests(object):
noweb = False