parent
9bffd9cf03
commit
4c831992aa
|
@ -1,5 +1,5 @@
|
|||
from __future__ import absolute_import
|
||||
from netlib import socks
|
||||
from netlib import socks, tcp
|
||||
|
||||
|
||||
class ProxyError(Exception):
|
||||
|
@ -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))
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
@ -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 = (
|
||||
|
@ -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()
|
||||
|
@ -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,6 +983,7 @@ 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",
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue