aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2014-01-18 22:57:28 +0100
committerMaximilian Hils <git@maximilianhils.com>2014-01-18 22:57:28 +0100
commit1786d778194b543ae77468a716c9e15caf247180 (patch)
tree57605c4b2193e2add528545813106e39ce253469
parent0b4ad05e02d2d8d4d7329e731ebf52ad5cd20043 (diff)
downloadmitmproxy-1786d778194b543ae77468a716c9e15caf247180.tar.gz
mitmproxy-1786d778194b543ae77468a716c9e15caf247180.tar.bz2
mitmproxy-1786d778194b543ae77468a716c9e15caf247180.zip
Add functionality to transparently proxify connections to other HTTPS proxies
-rw-r--r--libmproxy/protocol.py39
-rw-r--r--libmproxy/proxy.py4
2 files changed, 37 insertions, 6 deletions
diff --git a/libmproxy/protocol.py b/libmproxy/protocol.py
index d2459257..dcc8b75e 100644
--- a/libmproxy/protocol.py
+++ b/libmproxy/protocol.py
@@ -136,7 +136,8 @@ class HTTPRequest(HTTPMessage):
def is_live(self):
return True
- def _assemble_request_line(self, form):
+ def _assemble_request_line(self, form=None):
+ form = form or self.form_out
request_line = None
if form == "asterisk" or form == "origin":
request_line = '%s %s HTTP/%s.%s' % (self.method, self.path, self.httpversion[0], self.httpversion[1])
@@ -152,7 +153,7 @@ class HTTPRequest(HTTPMessage):
return request_line
def _assemble(self):
- request_line = self._assemble_request_line(self.form_out)
+ request_line = self._assemble_request_line()
return '%s\r\n%s\r\n%s' % (request_line, self._assemble_headers(), self.content)
@classmethod
@@ -267,7 +268,7 @@ class HTTPHandler(ProtocolHandler):
return False
if flow.request.form_in == "authority":
- self.ssl_upgrade()
+ self.ssl_upgrade(flow.request)
return True
except HttpAuthenticationError, e:
self.process_error(flow, code=407, message="Proxy Authentication Required", headers=e.auth_headers)
@@ -305,10 +306,40 @@ class HTTPHandler(ProtocolHandler):
self.c.client_conn.wfile.write(html_content)
self.c.client_conn.wfile.flush()
- def ssl_upgrade(self):
+ def ssl_upgrade(self, upstream_request=None):
+ """
+ Upgrade the connection to SSL after an authority (CONNECT) request has been made.
+ If the authority request has been forwarded upstream (because we have another proxy server there),
+ money-patch the ConnectionHandler.server_reconnect function to resend the request on reconnect.
+
+ This isn't particular beautiful code, but it isolates this rare edge-case from the
+ protocol-agnostic ConnectionHandler
+ """
self.c.mode = "transparent"
self.c.determine_conntype()
self.c.establish_ssl(server=True, client=True)
+
+ if upstream_request:
+ self.c.log("Hook reconnect function")
+ original_reconnect_func = self.c.server_reconnect
+
+ def reconnect_http_proxy():
+ self.c.log("Hooked reconnect function")
+ self.c.log("Hook: Run original redirect")
+ original_reconnect_func(no_ssl=True)
+ self.c.log("Hook: Write CONNECT request to upstream proxy", [upstream_request._assemble_request_line()])
+ self.c.server_conn.wfile.write(upstream_request._assemble())
+ self.c.server_conn.wfile.flush()
+ self.c.log("Hook: Read answer to CONNECT request from proxy")
+ resp = HTTPResponse.from_stream(self.c.server_conn.rfile, upstream_request.method)
+ if resp.code != 200:
+ raise ProxyError(resp.code,
+ "Cannot reestablish SSL connection with upstream proxy: \r\n" + str(resp.headers))
+ self.c.log("Hook: Establish SSL with upstream proxy")
+ self.c.establish_ssl(server=True)
+
+ self.c.server_reconnect = reconnect_http_proxy
+
raise ConnectionTypeChange
def process_request(self, request):
diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py
index 6ee3398a..854cc22b 100644
--- a/libmproxy/proxy.py
+++ b/libmproxy/proxy.py
@@ -242,11 +242,11 @@ class ConnectionHandler:
self.client_conn.convert_to_ssl(dummycert, self.config.certfile or self.config.cacert,
handle_sni=self.handle_sni)
- def server_reconnect(self):
+ def server_reconnect(self, no_ssl=False):
self.log("server reconnect")
had_ssl, sni = self.server_conn.ssl_established, self.sni
self.establish_server_connection(*self.server_conn.address)
- if had_ssl:
+ if had_ssl and not no_ssl:
self.sni = sni
self.establish_ssl(server=True)