diff options
-rw-r--r-- | .travis.yml | 7 | ||||
-rw-r--r-- | README.mkd | 2 | ||||
-rw-r--r-- | doc-src/certinstall/android.html | 7 | ||||
-rw-r--r-- | doc-src/transparent/osx.html | 12 | ||||
-rw-r--r-- | libmproxy/console/common.py | 2 | ||||
-rw-r--r-- | libmproxy/flow.py | 7 | ||||
-rw-r--r-- | libmproxy/proxy.py | 42 | ||||
-rw-r--r-- | requirements.txt | 15 | ||||
-rw-r--r-- | test/test_server.py | 10 |
9 files changed, 66 insertions, 38 deletions
diff --git a/.travis.yml b/.travis.yml index 5f4a3602..c1084319 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,13 @@ python: - "2.7" # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors install: + - "pip install coveralls --use-mirrors" + - "pip install nose-cov --use-mirrors" - "pip install -r requirements.txt --use-mirrors" - "pip install --upgrade git+https://github.com/mitmproxy/netlib.git" - "pip install --upgrade git+https://github.com/mitmproxy/pathod.git" # command to run tests, e.g. python setup.py test -script: nosetests
\ No newline at end of file +script: + - "nosetests --with-cov --cov-report term-missing" +after_success: + - coveralls
\ No newline at end of file @@ -1,3 +1,5 @@ +[![Build Status](https://travis-ci.org/mitmproxy/mitmproxy.png)](https://travis-ci.org/mitmproxy/mitmproxy) [![Coverage Status](https://coveralls.io/repos/mitmproxy/mitmproxy/badge.png)](https://coveralls.io/r/mitmproxy/mitmproxy) + __mitmproxy__ is an interactive, SSL-capable man-in-the-middle proxy for HTTP with a console interface. diff --git a/doc-src/certinstall/android.html b/doc-src/certinstall/android.html index e4efec1d..f215d91d 100644 --- a/doc-src/certinstall/android.html +++ b/doc-src/certinstall/android.html @@ -1,4 +1,3 @@ - The proxy situation on Android is [an embarrasment](http://code.google.com/p/android/issues/detail?id=1273). It's scarcely credible, but Android didn't have a global proxy setting at all until @@ -15,12 +14,12 @@ below - your device may differ, but the broad process should be similar. ## Getting the certificate onto the device First we need to get the __mitmproxy-ca-cert.cer__ file into the -__/sdcard/Downloads__ folder on the device. There are a number of ways to do +__/sdcard/Download__ folder on the device. There are a number of ways to do this. If you have the Android Developer Tools installed, you can use [__adb push__](http://developer.android.com/tools/help/adb.html) to accomplish this. Depending on your device, you could also transfer the file using external media like an SD Card. In this example, we're using wget from within a terminal -emulator to transfer the certificate from a local HTTP server: +emulator to transfer the certificate from a local HTTP server: <img src="android-shellwgetmitmproxyca.png"/> @@ -33,7 +32,7 @@ and select "Install from storage": <img src="android-settingssecuritymenu.png"/> -The certificate in /sdcard/Downloads is automatically located and offered for +The certificate in /sdcard/Download is automatically located and offered for installation. Installing the cert will delete the download file from the local disk: diff --git a/doc-src/transparent/osx.html b/doc-src/transparent/osx.html index 77eea63b..205e4c76 100644 --- a/doc-src/transparent/osx.html +++ b/doc-src/transparent/osx.html @@ -67,3 +67,15 @@ rdr on en2 inet proto tcp to any port 443 -> 127.0.0.1 port 8080 </ol> + +Note that the **rdr** rules in the pf.conf given above only apply to inbound +traffic. This means that they will NOT redirect traffic coming from the box +running pf itself. We can't distinguish between an outbound connection from a +non-mitmproxy app, and an outbound connection from mitmproxy itself - if you +want to intercept your OSX traffic, you should use an external host to run +mitmproxy. None the less, pf is flexible to cater for a range of creative +possibilities, like intercepting traffic emanating from VMs. See the +**pf.conf** man page for more. + + + diff --git a/libmproxy/console/common.py b/libmproxy/console/common.py index 006303a7..951d2c2a 100644 --- a/libmproxy/console/common.py +++ b/libmproxy/console/common.py @@ -190,7 +190,7 @@ def format_flow(f, focus, extended=False, hostheader=False, padding=2): delta = f.response.timestamp_end - f.response.timestamp_start size = len(f.response.content) + f.response.get_header_size() - rate = utils.pretty_size(size / delta) + rate = utils.pretty_size(size / ( delta if delta > 0 else 1 ) ) d.update(dict( resp_code = f.response.code, diff --git a/libmproxy/flow.py b/libmproxy/flow.py index 24042812..40b7e535 100644 --- a/libmproxy/flow.py +++ b/libmproxy/flow.py @@ -1580,6 +1580,13 @@ class FlowMaster(controller.Master): self.run_script_hook("clientdisconnect", r) r.reply() + def handle_serverconnection(self, sc): + # To unify the mitmproxy script API, we call the script hook "serverconnect" rather than "serverconnection". + # As things are handled differently in libmproxy (ClientConnect + ClientDisconnect vs ServerConnection class), + # there is no "serverdisonnect" event at the moment. + self.run_script_hook("serverconnect", sc) + sc.reply() + def handle_error(self, r): f = self.state.add_error(r) if f: diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 81838e44..1fc289ed 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -159,6 +159,7 @@ class ProxyHandler(tcp.BaseHandler): if not self.server_conn: try: self.server_conn = ServerConnection(self.config, scheme, host, port, sni) + self.channel.ask(self.server_conn) self.server_conn.connect() except tcp.NetLibError, v: raise ProxyError(502, v) @@ -264,13 +265,13 @@ class ProxyHandler(tcp.BaseHandler): else: response = response_reply self.send_response(response) - if request and http.request_connection_close(request.httpversion, request.headers): + if request and http.connection_close(request.httpversion, request.headers): return # We could keep the client connection when the server # connection needs to go away. However, we want to mimic # behaviour as closely as possible to the client, so we # disconnect. - if http.response_connection_close(response.httpversion, response.headers): + if http.connection_close(response.httpversion, response.headers): return except (IOError, ProxyError, http.HttpError, tcp.NetLibError), e: if hasattr(e, "code"): @@ -316,6 +317,17 @@ class ProxyHandler(tcp.BaseHandler): raise ProxyError(502, "Unable to generate dummy cert.") return ret + def establish_ssl(self, client_conn, host, port): + dummycert = self.find_cert(client_conn, host, port, host) + sni = HandleSNI( + self, client_conn, host, port, + dummycert, self.config.certfile or self.config.cacert + ) + try: + self.convert_to_ssl(dummycert, self.config.certfile or self.config.cacert, handle_sni=sni) + except tcp.NetLibError, v: + raise ProxyError(400, str(v)) + def get_line(self, fp): """ Get a line, possibly preceded by a blank. @@ -335,15 +347,7 @@ class ProxyHandler(tcp.BaseHandler): if port in self.config.transparent_proxy["sslports"]: scheme = "https" if not self.ssl_established: - dummycert = self.find_cert(client_conn, host, port, host) - sni = HandleSNI( - self, client_conn, host, port, - dummycert, self.config.certfile or self.config.cacert - ) - try: - self.convert_to_ssl(dummycert, self.config.certfile or self.config.cacert, handle_sni=sni) - except tcp.NetLibError, v: - raise ProxyError(400, str(v)) + self.establish_ssl(client_conn, host, port) else: scheme = "http" line = self.get_line(self.rfile) @@ -378,15 +382,7 @@ class ProxyHandler(tcp.BaseHandler): '\r\n' ) self.wfile.flush() - dummycert = self.find_cert(client_conn, host, port, host) - sni = HandleSNI( - self, client_conn, host, port, - dummycert, self.config.certfile or self.config.cacert - ) - try: - self.convert_to_ssl(dummycert, self.config.certfile or self.config.cacert, handle_sni=sni) - except tcp.NetLibError, v: - raise ProxyError(400, str(v)) + self.establish_ssl(client_conn, host, port) self.proxy_connect_state = (host, port, httpversion) line = self.rfile.readline(line) @@ -420,10 +416,12 @@ class ProxyHandler(tcp.BaseHandler): ) def read_request_reverse(self, client_conn): + scheme, host, port = self.config.reverse_proxy + if scheme.lower() == "https" and not self.ssl_established: + self.establish_ssl(client_conn, host, port) line = self.get_line(self.rfile) if line == "": return None - scheme, host, port = self.config.reverse_proxy r = http.parse_init_http(line) if not r: raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) @@ -433,7 +431,7 @@ class ProxyHandler(tcp.BaseHandler): self.rfile, self.wfile, headers, httpversion, self.config.body_size_limit ) return flow.Request( - client_conn, httpversion, host, port, "http", method, path, headers, content, + client_conn, httpversion, host, port, scheme, method, path, headers, content, self.rfile.first_byte_timestamp, utils.timestamp() ) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..3ecd8ed4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,15 @@ +Flask>=0.9 +Jinja2>=2.7 +MarkupSafe>=0.18 +PIL>=1.1.7 +Werkzeug>=0.8.3 +lxml>=3.2.1 +netlib>=0.9.2 +nose>=1.3.0 +pathod>=0.9.2 +pyOpenSSL>=0.13 +pyasn1>=0.1.7 +requests>=1.2.2 +urwid>=1.1.1 +wsgiref>=0.1.2 +jsbeautifier>=1.4.0
\ No newline at end of file diff --git a/test/test_server.py b/test/test_server.py index 079ed8ce..e9a6b727 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -191,16 +191,6 @@ class TestHTTPS(tservers.HTTPProxTest, CommonMixin): assert p.request("get:/:i0,'invalid\r\n\r\n'").status_code == 400 -class TestHTTPSNoUpstream(tservers.HTTPProxTest, CommonMixin): - ssl = True - no_upstream_cert = True - def test_cert_gen_error(self): - f = self.pathoc_raw() - f.connect((u"foo..bar".encode("utf8"), 0)) - f.request("get:/") - assert "dummy cert" in "".join(self.proxy.log) - - class TestHTTPSCertfile(tservers.HTTPProxTest, CommonMixin): ssl = True certfile = True |