diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/change_upstream_proxy.py | 47 | ||||
-rw-r--r-- | examples/dns_spoofing.py | 47 | ||||
-rw-r--r-- | examples/ignore_websocket.py | 37 | ||||
-rw-r--r-- | examples/stub.py | 13 |
4 files changed, 66 insertions, 78 deletions
diff --git a/examples/change_upstream_proxy.py b/examples/change_upstream_proxy.py index 7782dd84..8f58e1f2 100644 --- a/examples/change_upstream_proxy.py +++ b/examples/change_upstream_proxy.py @@ -1,29 +1,34 @@ # This scripts demonstrates how mitmproxy can switch to a second/different upstream proxy # in upstream proxy mode. # -# Usage: mitmdump -U http://default-upstream-proxy.local:8080/ -s -# "change_upstream_proxy.py host" -from libmproxy.protocol.http import send_connect_request - -alternative_upstream_proxy = ("localhost", 8082) +# Usage: mitmdump -U http://default-upstream-proxy.local:8080/ -s change_upstream_proxy.py +# +# If you want to change the target server, you should modify flow.request.host and flow.request.port +# flow.live.set_server should only be used by inline scripts to change the upstream proxy. -def should_redirect(flow): - return flow.request.host == "example.com" +def proxy_address(flow): + # Poor man's loadbalancing: route every second domain through the alternative proxy. + if hash(flow.request.host) % 2 == 1: + return ("localhost", 8082) + else: + return ("localhost", 8081) def request(context, flow): - if flow.live and should_redirect(flow): - - # If you want to change the target server, you should modify flow.request.host and flow.request.port - # flow.live.change_server should only be used by inline scripts to change the upstream proxy, - # unless you are sure that you know what you are doing. - server_changed = flow.live.change_server( - alternative_upstream_proxy, - persistent_change=True) - if flow.request.scheme == "https" and server_changed: - send_connect_request( - flow.live.c.server_conn, - flow.request.host, - flow.request.port) - flow.live.c.establish_ssl(server=True) + if flow.request.method == "CONNECT": + # If the decision is done by domain, one could also modify the server address here. + # We do it after CONNECT here to have the request data available as well. + return + address = proxy_address(flow) + if flow.live: + if flow.request.scheme == "http": + # For a normal HTTP request, we just change the proxy server and we're done! + if address != flow.live.server_conn.address: + flow.live.set_server(address, depth=1) + else: + # If we have CONNECTed (and thereby established "destination state"), the story is + # a bit more complex. Now we don't want to change the top level address (which is + # the connect destination) but the address below that. (Notice the `.via` and depth=2). + if address != flow.live.server_conn.via.address: + flow.live.set_server(address, depth=2) diff --git a/examples/dns_spoofing.py b/examples/dns_spoofing.py index dddf172c..98495d45 100644 --- a/examples/dns_spoofing.py +++ b/examples/dns_spoofing.py @@ -9,29 +9,42 @@ Using transparent mode is the better option most of the time. Usage: mitmproxy - -p 80 - -R http://example.com/ // Used as the target location if no Host header is present - mitmproxy -p 443 - -R https://example.com/ // Used as the target locaction if neither SNI nor host header are present. + -s dns_spoofing.py + # Used as the target location if neither SNI nor host header are present. + -R http://example.com/ + mitmdump + -p 80 + -R http://localhost:443/ -mitmproxy will always connect to the default location first, so it must be reachable. -As a workaround, you can spawn an arbitrary HTTP server and use that for both endpoints, e.g. -mitmproxy -p 80 -R http://localhost:8000 -mitmproxy -p 443 -R https2http://localhost:8000 + (Setting up a single proxy instance and using iptables to redirect to it + works as well) """ +import re + + +# This regex extracts splits the host header into host and port. +# Handles the edge case of IPv6 addresses containing colons. +# https://bugzilla.mozilla.org/show_bug.cgi?id=45891 +parse_host_header = re.compile(r"^(?P<host>[^:]+|\[.+\])(?::(?P<port>\d+))?$") def request(context, flow): if flow.client_conn.ssl_established: - # TLS SNI or Host header - flow.request.host = flow.client_conn.connection.get_servername( - ) or flow.request.pretty_host(hostheader=True) - - # If you use a https2http location as default destination, these - # attributes need to be corrected as well: - flow.request.port = 443 flow.request.scheme = "https" + sni = flow.client_conn.connection.get_servername() + port = 443 else: - # Host header - flow.request.host = flow.request.pretty_host(hostheader=True) + flow.request.scheme = "http" + sni = None + port = 80 + + host_header = flow.request.pretty_host(hostheader=True) + m = parse_host_header.match(host_header) + if m: + host_header = m.group("host").strip("[]") + if m.group("port"): + port = int(m.group("port")) + + flow.request.host = sni or host_header + flow.request.port = port
\ No newline at end of file diff --git a/examples/ignore_websocket.py b/examples/ignore_websocket.py deleted file mode 100644 index 57e11d5b..00000000 --- a/examples/ignore_websocket.py +++ /dev/null @@ -1,37 +0,0 @@ -# This script makes mitmproxy switch to passthrough mode for all HTTP -# responses with "Connection: Upgrade" header. This is useful to make -# WebSockets work in untrusted environments. -# -# Note: Chrome (and possibly other browsers), when explicitly configured -# to use a proxy (i.e. mitmproxy's regular mode), send a CONNECT request -# to the proxy before they initiate the websocket connection. -# To make WebSockets work in these cases, supply -# `--ignore :80$` as an additional parameter. -# (see http://mitmproxy.org/doc/features/passthrough.html) - -import netlib.http.semantics - -from libmproxy.protocol.tcp import TCPHandler -from libmproxy.protocol import KILL -from libmproxy.script import concurrent - - -def start(context, argv): - netlib.http.semantics.Request._headers_to_strip_off.remove("Connection") - netlib.http.semantics.Request._headers_to_strip_off.remove("Upgrade") - - -def done(context): - netlib.http.semantics.Request._headers_to_strip_off.append("Connection") - netlib.http.semantics.Request._headers_to_strip_off.append("Upgrade") - - -@concurrent -def response(context, flow): - value = flow.response.headers.get_first("Connection", None) - if value and value.upper() == "UPGRADE": - # We need to send the response manually now... - flow.client_conn.send(flow.client_conn.protocol.assemble(flow.response)) - # ...and then delegate to tcp passthrough. - TCPHandler(flow.live.c, log=False).handle_messages() - flow.reply(KILL) diff --git a/examples/stub.py b/examples/stub.py index d5502a47..bd3e7cd0 100644 --- a/examples/stub.py +++ b/examples/stub.py @@ -10,7 +10,7 @@ def start(context, argv): context.log("start") -def clientconnect(context, conn_handler): +def clientconnect(context, root_layer): """ Called when a client initiates a connection to the proxy. Note that a connection can correspond to multiple HTTP requests @@ -18,7 +18,7 @@ def clientconnect(context, conn_handler): context.log("clientconnect") -def serverconnect(context, conn_handler): +def serverconnect(context, server_connection): """ Called when the proxy initiates a connection to the target server. Note that a connection can correspond to multiple HTTP requests @@ -58,7 +58,14 @@ def error(context, flow): context.log("error") -def clientdisconnect(context, conn_handler): +def serverdisconnect(context, server_connection): + """ + Called when the proxy closes the connection to the target server. + """ + context.log("serverdisconnect") + + +def clientdisconnect(context, root_layer): """ Called when a client disconnects from the proxy. """ |