aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libmproxy/proxy/primitives.py34
-rw-r--r--test/test_server.py95
-rw-r--r--test/tservers.py7
3 files changed, 83 insertions, 53 deletions
diff --git a/libmproxy/proxy/primitives.py b/libmproxy/proxy/primitives.py
index b01ddde3..923f84ca 100644
--- a/libmproxy/proxy/primitives.py
+++ b/libmproxy/proxy/primitives.py
@@ -1,5 +1,5 @@
from __future__ import absolute_import
-from netlib import socks
+from netlib import socks, tcp
class ProxyError(Exception):
@@ -67,7 +67,7 @@ class SSLSpoofMode(ProxyMode):
http_form_in = "relative"
http_form_out = "relative"
- def __init__(self, sslport):
+ def __init__(self, sslport):
self.sslport = sslport
def get_upstream_server(self, client_conn):
@@ -106,24 +106,11 @@ class Socks5ProxyMode(ProxyMode):
def __init__(self, sslports):
self.sslports = sslports
- @staticmethod
- def _assert_socks5(msg):
- if msg.ver != socks.VERSION.SOCKS5:
- if msg.ver == ord("G") and len(msg.methods) == ord("E"):
- guess = "Probably not a SOCKS request but a regular HTTP request. "
- else:
- guess = ""
- raise socks.SocksError(
- socks.REP.GENERAL_SOCKS_SERVER_FAILURE,
- guess +
- "Invalid SOCKS version. Expected 0x05, got 0x%x" %
- msg.ver)
-
def get_upstream_server(self, client_conn):
try:
# Parse Client Greeting
- client_greet = socks.ClientGreeting.from_file(client_conn.rfile)
- self._assert_socks5(client_greet)
+ client_greet = socks.ClientGreeting.from_file(client_conn.rfile, fail_early=True)
+ client_greet.assert_socks5()
if socks.METHOD.NO_AUTHENTICATION_REQUIRED not in client_greet.methods:
raise socks.SocksError(
socks.METHOD.NO_ACCEPTABLE_METHODS,
@@ -140,7 +127,7 @@ class Socks5ProxyMode(ProxyMode):
# Parse Connect Request
connect_request = socks.Message.from_file(client_conn.rfile)
- self._assert_socks5(connect_request)
+ connect_request.assert_socks5()
if connect_request.msg != socks.CMD.CONNECT:
raise socks.SocksError(
socks.REP.COMMAND_NOT_SUPPORTED,
@@ -153,9 +140,9 @@ class Socks5ProxyMode(ProxyMode):
connect_reply = socks.Message(
socks.VERSION.SOCKS5,
socks.REP.SUCCEEDED,
- socks.ATYP.DOMAINNAME,
+ connect_request.atyp,
# dummy value, we don't have an upstream connection yet.
- client_conn.address
+ connect_request.addr
)
connect_reply.to_file(client_conn.wfile)
client_conn.wfile.flush()
@@ -163,12 +150,7 @@ class Socks5ProxyMode(ProxyMode):
ssl = bool(connect_request.addr.port in self.sslports)
return ssl, ssl, connect_request.addr.host, connect_request.addr.port
- except socks.SocksError as e:
- msg = socks.Message(5, e.code, socks.ATYP.DOMAINNAME, repr(e))
- try:
- msg.to_file(client_conn.wfile)
- except:
- pass
+ except (socks.SocksError, tcp.NetLibError) as e:
raise ProxyError(502, "SOCKS5 mode failure: %s" % str(e))
diff --git a/test/test_server.py b/test/test_server.py
index 3726ec27..2805e936 100644
--- a/test/test_server.py
+++ b/test/test_server.py
@@ -2,7 +2,7 @@ import socket
import time
from libmproxy.proxy.config import HostMatcher
import libpathod
-from netlib import tcp, http_auth, http
+from netlib import tcp, http_auth, http, socks
from libpathod import pathoc, pathod
from netlib.certutils import SSLCert
import tutils
@@ -237,6 +237,7 @@ class TestHTTP(tservers.HTTPProxTest, CommonMixin, AppMixin):
for i in l:
if "serverdisconnect" in i:
return True
+
req = "get:'%s/p/200:b@1'"
p = self.pathoc()
assert p.request(req % self.server.urlbase)
@@ -355,8 +356,8 @@ class TestHTTPSUpstreamServerVerificationWTrustedCert(tservers.HTTPProxTest):
"""
ssl = True
ssloptions = pathod.SSLOptions(
- cn = "trusted-cert",
- certs = [
+ cn="trusted-cert",
+ certs=[
("trusted-cert", tutils.test_data.path("data/trusted-server.crt"))
])
@@ -364,7 +365,7 @@ class TestHTTPSUpstreamServerVerificationWTrustedCert(tservers.HTTPProxTest):
self.config.openssl_verification_mode_server = SSL.VERIFY_PEER
self.config.openssl_trusted_cadir_server = tutils.test_data.path(
"data/trusted-cadir/")
-
+
self.pathoc()
def test_verification_w_pemfile(self):
@@ -381,9 +382,9 @@ class TestHTTPSUpstreamServerVerificationWBadCert(tservers.HTTPProxTest):
"""
ssl = True
ssloptions = pathod.SSLOptions(
- cn = "untrusted-cert",
- certs = [
- ("untrusted-cert", tutils.test_data.path("data/untrusted-server.crt"))
+ cn="untrusted-cert",
+ certs=[
+ ("untrusted-cert", tutils.test_data.path("data/untrusted-server.crt"))
])
def test_default_verification_w_bad_cert(self):
@@ -414,7 +415,7 @@ class TestHTTPSNoCommonName(tservers.HTTPProxTest):
"""
ssl = True
ssloptions = pathod.SSLOptions(
- certs = [
+ certs=[
("*", tutils.test_data.path("data/no_common_name.pem"))
]
)
@@ -428,6 +429,43 @@ class TestReverse(tservers.ReverseProxTest, CommonMixin, TcpMixin):
reverse = True
+class TestSocks5(tservers.SocksModeTest):
+ def test_simple(self):
+ p = self.pathoc()
+ p.socks_connect(("localhost", self.server.port))
+ f = p.request("get:/p/200")
+ assert f.status_code == 200
+
+ def test_with_authentication_only(self):
+ p = self.pathoc()
+ f = p.request("get:/p/200")
+ assert f.status_code == 502
+ assert "SOCKS5 mode failure" in f.content
+
+ def test_no_connect(self):
+ """
+ mitmproxy doesn't support UDP or BIND SOCKS CMDs
+ """
+ p = self.pathoc()
+
+ socks.ClientGreeting(
+ socks.VERSION.SOCKS5,
+ [socks.METHOD.NO_AUTHENTICATION_REQUIRED]
+ ).to_file(p.wfile)
+ socks.Message(
+ socks.VERSION.SOCKS5,
+ socks.CMD.BIND,
+ socks.ATYP.DOMAINNAME,
+ ("example.com", 8080)
+ ).to_file(p.wfile)
+
+ p.wfile.flush()
+ p.rfile.read(2) # read server greeting
+ f = p.request("get:/p/200") # the request doesn't matter, error response from handshake will be read anyway.
+ assert f.status_code == 502
+ assert "SOCKS5 mode failure" in f.content
+
+
class TestSpoof(tservers.SpoofModeTest):
def test_http(self):
alist = (
@@ -444,7 +482,7 @@ class TestSpoof(tservers.SpoofModeTest):
assert l.server_conn.address
assert l.server_conn.address.host == a[0]
assert l.server_conn.address.port == a[1]
-
+
def test_http_without_host(self):
p = self.pathoc()
f = p.request("get:/p/304:r")
@@ -468,7 +506,7 @@ class TestSSLSpoof(tservers.SSLSpoofModeTest):
assert l.server_conn.address
assert l.server_conn.address.host == a[0]
assert l.server_conn.address.port == a[1]
-
+
def test_https_without_sni(self):
a = ("localhost", self.server.port)
self.config.mode.sslport = a[1]
@@ -556,7 +594,7 @@ class TestProxy(tservers.HTTPProxTest):
connection.close()
request, response = self.master.state.view[
- 0].request, self.master.state.view[0].response
+ 0].request, self.master.state.view[0].response
assert response.code == 304 # sanity test for our low level request
# time.sleep might be a little bit shorter than a second
assert 0.95 < (request.timestamp_end - request.timestamp_start) < 1.2
@@ -718,7 +756,6 @@ class TestStreamRequest(tservers.HTTPProxTest):
assert self.server.last_log()
def test_stream_chunked(self):
-
connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connection.connect(("127.0.0.1", self.proxy.port))
fconn = connection.makefile()
@@ -736,8 +773,8 @@ class TestStreamRequest(tservers.HTTPProxTest):
chunks = list(
content for _,
- content,
- _ in http.read_http_body_chunked(
+ content,
+ _ in http.read_http_body_chunked(
fconn,
headers,
None,
@@ -839,9 +876,9 @@ class TestUpstreamProxy(tservers.HTTPUpstreamProxTest, CommonMixin, AppMixin):
class TestUpstreamProxySSL(
- tservers.HTTPUpstreamProxTest,
- CommonMixin,
- TcpMixin):
+ tservers.HTTPUpstreamProxTest,
+ CommonMixin,
+ TcpMixin):
ssl = True
def _host_pattern_on(self, attr):
@@ -911,10 +948,12 @@ class TestUpstreamProxySSL(
"""
https://github.com/mitmproxy/mitmproxy/issues/313
"""
+
def handle_request(f):
f.request.httpversion = (1, 0)
del f.request.headers["Content-Length"]
f.reply()
+
_handle_request = self.chain[0].tmaster.handle_request
self.chain[0].tmaster.handle_request = handle_request
try:
@@ -932,6 +971,7 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxTest):
If we have a disconnect on a secure connection that's transparently proxified to
an upstream http proxy, we need to send the CONNECT request again.
"""
+
def kill_requests(master, attr, exclude):
k = [0] # variable scope workaround: put into array
_func = getattr(master, attr)
@@ -943,21 +983,22 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxTest):
f.error = Error("terminated")
f.reply(KILL)
return _func(f)
+
setattr(master, attr, handler)
kill_requests(self.chain[1].tmaster, "handle_request",
exclude=[
- # fail first request
+ # fail first request
2, # allow second request
- ])
+ ])
kill_requests(self.chain[0].tmaster, "handle_request",
exclude=[
1, # CONNECT
- # fail first request
+ # fail first request
3, # reCONNECT
4, # request
- ])
+ ])
p = self.pathoc()
req = p.request("get:'/p/418:b\"content\"'")
@@ -979,18 +1020,18 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxTest):
assert self.proxy.tmaster.state.flows[1].request.form_in == "relative"
assert self.chain[0].tmaster.state.flows[
- 0].request.form_in == "authority"
+ 0].request.form_in == "authority"
assert self.chain[0].tmaster.state.flows[
- 1].request.form_in == "relative"
+ 1].request.form_in == "relative"
assert self.chain[0].tmaster.state.flows[
- 2].request.form_in == "authority"
+ 2].request.form_in == "authority"
assert self.chain[0].tmaster.state.flows[
- 3].request.form_in == "relative"
+ 3].request.form_in == "relative"
assert self.chain[1].tmaster.state.flows[
- 0].request.form_in == "relative"
+ 0].request.form_in == "relative"
assert self.chain[1].tmaster.state.flows[
- 1].request.form_in == "relative"
+ 1].request.form_in == "relative"
req = p.request("get:'/p/418:b\"content2\"'")
diff --git a/test/tservers.py b/test/tservers.py
index 96e340e9..3c3cb923 100644
--- a/test/tservers.py
+++ b/test/tservers.py
@@ -268,6 +268,13 @@ class ReverseProxTest(ProxTestBase):
return p.request(q)
+class SocksModeTest(HTTPProxTest):
+ @classmethod
+ def get_proxy_config(cls):
+ d = ProxTestBase.get_proxy_config()
+ d["mode"] = "socks5"
+ return d
+
class SpoofModeTest(ProxTestBase):
ssl = None