aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAldo Cortesi <aldo@nullcube.com>2017-03-06 20:30:49 +1300
committerAldo Cortesi <aldo@nullcube.com>2017-03-06 20:30:49 +1300
commit82163a1e680d608cdf2fb33f30dfcc6e142547a7 (patch)
treebb61c8e4c368207e43ae8ed1d14ca95eefb377bb
parentf5fb6972aac9b4f17e0d0d34b2a5c347141f476c (diff)
downloadmitmproxy-82163a1e680d608cdf2fb33f30dfcc6e142547a7.tar.gz
mitmproxy-82163a1e680d608cdf2fb33f30dfcc6e142547a7.tar.bz2
mitmproxy-82163a1e680d608cdf2fb33f30dfcc6e142547a7.zip
Unify mode specification
We now have: --mode regular (the default) --mode transparent --mode socks5 --mode reverse:SPEC --mode upstream:SPEC Where SPEC is a host specification.
-rw-r--r--mitmproxy/addons/core.py29
-rw-r--r--mitmproxy/master.py2
-rw-r--r--mitmproxy/options.py15
-rw-r--r--mitmproxy/proxy/config.py8
-rw-r--r--mitmproxy/proxy/protocol/http.py2
-rw-r--r--mitmproxy/proxy/protocol/http_replay.py2
-rw-r--r--mitmproxy/proxy/server.py4
-rw-r--r--mitmproxy/tools/cmdline.py67
-rw-r--r--mitmproxy/tools/console/statusbar.py10
-rw-r--r--test/mitmproxy/addons/test_core.py18
-rw-r--r--test/mitmproxy/proxy/test_server.py4
-rw-r--r--test/mitmproxy/test_flow.py3
-rw-r--r--test/mitmproxy/test_proxy.py37
-rw-r--r--test/mitmproxy/tservers.py8
14 files changed, 80 insertions, 129 deletions
diff --git a/mitmproxy/addons/core.py b/mitmproxy/addons/core.py
index 5d2cf57b..8a2ea525 100644
--- a/mitmproxy/addons/core.py
+++ b/mitmproxy/addons/core.py
@@ -3,18 +3,37 @@
checked by other addons.
"""
from mitmproxy import exceptions
+from mitmproxy import options
+from mitmproxy import platform
from mitmproxy.utils import human
class Core:
- def configure(self, options, updated):
- if "body_size_limit" in updated and options.body_size_limit:
+ def configure(self, opts, updated):
+ if "body_size_limit" in updated and opts.body_size_limit:
try:
- options._processed["body_size_limit"] = human.parse_size(
- options.body_size_limit
+ opts._processed["body_size_limit"] = human.parse_size(
+ opts.body_size_limit
)
except ValueError as e:
raise exceptions.OptionsError(
"Invalid body size limit specification: %s" %
- options.body_size_limit
+ opts.body_size_limit
+ )
+ if "mode" in updated:
+ mode = opts.mode
+ if mode.startswith("reverse:") or mode.startswith("upstream:"):
+ spec = options.get_mode_spec(mode)
+ if not spec:
+ raise exceptions.OptionsError(
+ "Invalid mode specification: %s" % mode
+ )
+ elif mode == "transparent":
+ if not platform.original_addr:
+ raise exceptions.OptionsError(
+ "Transparent mode not supported on this platform."
+ )
+ elif mode not in ["regular", "socks5"]:
+ raise exceptions.OptionsError(
+ "Invalid mode specification: %s" % mode
)
diff --git a/mitmproxy/master.py b/mitmproxy/master.py
index 633f32aa..8855452c 100644
--- a/mitmproxy/master.py
+++ b/mitmproxy/master.py
@@ -148,7 +148,7 @@ class Master:
Loads a flow
"""
if isinstance(f, http.HTTPFlow):
- if self.server and self.options.mode == "reverse":
+ if self.server and self.options.mode.startswith("reverse:"):
f.request.host = self.server.config.upstream_server.address[0]
f.request.port = self.server.config.upstream_server.address[1]
f.request.scheme = self.server.config.upstream_server.scheme
diff --git a/mitmproxy/options.py b/mitmproxy/options.py
index 84ab1ecf..41e02031 100644
--- a/mitmproxy/options.py
+++ b/mitmproxy/options.py
@@ -22,6 +22,11 @@ view_orders = [
"size",
]
+
+def get_mode_spec(m):
+ return m.split(":", maxsplit=1)[1]
+
+
APP_HOST = "mitm.it"
APP_PORT = 80
CA_DIR = "~/.mitmproxy"
@@ -247,7 +252,14 @@ class Options(optmanager.OptManager):
"upstream_bind_address", "", str,
"Address to bind upstream requests to (defaults to none)"
)
- self.add_option("mode", "regular", str)
+ self.add_option(
+ "mode", "regular", str,
+ """
+ Mode can be "regular", "transparent", "socks5", "reverse:SPEC",
+ or "upstream:SPEC". For reverse and upstream proxy modes, SPEC
+ is proxy specification in the form of "http[s]://host[:port]".
+ """
+ )
self.add_option(
"upstream_cert", True, bool,
"Connect to upstream server to look up certificate details."
@@ -285,7 +297,6 @@ class Options(optmanager.OptManager):
"Use the client's IP for server-side connections. "
"Combine with --upstream-bind-address to spoof a fixed source address."
)
- self.add_option("upstream_server", None, Optional[str])
self.add_option(
"upstream_auth", None, Optional[str],
"""
diff --git a/mitmproxy/proxy/config.py b/mitmproxy/proxy/config.py
index 61d8e1b7..9cf2b00f 100644
--- a/mitmproxy/proxy/config.py
+++ b/mitmproxy/proxy/config.py
@@ -121,7 +121,7 @@ class ProxyConfig:
raise exceptions.OptionsError(
"Invalid certificate format: %s" % cert
)
-
- self.upstream_server = None
- if options.upstream_server:
- self.upstream_server = parse_server_spec(options.upstream_server)
+ m = options.mode
+ if m.startswith("upstream:") or m.startswith("reverse:"):
+ spec = moptions.get_mode_spec(options.mode)
+ self.upstream_server = parse_server_spec(spec)
diff --git a/mitmproxy/proxy/protocol/http.py b/mitmproxy/proxy/protocol/http.py
index d2f6d374..d9e53fed 100644
--- a/mitmproxy/proxy/protocol/http.py
+++ b/mitmproxy/proxy/protocol/http.py
@@ -290,7 +290,7 @@ class HttpLayer(base.Layer):
request.first_line_format = "relative"
# update host header in reverse proxy mode
- if self.config.options.mode == "reverse" and not self.config.options.keep_host_header:
+ if self.config.options.mode.startswith("reverse:") and not self.config.options.keep_host_header:
f.request.host_header = self.config.upstream_server.address[0]
# Determine .scheme, .host and .port attributes for inline scripts. For
diff --git a/mitmproxy/proxy/protocol/http_replay.py b/mitmproxy/proxy/protocol/http_replay.py
index 161816e7..25867871 100644
--- a/mitmproxy/proxy/protocol/http_replay.py
+++ b/mitmproxy/proxy/protocol/http_replay.py
@@ -45,7 +45,7 @@ class RequestReplayThread(basethread.BaseThread):
if not self.f.response:
# In all modes, we directly connect to the server displayed
- if self.config.options.mode == "upstream":
+ if self.config.options.mode.startswith("upstream:"):
server_address = self.config.upstream_server.address
server = connections.ServerConnection(server_address, (self.config.options.listen_host, 0))
server.connect()
diff --git a/mitmproxy/proxy/server.py b/mitmproxy/proxy/server.py
index 8082cb64..16692234 100644
--- a/mitmproxy/proxy/server.py
+++ b/mitmproxy/proxy/server.py
@@ -85,14 +85,14 @@ class ConnectionHandler:
)
mode = self.config.options.mode
- if mode == "upstream":
+ if mode.startswith("upstream:"):
return modes.HttpUpstreamProxy(
root_ctx,
self.config.upstream_server.address
)
elif mode == "transparent":
return modes.TransparentProxy(root_ctx)
- elif mode == "reverse":
+ elif mode.startswith("reverse:"):
server_tls = self.config.upstream_server.scheme == "https"
return modes.ReverseProxy(
root_ctx,
diff --git a/mitmproxy/tools/cmdline.py b/mitmproxy/tools/cmdline.py
index 5e83e828..1160e1e4 100644
--- a/mitmproxy/tools/cmdline.py
+++ b/mitmproxy/tools/cmdline.py
@@ -3,7 +3,6 @@ import os
from mitmproxy import exceptions
from mitmproxy import options
-from mitmproxy import platform
from mitmproxy import version
@@ -15,34 +14,6 @@ class ParseException(Exception):
def get_common_options(args):
- # Establish proxy mode
- c = 0
- mode, upstream_server = "regular", None
- if args.transparent_proxy:
- c += 1
- if not platform.original_addr:
- raise exceptions.OptionsError(
- "Transparent mode not supported on this platform."
- )
- mode = "transparent"
- if args.socks_proxy:
- c += 1
- mode = "socks5"
- if args.reverse_proxy:
- c += 1
- mode = "reverse"
- upstream_server = args.reverse_proxy
- if args.upstream_proxy:
- c += 1
- mode = "upstream"
- upstream_server = args.upstream_proxy
- if c > 1:
- raise exceptions.OptionsError(
- "Transparent, SOCKS5, reverse and upstream proxy mode "
- "are mutually exclusive. Read the docs on proxy modes "
- "to understand why."
- )
-
if args.add_upstream_certs_to_client_chain and not args.upstream_cert:
raise exceptions.OptionsError(
"The no-upstream-cert and add-upstream-certs-to-client-chain "
@@ -99,7 +70,7 @@ def get_common_options(args):
listen_host = args.listen_host,
listen_port = args.listen_port,
upstream_bind_address = args.upstream_bind_address,
- mode = mode,
+ mode = args.mode,
upstream_cert = args.upstream_cert,
spoof_source_address = args.spoof_source_address,
@@ -108,7 +79,6 @@ def get_common_options(args):
websocket = args.websocket,
rawtcp = args.rawtcp,
- upstream_server = upstream_server,
upstream_auth = args.upstream_auth,
ssl_version_client = args.ssl_version_client,
ssl_version_server = args.ssl_version_server,
@@ -154,39 +124,6 @@ def basic_options(parser, opts):
opts.make_parser(parser, "stream_large_bodies")
-def proxy_modes(parser, opts):
- group = parser.add_argument_group("Proxy Modes")
- group.add_argument(
- "-R", "--reverse",
- action="store",
- type=str,
- dest="reverse_proxy",
- help="""
- Forward all requests to upstream HTTP server:
- http[s]://host[:port]. Clients can always connect both
- via HTTPS and HTTP, the connection to the server is
- determined by the specified scheme.
- """
- )
- group.add_argument(
- "--socks",
- action="store_true", dest="socks_proxy",
- help="Set SOCKS5 proxy mode."
- )
- group.add_argument(
- "-T", "--transparent",
- action="store_true", dest="transparent_proxy",
- help="Set transparent proxy mode."
- )
- group.add_argument(
- "-U", "--upstream",
- action="store",
- type=str,
- dest="upstream_proxy",
- help="Forward all requests to upstream proxy server: http://host[:port]"
- )
-
-
def proxy_options(parser, opts):
group = parser.add_argument_group("Proxy Options")
opts.make_parser(group, "listen_host")
@@ -315,8 +252,8 @@ def common_options(parser, opts):
metavar="PATH",
help="Configuration file"
)
+ opts.make_parser(parser, "mode")
basic_options(parser, opts)
- proxy_modes(parser, opts)
proxy_options(parser, opts)
proxy_ssl_options(parser, opts)
onboarding_app(parser, opts)
diff --git a/mitmproxy/tools/console/statusbar.py b/mitmproxy/tools/console/statusbar.py
index a5611b28..3e524972 100644
--- a/mitmproxy/tools/console/statusbar.py
+++ b/mitmproxy/tools/console/statusbar.py
@@ -2,7 +2,6 @@ import os.path
import urwid
-import mitmproxy.net.http.url
from mitmproxy.tools.console import common
from mitmproxy.tools.console import pathedit
from mitmproxy.tools.console import signals
@@ -234,13 +233,8 @@ class StatusBar(urwid.WidgetWrap):
if opts:
r.append("[%s]" % (":".join(opts)))
- if self.master.options.mode in ["reverse", "upstream"]:
- dst = self.master.server.config.upstream_server
- r.append("[dest:%s]" % mitmproxy.net.http.url.unparse(
- dst.scheme,
- dst.address[0],
- dst.address[1],
- ))
+ if self.master.options.mode != "regular":
+ r.append("[%s]" % self.master.options.mode)
if self.master.options.scripts:
r.append("[")
r.append(("heading_key", "s"))
diff --git a/test/mitmproxy/addons/test_core.py b/test/mitmproxy/addons/test_core.py
index 95272716..533eb58e 100644
--- a/test/mitmproxy/addons/test_core.py
+++ b/test/mitmproxy/addons/test_core.py
@@ -2,6 +2,7 @@ from mitmproxy import exceptions
from mitmproxy.addons import core
from mitmproxy.test import taddons
import pytest
+from unittest import mock
def test_simple():
@@ -11,3 +12,20 @@ def test_simple():
tctx.configure(sa, body_size_limit = "invalid")
tctx.configure(sa, body_size_limit = "1m")
assert tctx.options._processed["body_size_limit"]
+
+
+@mock.patch("mitmproxy.platform.original_addr", None)
+def test_no_transparent():
+ sa = core.Core()
+ with taddons.context() as tctx:
+ with pytest.raises(Exception, match="Transparent mode not supported"):
+ tctx.configure(sa, mode = "transparent")
+
+
+@mock.patch("mitmproxy.platform.original_addr")
+def test_modes(m):
+ sa = core.Core()
+ with taddons.context() as tctx:
+ tctx.configure(sa, mode = "reverse:http://localhost")
+ with pytest.raises(Exception, match="Invalid mode"):
+ tctx.configure(sa, mode = "reverse:")
diff --git a/test/mitmproxy/proxy/test_server.py b/test/mitmproxy/proxy/test_server.py
index bcfecf6f..b90840ab 100644
--- a/test/mitmproxy/proxy/test_server.py
+++ b/test/mitmproxy/proxy/test_server.py
@@ -10,7 +10,7 @@ from mitmproxy import options
from mitmproxy.addons import script
from mitmproxy.addons import proxyauth
from mitmproxy import http
-from mitmproxy.proxy.config import HostMatcher, parse_server_spec
+from mitmproxy.proxy.config import HostMatcher
import mitmproxy.net.http
from mitmproxy.net import tcp
from mitmproxy.net import socks
@@ -579,8 +579,6 @@ class TestHttps2Http(tservers.ReverseProxyTest):
@classmethod
def get_options(cls):
opts = super().get_options()
- s = parse_server_spec(opts.upstream_server)
- opts.upstream_server = "http://{}:{}".format(s.address[0], s.address[1])
return opts
def pathoc(self, ssl, sni=None):
diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py
index 0ac3bfd6..f4d32cbb 100644
--- a/test/mitmproxy/test_flow.py
+++ b/test/mitmproxy/test_flow.py
@@ -63,8 +63,7 @@ class TestSerialize:
r = self._treader()
s = tservers.TestState()
opts = options.Options(
- mode="reverse",
- upstream_server="https://use-this-domain"
+ mode="reverse:https://use-this-domain"
)
conf = ProxyConfig(opts)
fm = master.Master(opts, DummyServer(conf))
diff --git a/test/mitmproxy/test_proxy.py b/test/mitmproxy/test_proxy.py
index 5dd4a9e3..784a7d84 100644
--- a/test/mitmproxy/test_proxy.py
+++ b/test/mitmproxy/test_proxy.py
@@ -49,31 +49,6 @@ class TestProcessProxyOptions:
with tutils.tmpdir() as cadir:
self.assert_noerr("--cadir", cadir)
- @mock.patch("mitmproxy.platform.original_addr", None)
- def test_no_transparent(self):
- with pytest.raises(Exception, match="Transparent mode not supported"):
- self.p("-T")
-
- @mock.patch("mitmproxy.platform.original_addr")
- def test_modes(self, _):
- self.assert_noerr("-R", "http://localhost")
- with pytest.raises(Exception, match="expected one argument"):
- self.p("-R")
- with pytest.raises(Exception, match="Invalid server specification"):
- self.p("-R", "reverse")
-
- self.assert_noerr("-T")
-
- self.assert_noerr("-U", "http://localhost")
- with pytest.raises(Exception, match="Invalid server specification"):
- self.p("-U", "upstream")
-
- self.assert_noerr("--upstream-auth", "test:test")
- with pytest.raises(Exception, match="expected one argument"):
- self.p("--upstream-auth")
- with pytest.raises(Exception, match="mutually exclusive"):
- self.p("-R", "http://localhost", "-T")
-
def test_client_certs(self):
with tutils.tmpdir() as cadir:
self.assert_noerr("--client-certs", cadir)
@@ -131,19 +106,19 @@ class TestDummyServer:
class TestConnectionHandler:
def test_fatal_error(self, capsys):
- config = mock.Mock()
- root_layer = mock.Mock()
- root_layer.side_effect = RuntimeError
- config.options.mode.return_value = root_layer
+ opts = options.Options()
+ pconf = config.ProxyConfig(opts)
+
channel = mock.Mock()
def ask(_, x):
- return x
+ raise RuntimeError
+
channel.ask = ask
c = ConnectionHandler(
mock.MagicMock(),
("127.0.0.1", 8080),
- config,
+ pconf,
channel
)
c.handle()
diff --git a/test/mitmproxy/tservers.py b/test/mitmproxy/tservers.py
index 9a289ae5..a8aaa358 100644
--- a/test/mitmproxy/tservers.py
+++ b/test/mitmproxy/tservers.py
@@ -288,7 +288,7 @@ class ReverseProxyTest(ProxyTestBase):
@classmethod
def get_options(cls):
opts = ProxyTestBase.get_options()
- opts.upstream_server = "".join(
+ s = "".join(
[
"https" if cls.ssl else "http",
"://",
@@ -296,7 +296,7 @@ class ReverseProxyTest(ProxyTestBase):
str(cls.server.port)
]
)
- opts.mode = "reverse"
+ opts.mode = "reverse:" + s
return opts
def pathoc(self, sni=None):
@@ -373,9 +373,9 @@ class ChainProxyTest(ProxyTestBase):
def get_options(cls):
opts = super().get_options()
if cls.chain: # First proxy is in normal mode.
+ s = "http://127.0.0.1:%s" % cls.chain[0].port
opts.update(
- mode="upstream",
- upstream_server="http://127.0.0.1:%s" % cls.chain[0].port
+ mode="upstream:" + s,
)
return opts