mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-23 08:11:00 +00:00
lay the foundations for --(in|out)(abs|rel) command line switches, as proposed in https://groups.google.com/forum/#!topic/mitmproxy/nApno2TXS0c
This commit is contained in:
parent
dd3aedca01
commit
78750a8b4d
@ -843,6 +843,12 @@ class HttpAuthenticationError(Exception):
|
|||||||
|
|
||||||
class HTTPHandler(ProtocolHandler, TemporaryServerChangeMixin):
|
class HTTPHandler(ProtocolHandler, TemporaryServerChangeMixin):
|
||||||
|
|
||||||
|
def __init__(self, c):
|
||||||
|
super(HTTPHandler, self).__init__(c)
|
||||||
|
self.expected_form_in = c.config.http_form_in
|
||||||
|
self.expected_form_out = c.config.http_form_out
|
||||||
|
self.skip_authentication = False
|
||||||
|
|
||||||
def handle_messages(self):
|
def handle_messages(self):
|
||||||
while self.handle_flow():
|
while self.handle_flow():
|
||||||
pass
|
pass
|
||||||
@ -877,13 +883,15 @@ class HTTPHandler(ProtocolHandler, TemporaryServerChangeMixin):
|
|||||||
flow = HTTPFlow(self.c.client_conn, self.c.server_conn, self.change_server)
|
flow = HTTPFlow(self.c.client_conn, self.c.server_conn, self.change_server)
|
||||||
try:
|
try:
|
||||||
req = HTTPRequest.from_stream(self.c.client_conn.rfile,
|
req = HTTPRequest.from_stream(self.c.client_conn.rfile,
|
||||||
body_size_limit=self.c.config.body_size_limit)
|
body_size_limit=self.c.config.body_size_limit)
|
||||||
self.c.log("request", [req._assemble_first_line(req.form_in)])
|
self.c.log("request", [req._assemble_first_line(req.form_in)])
|
||||||
self.process_request(flow, req)
|
send_upstream = self.process_request(flow, req)
|
||||||
|
if not send_upstream:
|
||||||
|
return True
|
||||||
|
|
||||||
# Be careful NOT to assign the request to the flow before
|
# Be careful NOT to assign the request to the flow before
|
||||||
# process_request completes. This is because the call can raise an
|
# process_request completes. This is because the call can raise an
|
||||||
# exception. If the requets object is already attached, this results
|
# exception. If the request object is already attached, this results
|
||||||
# in an Error object that has an attached request that has not been
|
# in an Error object that has an attached request that has not been
|
||||||
# sent through to the Master.
|
# sent through to the Master.
|
||||||
flow.request = req
|
flow.request = req
|
||||||
@ -1004,44 +1012,48 @@ class HTTPHandler(ProtocolHandler, TemporaryServerChangeMixin):
|
|||||||
Upgrade the connection to SSL after an authority (CONNECT) request has been made.
|
Upgrade the connection to SSL after an authority (CONNECT) request has been made.
|
||||||
"""
|
"""
|
||||||
self.c.log("Received CONNECT request. Upgrading to SSL...")
|
self.c.log("Received CONNECT request. Upgrading to SSL...")
|
||||||
self.c.mode = "transparent"
|
self.expected_form_in = "relative"
|
||||||
self.c.determine_conntype()
|
self.expected_form_out = "relative"
|
||||||
self.c.establish_ssl(server=True, client=True)
|
self.c.establish_ssl(server=True, client=True)
|
||||||
self.c.log("Upgrade to SSL completed.")
|
self.c.log("Upgrade to SSL completed.")
|
||||||
raise ConnectionTypeChange
|
|
||||||
|
|
||||||
def process_request(self, flow, request):
|
def process_request(self, flow, request):
|
||||||
if self.c.mode == "regular":
|
|
||||||
|
if not self.skip_authentication:
|
||||||
self.authenticate(request)
|
self.authenticate(request)
|
||||||
if request.form_in == "authority" and self.c.client_conn.ssl_established:
|
|
||||||
raise http.HttpError(502, "Must not CONNECT on already encrypted connection")
|
|
||||||
|
|
||||||
# If we have a CONNECT request, we might need to intercept
|
|
||||||
if request.form_in == "authority":
|
if request.form_in == "authority":
|
||||||
directly_addressed_at_mitmproxy = (self.c.mode == "regular" and not self.c.config.forward_proxy)
|
if self.c.client_conn.ssl_established:
|
||||||
if directly_addressed_at_mitmproxy:
|
raise http.HttpError(400, "Must not CONNECT on already encrypted connection")
|
||||||
self.c.set_server_address((request.host, request.port), AddressPriority.FROM_PROTOCOL)
|
|
||||||
flow.server_conn = self.c.server_conn # Update server_conn attribute on the flow
|
|
||||||
self.c.client_conn.wfile.write(
|
|
||||||
'HTTP/1.1 200 Connection established\r\n' +
|
|
||||||
('Proxy-agent: %s\r\n' % self.c.server_version) +
|
|
||||||
'\r\n'
|
|
||||||
)
|
|
||||||
self.c.client_conn.wfile.flush()
|
|
||||||
self.ssl_upgrade() # raises ConnectionTypeChange exception
|
|
||||||
|
|
||||||
if self.c.mode == "regular":
|
if self.expected_form_in == "absolute":
|
||||||
if request.form_in == "authority": # forward mode
|
if not self.c.config.upstream_server:
|
||||||
self.hook_reconnect(request)
|
|
||||||
elif request.form_in == "absolute":
|
|
||||||
if request.scheme != "http":
|
|
||||||
raise http.HttpError(400, "Invalid Request")
|
|
||||||
if not self.c.config.forward_proxy:
|
|
||||||
request.form_out = "relative"
|
|
||||||
self.c.set_server_address((request.host, request.port), AddressPriority.FROM_PROTOCOL)
|
self.c.set_server_address((request.host, request.port), AddressPriority.FROM_PROTOCOL)
|
||||||
flow.server_conn = self.c.server_conn # Update server_conn attribute on the flow
|
flow.server_conn = self.c.server_conn # Update server_conn attribute on the flow
|
||||||
else:
|
self.c.client_conn.send(
|
||||||
raise http.HttpError(400, "Invalid request form (absolute-form or authority-form required)")
|
'HTTP/1.1 200 Connection established\r\n' +
|
||||||
|
('Proxy-agent: %s\r\n' % self.c.server_version) +
|
||||||
|
'\r\n'
|
||||||
|
)
|
||||||
|
self.ssl_upgrade()
|
||||||
|
self.skip_authentication = True
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self.hook_reconnect(request)
|
||||||
|
return True
|
||||||
|
elif request.form_in == self.expected_form_in:
|
||||||
|
if request.form_in == "absolute":
|
||||||
|
if request.scheme != "http":
|
||||||
|
raise http.HttpError(400, "Invalid request scheme: %s" % request.scheme)
|
||||||
|
|
||||||
|
self.c.set_server_address((request.host, request.port), AddressPriority.FROM_PROTOCOL)
|
||||||
|
flow.server_conn = self.c.server_conn # Update server_conn attribute on the flow
|
||||||
|
|
||||||
|
request.form_out = self.expected_form_out
|
||||||
|
return True
|
||||||
|
|
||||||
|
raise http.HttpError(400, "Invalid HTTP request form (expected: %s, got: %s)" % (self.expected_form_in,
|
||||||
|
request.form_in))
|
||||||
|
|
||||||
def authenticate(self, request):
|
def authenticate(self, request):
|
||||||
if self.c.config.authenticator:
|
if self.c.config.authenticator:
|
||||||
|
@ -10,16 +10,17 @@ CONF_DIR = "~/.mitmproxy"
|
|||||||
|
|
||||||
class ProxyConfig:
|
class ProxyConfig:
|
||||||
def __init__(self, confdir=CONF_DIR, clientcerts=None,
|
def __init__(self, confdir=CONF_DIR, clientcerts=None,
|
||||||
no_upstream_cert=False, body_size_limit=None, reverse_proxy=None,
|
no_upstream_cert=False, body_size_limit=None, upstream_server=None,
|
||||||
forward_proxy=None, transparent_proxy=None, authenticator=None,
|
http_form_in="absolute", http_form_out="relative", transparent_proxy=None, authenticator=None,
|
||||||
ciphers=None, certs=None
|
ciphers=None, certs=None
|
||||||
):
|
):
|
||||||
self.ciphers = ciphers
|
self.ciphers = ciphers
|
||||||
self.clientcerts = clientcerts
|
self.clientcerts = clientcerts
|
||||||
self.no_upstream_cert = no_upstream_cert
|
self.no_upstream_cert = no_upstream_cert
|
||||||
self.body_size_limit = body_size_limit
|
self.body_size_limit = body_size_limit
|
||||||
self.reverse_proxy = reverse_proxy
|
self.upstream_server = upstream_server
|
||||||
self.forward_proxy = forward_proxy
|
self.http_form_in = http_form_in
|
||||||
|
self.http_form_out = http_form_out
|
||||||
self.transparent_proxy = transparent_proxy
|
self.transparent_proxy = transparent_proxy
|
||||||
self.authenticator = authenticator
|
self.authenticator = authenticator
|
||||||
self.confdir = os.path.expanduser(confdir)
|
self.confdir = os.path.expanduser(confdir)
|
||||||
@ -93,8 +94,9 @@ def process_proxy_options(parser, options):
|
|||||||
clientcerts=options.clientcerts,
|
clientcerts=options.clientcerts,
|
||||||
body_size_limit=body_size_limit,
|
body_size_limit=body_size_limit,
|
||||||
no_upstream_cert=options.no_upstream_cert,
|
no_upstream_cert=options.no_upstream_cert,
|
||||||
reverse_proxy=rp,
|
upstream_server=(rp or fp),
|
||||||
forward_proxy=fp,
|
http_form_in=("relative" if (rp or trans) else "absolute"),
|
||||||
|
http_form_out=("absolute" if fp else "relative"),
|
||||||
transparent_proxy=trans,
|
transparent_proxy=trans,
|
||||||
authenticator=authenticator,
|
authenticator=authenticator,
|
||||||
ciphers=options.ciphers,
|
ciphers=options.ciphers,
|
||||||
|
@ -23,12 +23,10 @@ class AddressPriority(object):
|
|||||||
Enum that signifies the priority of the given address when choosing the destination host.
|
Enum that signifies the priority of the given address when choosing the destination host.
|
||||||
Higher is better (None < i)
|
Higher is better (None < i)
|
||||||
"""
|
"""
|
||||||
FORCE = 5
|
|
||||||
"""forward mode"""
|
|
||||||
MANUALLY_CHANGED = 4
|
MANUALLY_CHANGED = 4
|
||||||
"""user changed the target address in the ui"""
|
"""user changed the target address in the ui"""
|
||||||
FROM_SETTINGS = 3
|
FROM_SETTINGS = 3
|
||||||
"""reverse proxy mode"""
|
"""upstream proxy from arguments (reverse proxy or forward proxy)"""
|
||||||
FROM_CONNECTION = 2
|
FROM_CONNECTION = 2
|
||||||
"""derived from transparent resolver"""
|
"""derived from transparent resolver"""
|
||||||
FROM_PROTOCOL = 1
|
FROM_PROTOCOL = 1
|
||||||
|
@ -59,12 +59,6 @@ class ConnectionHandler:
|
|||||||
self.conntype = None
|
self.conntype = None
|
||||||
self.sni = None
|
self.sni = None
|
||||||
|
|
||||||
self.mode = "regular"
|
|
||||||
if self.config.reverse_proxy:
|
|
||||||
self.mode = "reverse"
|
|
||||||
if self.config.transparent_proxy:
|
|
||||||
self.mode = "transparent"
|
|
||||||
|
|
||||||
def handle(self):
|
def handle(self):
|
||||||
self.log("clientconnect")
|
self.log("clientconnect")
|
||||||
self.channel.ask("clientconnect", self)
|
self.channel.ask("clientconnect", self)
|
||||||
@ -76,11 +70,8 @@ class ConnectionHandler:
|
|||||||
# Can we already identify the target server and connect to it?
|
# Can we already identify the target server and connect to it?
|
||||||
server_address = None
|
server_address = None
|
||||||
address_priority = None
|
address_priority = None
|
||||||
if self.config.forward_proxy:
|
if self.config.upstream_server:
|
||||||
server_address = self.config.forward_proxy[1:]
|
server_address = self.config.upstream_server[1:]
|
||||||
address_priority = AddressPriority.FORCE
|
|
||||||
elif self.config.reverse_proxy:
|
|
||||||
server_address = self.config.reverse_proxy[1:]
|
|
||||||
address_priority = AddressPriority.FROM_SETTINGS
|
address_priority = AddressPriority.FROM_SETTINGS
|
||||||
elif self.config.transparent_proxy:
|
elif self.config.transparent_proxy:
|
||||||
server_address = self.config.transparent_proxy["resolver"].original_addr(
|
server_address = self.config.transparent_proxy["resolver"].original_addr(
|
||||||
@ -125,8 +116,8 @@ class ConnectionHandler:
|
|||||||
|
|
||||||
if self.config.transparent_proxy:
|
if self.config.transparent_proxy:
|
||||||
client_ssl = server_ssl = (self.server_conn.address.port in self.config.transparent_proxy["sslports"])
|
client_ssl = server_ssl = (self.server_conn.address.port in self.config.transparent_proxy["sslports"])
|
||||||
elif self.config.reverse_proxy:
|
elif self.config.upstream_server:
|
||||||
client_ssl = server_ssl = (self.config.reverse_proxy[0] == "https")
|
client_ssl = server_ssl = (self.config.upstream_server[0] == "https")
|
||||||
# TODO: Make protocol generic (as with transparent proxies)
|
# TODO: Make protocol generic (as with transparent proxies)
|
||||||
# TODO: Add SSL-terminating capatbility (SSL -> mitmproxy -> plain and vice versa)
|
# TODO: Add SSL-terminating capatbility (SSL -> mitmproxy -> plain and vice versa)
|
||||||
if client_ssl or server_ssl:
|
if client_ssl or server_ssl:
|
||||||
@ -152,7 +143,6 @@ class ConnectionHandler:
|
|||||||
"""
|
"""
|
||||||
Sets a new server address with the given priority.
|
Sets a new server address with the given priority.
|
||||||
Does not re-establish either connection or SSL handshake.
|
Does not re-establish either connection or SSL handshake.
|
||||||
@type priority: libmproxy.proxy.primitives.AddressPriority
|
|
||||||
"""
|
"""
|
||||||
address = tcp.Address.wrap(address)
|
address = tcp.Address.wrap(address)
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ class TestInvalidRequests(tservers.HTTPProxTest):
|
|||||||
def test_double_connect(self):
|
def test_double_connect(self):
|
||||||
p = self.pathoc()
|
p = self.pathoc()
|
||||||
r = p.request("connect:'%s:%s'" % ("127.0.0.1", self.server2.port))
|
r = p.request("connect:'%s:%s'" % ("127.0.0.1", self.server2.port))
|
||||||
assert r.status_code == 502
|
assert r.status_code == 400
|
||||||
assert "Must not CONNECT on already encrypted connection" in r.content
|
assert "Must not CONNECT on already encrypted connection" in r.content
|
||||||
|
|
||||||
def test_relative_request(self):
|
def test_relative_request(self):
|
||||||
@ -98,7 +98,7 @@ class TestInvalidRequests(tservers.HTTPProxTest):
|
|||||||
p.connect()
|
p.connect()
|
||||||
r = p.request("get:/p/200")
|
r = p.request("get:/p/200")
|
||||||
assert r.status_code == 400
|
assert r.status_code == 400
|
||||||
assert "Invalid request form" in r.content
|
assert "Invalid HTTP request form" in r.content
|
||||||
|
|
||||||
|
|
||||||
class TestProxyChaining(tservers.HTTPChainProxyTest):
|
class TestProxyChaining(tservers.HTTPChainProxyTest):
|
||||||
|
@ -197,6 +197,8 @@ class TransparentProxTest(ProxTestBase):
|
|||||||
resolver = cls.resolver(cls.server.port),
|
resolver = cls.resolver(cls.server.port),
|
||||||
sslports = ports
|
sslports = ports
|
||||||
)
|
)
|
||||||
|
d["http_form_in"] = "relative"
|
||||||
|
d["http_form_out"] = "relative"
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def pathod(self, spec, sni=None):
|
def pathod(self, spec, sni=None):
|
||||||
@ -225,11 +227,13 @@ class ReverseProxTest(ProxTestBase):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_proxy_config(cls):
|
def get_proxy_config(cls):
|
||||||
d = ProxTestBase.get_proxy_config()
|
d = ProxTestBase.get_proxy_config()
|
||||||
d["reverse_proxy"] = (
|
d["upstream_server"] = (
|
||||||
"https" if cls.ssl else "http",
|
"https" if cls.ssl else "http",
|
||||||
"127.0.0.1",
|
"127.0.0.1",
|
||||||
cls.server.port
|
cls.server.port
|
||||||
)
|
)
|
||||||
|
d["http_form_in"] = "relative"
|
||||||
|
d["http_form_out"] = "relative"
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def pathoc(self, sni=None):
|
def pathoc(self, sni=None):
|
||||||
@ -258,18 +262,19 @@ class ChainProxTest(ProxTestBase):
|
|||||||
Chain n instances of mitmproxy in a row - because we can.
|
Chain n instances of mitmproxy in a row - because we can.
|
||||||
"""
|
"""
|
||||||
n = 2
|
n = 2
|
||||||
chain_config = [lambda: ProxyConfig(
|
chain_config = [lambda: ProxyConfig()] * n
|
||||||
)] * n
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setupAll(cls):
|
def setupAll(cls):
|
||||||
super(ChainProxTest, cls).setupAll()
|
super(ChainProxTest, cls).setupAll()
|
||||||
cls.chain = []
|
cls.chain = []
|
||||||
for i in range(cls.n):
|
for i in range(cls.n):
|
||||||
config = cls.chain_config[i]()
|
config = cls.chain_config[i]()
|
||||||
config.forward_proxy = ("http", "127.0.0.1",
|
config.upstream_server = ("http", "127.0.0.1",
|
||||||
cls.proxy.port if i == 0 else
|
cls.proxy.port if i == 0 else
|
||||||
cls.chain[-1].port
|
cls.chain[-1].port
|
||||||
)
|
)
|
||||||
|
config.http_form_in = "absolute"
|
||||||
|
config.http_form_out = "absolute"
|
||||||
tmaster = cls.masterclass(config)
|
tmaster = cls.masterclass(config)
|
||||||
tmaster.start_app(APP_HOST, APP_PORT, cls.externalapp)
|
tmaster.start_app(APP_HOST, APP_PORT, cls.externalapp)
|
||||||
cls.chain.append(ProxyThread(tmaster))
|
cls.chain.append(ProxyThread(tmaster))
|
||||||
|
Loading…
Reference in New Issue
Block a user