From e3fd0e838d67d2e73176a7650d371b6ab59333e0 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Mon, 25 Mar 2013 09:20:26 +1300 Subject: [PATCH] Add a basic built-in web app. --- libmproxy/app.py | 8 ++++++++ libmproxy/cmdline.py | 9 ++++++++- libmproxy/flow.py | 12 +++++++----- libmproxy/proxy.py | 22 +++++++++++++++++++--- test/test_server.py | 12 +++++++++++- test/tservers.py | 15 +++++++++++++++ 6 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 libmproxy/app.py diff --git a/libmproxy/app.py b/libmproxy/app.py new file mode 100644 index 000000000..666b4d484 --- /dev/null +++ b/libmproxy/app.py @@ -0,0 +1,8 @@ +import flask + +mapp = flask.Flask(__name__) + +@mapp.route("/") +def hello(): + return "mitmproxy" + diff --git a/libmproxy/cmdline.py b/libmproxy/cmdline.py index 1d5902a9d..b76792cf2 100644 --- a/libmproxy/cmdline.py +++ b/libmproxy/cmdline.py @@ -163,7 +163,7 @@ def get_common_options(options): def common_options(parser): parser.add_argument( - "-a", + "-b", action="store", type = str, dest="addr", default='', help = "Address to bind proxy to (defaults to all interfaces)" ) @@ -261,6 +261,13 @@ def common_options(parser): help="Don't connect to upstream server to look up certificate details." ) + group = parser.add_argument_group("Web App") + group.add_argument( + "-a", + action="store_true", dest="app", default=False, + help="Enable the mitmproxy web app." + ) + group = parser.add_argument_group("Client Replay") group.add_argument( "-c", diff --git a/libmproxy/flow.py b/libmproxy/flow.py index d33a31082..6d77fd885 100644 --- a/libmproxy/flow.py +++ b/libmproxy/flow.py @@ -23,6 +23,7 @@ import tnetstring, filt, script, utils, encoding, proxy from email.utils import parsedate_tz, formatdate, mktime_tz from netlib import odict, http, certutils import controller, version +import app HDR_FORM_URLENCODED = "application/x-www-form-urlencoded" CONTENT_MISSING = 0 @@ -42,13 +43,13 @@ class ReplaceHooks: def add(self, fpatt, rex, s): """ - Add a replacement hook. + add a replacement hook. - fpatt: A string specifying a filter pattern. - rex: A regular expression. - s: The replacement string + fpatt: a string specifying a filter pattern. + rex: a regular expression. + s: the replacement string - Returns True if hook was added, False if the pattern could not be + returns true if hook was added, false if the pattern could not be parsed. """ cpatt = filt.parse(fpatt) @@ -1352,6 +1353,7 @@ class FlowMaster(controller.Master): self.setheaders = SetHeaders() self.stream = None + app.mapp.config["PMASTER"] = self def add_event(self, e, level="info"): """ diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index ad7be354f..3fac17b8b 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -17,8 +17,11 @@ import shutil, tempfile, threading import SocketServer from OpenSSL import SSL from netlib import odict, tcp, http, wsgi, certutils, http_status, http_auth -import utils, flow, version, platform, controller +import utils, flow, version, platform, controller, app + +APP_DOMAIN = "mitm" +APP_IP = "1.1.1.1" KILL = 0 @@ -36,8 +39,8 @@ class Log: class ProxyConfig: - def __init__(self, certfile = None, cacert = None, clientcerts = None, no_upstream_cert=False, body_size_limit = None, reverse_proxy=None, transparent_proxy=None, certdir = None, authenticator=None): - assert not (reverse_proxy and transparent_proxy) + def __init__(self, app=False, certfile = None, cacert = None, clientcerts = None, no_upstream_cert=False, body_size_limit = None, reverse_proxy=None, transparent_proxy=None, certdir = None, authenticator=None): + self.app = app self.certfile = certfile self.cacert = cacert self.clientcerts = clientcerts @@ -85,6 +88,7 @@ class ServerConnection(tcp.TCPClient): pass + class RequestReplayThread(threading.Thread): def __init__(self, config, flow, masterq): self.config, self.flow, self.channel = config, flow, controller.Channel(masterq) @@ -497,6 +501,17 @@ class ProxyServer(tcp.TCPServer): raise ProxyServerError('Error starting proxy server: ' + v.strerror) self.channel = None self.apps = AppRegistry() + if config.app: + self.apps.add( + app.mapp, + APP_DOMAIN, + 80 + ) + self.apps.add( + app.mapp, + APP_IP, + 80 + ) def start_slave(self, klass, channel): slave = klass(channel, self) @@ -629,6 +644,7 @@ def process_proxy_options(parser, options): authenticator = http_auth.NullProxyAuth(None) return ProxyConfig( + app = options.app, certfile = options.cert, cacert = cacert, clientcerts = options.clientcerts, diff --git a/test/test_server.py b/test/test_server.py index f12fbceec..c20b0cea5 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -53,7 +53,16 @@ class CommonMixin: assert "Bad Request" in t.rfile.readline() -class TestHTTP(tservers.HTTPProxTest, CommonMixin): + +class AppMixin: + def test_app(self): + ret = self.app("/") + assert ret.status_code == 200 + assert "mitmproxy" in ret.content + + + +class TestHTTP(tservers.HTTPProxTest, CommonMixin, AppMixin): def test_app_err(self): p = self.pathoc() ret = p.request("get:'http://errapp/'") @@ -135,6 +144,7 @@ class TestHTTP(tservers.HTTPProxTest, CommonMixin): assert req.status_code == 400 + class TestHTTPAuth(tservers.HTTPProxTest): authenticator = http_auth.BasicProxyAuth(http_auth.PassManSingleUser("test", "test"), "realm") def test_auth(self): diff --git a/test/tservers.py b/test/tservers.py index 0c2f8c2ff..91ce4dc01 100644 --- a/test/tservers.py +++ b/test/tservers.py @@ -84,6 +84,7 @@ class ProxTestBase: no_upstream_cert = cls.no_upstream_cert, cacert = tutils.test_data.path("data/serverkey.pem"), authenticator = cls.authenticator, + app = True, **pconf ) tmaster = cls.masterclass(cls.tqueue, config) @@ -156,6 +157,17 @@ class HTTPProxTest(ProxTestBase): q = "get:'%s/p/%s'"%(self.server.urlbase, spec) return p.request(q) + def app(self, page): + if self.ssl: + p = libpathod.pathoc.Pathoc("127.0.0.1", self.proxy.port, True) + print "PRE" + p.connect((proxy.APP_IP, 80)) + print "POST" + return p.request("get:'/%s'"%page) + else: + p = self.pathoc() + return p.request("get:'http://%s/%s'"%(proxy.APP_DOMAIN, page)) + class TResolver: def __init__(self, port): @@ -234,3 +246,6 @@ class ReverseProxTest(ProxTestBase): q = "get:'/p/%s'"%spec return p.request(q) + + +