mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-26 18:18:25 +00:00
parent
8353f4a55a
commit
8d0c800d15
@ -67,6 +67,10 @@ class Options(optmanager.OptManager):
|
|||||||
regular expression and matched on the ip or the hostname.
|
regular expression and matched on the ip or the hostname.
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
self.add_option(
|
||||||
|
"allow_hosts", Sequence[str], [],
|
||||||
|
"Opposite of --ignore_hosts."
|
||||||
|
)
|
||||||
self.add_option(
|
self.add_option(
|
||||||
"listen_host", str, "",
|
"listen_host", str, "",
|
||||||
"Address to bind proxy to."
|
"Address to bind proxy to."
|
||||||
|
@ -14,7 +14,8 @@ CONF_BASENAME = "mitmproxy"
|
|||||||
|
|
||||||
class HostMatcher:
|
class HostMatcher:
|
||||||
|
|
||||||
def __init__(self, patterns=tuple()):
|
def __init__(self, handle, patterns=tuple()):
|
||||||
|
self.handle = handle
|
||||||
self.patterns = list(patterns)
|
self.patterns = list(patterns)
|
||||||
self.regexes = [re.compile(p, re.IGNORECASE) for p in self.patterns]
|
self.regexes = [re.compile(p, re.IGNORECASE) for p in self.patterns]
|
||||||
|
|
||||||
@ -22,8 +23,10 @@ class HostMatcher:
|
|||||||
if not address:
|
if not address:
|
||||||
return False
|
return False
|
||||||
host = "%s:%s" % address
|
host = "%s:%s" % address
|
||||||
if any(rex.search(host) for rex in self.regexes):
|
if self.handle in ["ignore", "tcp"]:
|
||||||
return True
|
return any(rex.search(host) for rex in self.regexes)
|
||||||
|
elif self.handle == "allow":
|
||||||
|
return any(not rex.search(host) for rex in self.regexes)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -36,7 +39,7 @@ class ProxyConfig:
|
|||||||
def __init__(self, options: moptions.Options) -> None:
|
def __init__(self, options: moptions.Options) -> None:
|
||||||
self.options = options
|
self.options = options
|
||||||
|
|
||||||
self.check_ignore: HostMatcher = None
|
self.check_filter: HostMatcher = None
|
||||||
self.check_tcp: HostMatcher = None
|
self.check_tcp: HostMatcher = None
|
||||||
self.certstore: certs.CertStore = None
|
self.certstore: certs.CertStore = None
|
||||||
self.upstream_server: typing.Optional[server_spec.ServerSpec] = None
|
self.upstream_server: typing.Optional[server_spec.ServerSpec] = None
|
||||||
@ -44,10 +47,18 @@ class ProxyConfig:
|
|||||||
options.changed.connect(self.configure)
|
options.changed.connect(self.configure)
|
||||||
|
|
||||||
def configure(self, options: moptions.Options, updated: typing.Any) -> None:
|
def configure(self, options: moptions.Options, updated: typing.Any) -> None:
|
||||||
if "ignore_hosts" in updated:
|
if options.allow_hosts and options.ignore_hosts:
|
||||||
self.check_ignore = HostMatcher(options.ignore_hosts)
|
raise exceptions.OptionsError("--ignore-hosts and --allow-hosts are mutually "
|
||||||
|
"exclusive; please choose one.")
|
||||||
|
|
||||||
|
if options.ignore_hosts:
|
||||||
|
self.check_filter = HostMatcher("ignore", options.ignore_hosts)
|
||||||
|
elif options.allow_hosts:
|
||||||
|
self.check_filter = HostMatcher("allow", options.allow_hosts)
|
||||||
|
else:
|
||||||
|
self.check_filter = HostMatcher(False)
|
||||||
if "tcp_hosts" in updated:
|
if "tcp_hosts" in updated:
|
||||||
self.check_tcp = HostMatcher(options.tcp_hosts)
|
self.check_tcp = HostMatcher("tcp", options.tcp_hosts)
|
||||||
|
|
||||||
certstore_path = os.path.expanduser(options.confdir)
|
certstore_path = os.path.expanduser(options.confdir)
|
||||||
if not os.path.exists(os.path.dirname(certstore_path)):
|
if not os.path.exists(os.path.dirname(certstore_path)):
|
||||||
|
@ -48,17 +48,17 @@ class RootContext:
|
|||||||
raise exceptions.ProtocolException(str(e))
|
raise exceptions.ProtocolException(str(e))
|
||||||
client_tls = tls.is_tls_record_magic(d)
|
client_tls = tls.is_tls_record_magic(d)
|
||||||
|
|
||||||
# 1. check for --ignore
|
# 1. check for filter
|
||||||
if self.config.check_ignore:
|
if self.config.check_filter:
|
||||||
ignore = self.config.check_ignore(top_layer.server_conn.address)
|
is_filtered = self.config.check_filter(top_layer.server_conn.address)
|
||||||
if not ignore and client_tls:
|
if not is_filtered and client_tls:
|
||||||
try:
|
try:
|
||||||
client_hello = tls.ClientHello.from_file(self.client_conn.rfile)
|
client_hello = tls.ClientHello.from_file(self.client_conn.rfile)
|
||||||
except exceptions.TlsProtocolException as e:
|
except exceptions.TlsProtocolException as e:
|
||||||
self.log("Cannot parse Client Hello: %s" % repr(e), "error")
|
self.log("Cannot parse Client Hello: %s" % repr(e), "error")
|
||||||
else:
|
else:
|
||||||
ignore = self.config.check_ignore((client_hello.sni, 443))
|
is_filtered = self.config.check_filter((client_hello.sni, 443))
|
||||||
if ignore:
|
if is_filtered:
|
||||||
return protocol.RawTCPLayer(top_layer, ignore=True)
|
return protocol.RawTCPLayer(top_layer, ignore=True)
|
||||||
|
|
||||||
# 2. Always insert a TLS layer, even if there's neither client nor server tls.
|
# 2. Always insert a TLS layer, even if there's neither client nor server tls.
|
||||||
|
@ -65,6 +65,7 @@ def common_options(parser, opts):
|
|||||||
opts.make_parser(group, "listen_port", metavar="PORT", short="p")
|
opts.make_parser(group, "listen_port", metavar="PORT", short="p")
|
||||||
opts.make_parser(group, "server", short="n")
|
opts.make_parser(group, "server", short="n")
|
||||||
opts.make_parser(group, "ignore_hosts", metavar="HOST")
|
opts.make_parser(group, "ignore_hosts", metavar="HOST")
|
||||||
|
opts.make_parser(group, "allow_hosts", metavar="HOST")
|
||||||
opts.make_parser(group, "tcp_hosts", metavar="HOST")
|
opts.make_parser(group, "tcp_hosts", metavar="HOST")
|
||||||
opts.make_parser(group, "upstream_auth", metavar="USER:PASS")
|
opts.make_parser(group, "upstream_auth", metavar="USER:PASS")
|
||||||
opts.make_parser(group, "proxyauth", metavar="SPEC")
|
opts.make_parser(group, "proxyauth", metavar="SPEC")
|
||||||
|
@ -215,6 +215,10 @@ class StatusBar(urwid.WidgetWrap):
|
|||||||
r.append("[")
|
r.append("[")
|
||||||
r.append(("heading_key", "I"))
|
r.append(("heading_key", "I"))
|
||||||
r.append("gnore:%d]" % len(self.master.options.ignore_hosts))
|
r.append("gnore:%d]" % len(self.master.options.ignore_hosts))
|
||||||
|
elif self.master.options.allow_hosts:
|
||||||
|
r.append("[")
|
||||||
|
r.append(("heading_key", "A"))
|
||||||
|
r.append("llow:%d]" % len(self.master.options.allow_hosts))
|
||||||
if self.master.options.tcp_hosts:
|
if self.master.options.tcp_hosts:
|
||||||
r.append("[")
|
r.append("[")
|
||||||
r.append(("heading_key", "T"))
|
r.append(("heading_key", "T"))
|
||||||
|
@ -78,6 +78,16 @@ class TcpMixin:
|
|||||||
self.options.ignore_hosts = self._ignore_backup
|
self.options.ignore_hosts = self._ignore_backup
|
||||||
del self._ignore_backup
|
del self._ignore_backup
|
||||||
|
|
||||||
|
def _allow_on(self):
|
||||||
|
assert not hasattr(self, "_allow_backup")
|
||||||
|
self._allow_backup = self.options.allow_hosts
|
||||||
|
self.options.allow_hosts = ["(127.0.0.1|None):\\d+"] + self.options.allow_hosts
|
||||||
|
|
||||||
|
def _allow_off(self):
|
||||||
|
assert hasattr(self, "_allow_backup")
|
||||||
|
self.options.allow_hosts = self._allow_backup
|
||||||
|
del self._allow_backup
|
||||||
|
|
||||||
def test_ignore(self):
|
def test_ignore(self):
|
||||||
n = self.pathod("304")
|
n = self.pathod("304")
|
||||||
self._ignore_on()
|
self._ignore_on()
|
||||||
@ -111,6 +121,40 @@ class TcpMixin:
|
|||||||
|
|
||||||
self._ignore_off()
|
self._ignore_off()
|
||||||
|
|
||||||
|
def test_allow(self):
|
||||||
|
n = self.pathod("304")
|
||||||
|
self._allow_on()
|
||||||
|
i = self.pathod("305")
|
||||||
|
i2 = self.pathod("306")
|
||||||
|
self._allow_off()
|
||||||
|
|
||||||
|
assert n.status_code == 304
|
||||||
|
assert i.status_code == 305
|
||||||
|
assert i2.status_code == 306
|
||||||
|
|
||||||
|
assert any(f.response.status_code == 304 for f in self.master.state.flows)
|
||||||
|
assert any(f.response.status_code == 305 for f in self.master.state.flows)
|
||||||
|
assert any(f.response.status_code == 306 for f in self.master.state.flows)
|
||||||
|
|
||||||
|
# Test that we get the original SSL cert
|
||||||
|
if self.ssl:
|
||||||
|
i_cert = certs.Cert(i.sslinfo.certchain[0])
|
||||||
|
i2_cert = certs.Cert(i2.sslinfo.certchain[0])
|
||||||
|
n_cert = certs.Cert(n.sslinfo.certchain[0])
|
||||||
|
|
||||||
|
assert i_cert == i2_cert
|
||||||
|
assert i_cert != n_cert
|
||||||
|
|
||||||
|
# Test Non-HTTP traffic
|
||||||
|
spec = "200:i0,@100:d0" # this results in just 100 random bytes
|
||||||
|
# mitmproxy responds with bad gateway
|
||||||
|
assert self.pathod(spec).status_code == 502
|
||||||
|
self._allow_on()
|
||||||
|
|
||||||
|
self.pathod(spec) # pathoc parses answer as HTTP
|
||||||
|
|
||||||
|
self._allow_off()
|
||||||
|
|
||||||
def _tcpproxy_on(self):
|
def _tcpproxy_on(self):
|
||||||
assert not hasattr(self, "_tcpproxy_backup")
|
assert not hasattr(self, "_tcpproxy_backup")
|
||||||
self._tcpproxy_backup = self.options.tcp_hosts
|
self._tcpproxy_backup = self.options.tcp_hosts
|
||||||
@ -852,10 +896,12 @@ class TestUpstreamProxySSL(
|
|||||||
|
|
||||||
def _host_pattern_on(self, attr):
|
def _host_pattern_on(self, attr):
|
||||||
"""
|
"""
|
||||||
Updates config.check_tcp or check_ignore, depending on attr.
|
Updates config.check_tcp or check_filter, depending on attr.
|
||||||
"""
|
"""
|
||||||
assert not hasattr(self, "_ignore_%s_backup" % attr)
|
assert not hasattr(self, "_ignore_%s_backup" % attr)
|
||||||
backup = []
|
backup = []
|
||||||
|
handle = attr
|
||||||
|
attr = "filter" if attr in ["allow", "ignore"] else attr
|
||||||
for proxy in self.chain:
|
for proxy in self.chain:
|
||||||
old_matcher = getattr(
|
old_matcher = getattr(
|
||||||
proxy.tmaster.server.config,
|
proxy.tmaster.server.config,
|
||||||
@ -865,12 +911,13 @@ class TestUpstreamProxySSL(
|
|||||||
setattr(
|
setattr(
|
||||||
proxy.tmaster.server.config,
|
proxy.tmaster.server.config,
|
||||||
"check_%s" % attr,
|
"check_%s" % attr,
|
||||||
HostMatcher([".+:%s" % self.server.port] + old_matcher.patterns)
|
HostMatcher(handle, [".+:%s" % self.server.port] + old_matcher.patterns)
|
||||||
)
|
)
|
||||||
|
|
||||||
setattr(self, "_ignore_%s_backup" % attr, backup)
|
setattr(self, "_ignore_%s_backup" % attr, backup)
|
||||||
|
|
||||||
def _host_pattern_off(self, attr):
|
def _host_pattern_off(self, attr):
|
||||||
|
attr = "filter" if attr in ["allow", "ignore"] else attr
|
||||||
backup = getattr(self, "_ignore_%s_backup" % attr)
|
backup = getattr(self, "_ignore_%s_backup" % attr)
|
||||||
for proxy in reversed(self.chain):
|
for proxy in reversed(self.chain):
|
||||||
setattr(
|
setattr(
|
||||||
|
Loading…
Reference in New Issue
Block a user