diff options
| -rw-r--r-- | libmproxy/cmdline.py | 13 | ||||
| -rw-r--r-- | libmproxy/protocol2/http_proxy.py | 5 | ||||
| -rw-r--r-- | libmproxy/protocol2/reverse_proxy.py | 10 | ||||
| -rw-r--r-- | libmproxy/protocol2/root_context.py | 33 | ||||
| -rw-r--r-- | libmproxy/protocol2/tls.py | 13 | ||||
| -rw-r--r-- | libmproxy/proxy/config.py | 2 | ||||
| -rw-r--r-- | libmproxy/proxy/server.py | 4 | ||||
| -rw-r--r-- | test/test_cmdline.py | 4 | ||||
| -rw-r--r-- | test/test_server.py | 4 | 
9 files changed, 49 insertions, 39 deletions
diff --git a/libmproxy/cmdline.py b/libmproxy/cmdline.py index 591e87ed..55377af2 100644 --- a/libmproxy/cmdline.py +++ b/libmproxy/cmdline.py @@ -102,9 +102,9 @@ def parse_setheader(s):      return _parse_hook(s) -def parse_server_spec(url, allowed_schemes=("http", "https")): +def parse_server_spec(url):      p = netlib.utils.parse_url(url) -    if not p or not p[1] or p[0] not in allowed_schemes: +    if not p or not p[1] or p[0] not in ("http", "https"):          raise configargparse.ArgumentTypeError(              "Invalid server specification: %s" % url          ) @@ -113,13 +113,6 @@ def parse_server_spec(url, allowed_schemes=("http", "https")):      return config.ServerSpec(scheme, address) -def parse_server_spec_special(url): -    """ -    Provides additional support for http2https and https2http schemes. -    """ -    return parse_server_spec(url, allowed_schemes=("http", "https", "http2https", "https2http")) - -  def get_common_options(options):      stickycookie, stickyauth = None, None      if options.stickycookie_filt: @@ -297,7 +290,7 @@ def proxy_modes(parser):      group.add_argument(          "-R", "--reverse",          action="store", -        type=parse_server_spec_special, +        type=parse_server_spec,          dest="reverse_proxy",          default=None,          help=""" diff --git a/libmproxy/protocol2/http_proxy.py b/libmproxy/protocol2/http_proxy.py index b3389eb7..2876c022 100644 --- a/libmproxy/protocol2/http_proxy.py +++ b/libmproxy/protocol2/http_proxy.py @@ -1,12 +1,11 @@  from __future__ import (absolute_import, print_function, division)  from .layer import Layer, ServerConnectionMixin -from .http import Http1Layer  class HttpProxy(Layer, ServerConnectionMixin):      def __call__(self): -        layer = Http1Layer(self, "regular") +        layer = self.ctx.next_layer(self)          try:              layer()          finally: @@ -19,7 +18,7 @@ class HttpUpstreamProxy(Layer, ServerConnectionMixin):          super(HttpUpstreamProxy, self).__init__(ctx, server_address=server_address)      def __call__(self): -        layer = Http1Layer(self, "upstream") +        layer = self.ctx.next_layer(self)          try:              layer()          finally: diff --git a/libmproxy/protocol2/reverse_proxy.py b/libmproxy/protocol2/reverse_proxy.py index e959db86..c4cabccc 100644 --- a/libmproxy/protocol2/reverse_proxy.py +++ b/libmproxy/protocol2/reverse_proxy.py @@ -5,16 +5,12 @@ from .tls import TlsLayer  class ReverseProxy(Layer, ServerConnectionMixin): -    def __init__(self, ctx, server_address, client_tls, server_tls): +    def __init__(self, ctx, server_address, server_tls):          super(ReverseProxy, self).__init__(ctx, server_address=server_address) -        self._client_tls = client_tls -        self._server_tls = server_tls +        self.server_tls = server_tls      def __call__(self): -        # Always use a TLS layer here; if someone changes the scheme, there needs to be a -        # TLS layer underneath. -        layer = TlsLayer(self, self._client_tls, self._server_tls) - +        layer = self.ctx.next_layer(self)          try:              layer()          finally: diff --git a/libmproxy/protocol2/root_context.py b/libmproxy/protocol2/root_context.py index 4d69204f..210ba6ab 100644 --- a/libmproxy/protocol2/root_context.py +++ b/libmproxy/protocol2/root_context.py @@ -6,7 +6,9 @@ from netlib.http.http2 import HTTP2Protocol  from .rawtcp import RawTcpLayer  from .tls import TlsLayer, is_tls_record_magic  from .http import Http1Layer, Http2Layer - +from .layer import ServerConnectionMixin +from .http_proxy import HttpProxy, HttpUpstreamProxy +from .reverse_proxy import ReverseProxy  class RootContext(object):      """ @@ -34,18 +36,33 @@ class RootContext(object):          if self.config.check_ignore(top_layer.server_conn.address):              return RawTcpLayer(top_layer, logging=False) -        # 2. Check for TLS -        # TLS ClientHello magic, works for SSLv3, TLSv1.0, TLSv1.1, TLSv1.2 -        # http://www.moserware.com/2009/06/first-few-milliseconds-of-https.html#client-hello          d = top_layer.client_conn.rfile.peek(3) -        if is_tls_record_magic(d): +        client_tls = is_tls_record_magic(d) + +        # 2. Always insert a TLS layer, even if there's neither client nor server tls. +        # An inline script may upgrade from http to https, +        # in which case we need some form of TLS layer. +        if isinstance(top_layer, ReverseProxy): +            return TlsLayer(top_layer, client_tls, top_layer.server_tls) +        if isinstance(top_layer, ServerConnectionMixin): +            return TlsLayer(top_layer, client_tls, client_tls) + +        # 3. In Http Proxy mode and Upstream Proxy mode, the next layer is fixed. +        if isinstance(top_layer, TlsLayer): +            if isinstance(top_layer.ctx, HttpProxy): +                return Http1Layer(top_layer, "regular") +            if isinstance(top_layer.ctx, HttpUpstreamProxy): +                return Http1Layer(top_layer, "upstream") + +        # 4. Check for other TLS cases (e.g. after CONNECT). +        if client_tls:              return TlsLayer(top_layer, True, True) -        # 3. Check for --tcp +        # 4. Check for --tcp          if self.config.check_tcp(top_layer.server_conn.address):              return RawTcpLayer(top_layer) -        # 4. Check for TLS ALPN (HTTP1/HTTP2) +        # 5. Check for TLS ALPN (HTTP1/HTTP2)          if isinstance(top_layer, TlsLayer):              alpn = top_layer.client_conn.get_alpn_proto_negotiated()              if alpn == HTTP2Protocol.ALPN_PROTO_H2: @@ -53,7 +70,7 @@ class RootContext(object):              if alpn == HTTP1Protocol.ALPN_PROTO_HTTP1:                  return Http1Layer(top_layer, 'transparent') -        # 5. Assume HTTP1 by default +        # 6. Assume HTTP1 by default          return Http1Layer(top_layer, 'transparent')          # In a future version, we want to implement TCP passthrough as the last fallback, diff --git a/libmproxy/protocol2/tls.py b/libmproxy/protocol2/tls.py index 0c02b0ea..041adaaa 100644 --- a/libmproxy/protocol2/tls.py +++ b/libmproxy/protocol2/tls.py @@ -18,6 +18,9 @@ def is_tls_record_magic(d):          False, otherwise.      """      d = d[:3] + +    # TLS ClientHello magic, works for SSLv3, TLSv1.0, TLSv1.1, TLSv1.2 +    # http://www.moserware.com/2009/06/first-few-milliseconds-of-https.html#client-hello      return (          len(d) == 3 and          d[0] == '\x16' and @@ -73,6 +76,16 @@ class TlsLayer(Layer):          layer = self.ctx.next_layer(self)          layer() +    def __repr__(self): +        if self._client_tls and self._server_tls: +            return "TlsLayer(client and server)" +        elif self._client_tls: +            return "TlsLayer(client)" +        elif self._server_tls: +            return "TlsLayer(server)" +        else: +            return "TlsLayer(inactive)" +      def _get_client_hello(self):          """          Peek into the socket and read all records that contain the initial client hello message. diff --git a/libmproxy/proxy/config.py b/libmproxy/proxy/config.py index 415ee215..b360abbd 100644 --- a/libmproxy/proxy/config.py +++ b/libmproxy/proxy/config.py @@ -24,6 +24,8 @@ class HostMatcher(object):          self.regexes = [re.compile(p, re.IGNORECASE) for p in self.patterns]      def __call__(self, address): +        if not address: +            return False          address = tcp.Address.wrap(address)          host = "%s:%s" % (address.host, address.port)          if any(rex.search(host) for rex in self.regexes): diff --git a/libmproxy/proxy/server.py b/libmproxy/proxy/server.py index 69784014..5abd0877 100644 --- a/libmproxy/proxy/server.py +++ b/libmproxy/proxy/server.py @@ -86,12 +86,10 @@ class ConnectionHandler(object):          elif mode == "transparent":              return protocol2.TransparentProxy(root_context)          elif mode == "reverse": -            client_tls = self.config.upstream_server.scheme.startswith("https") -            server_tls = self.config.upstream_server.scheme.endswith("https") +            server_tls = self.config.upstream_server.scheme == "https"              return protocol2.ReverseProxy(                  root_context,                  self.config.upstream_server.address, -                client_tls,                  server_tls              )          elif mode == "socks5": diff --git a/test/test_cmdline.py b/test/test_cmdline.py index ee2f7044..1443ee1c 100644 --- a/test/test_cmdline.py +++ b/test/test_cmdline.py @@ -43,10 +43,6 @@ def test_parse_server_spec():          "http://foo.com") == ("http", ("foo.com", 80))      assert cmdline.parse_server_spec(          "https://foo.com") == ("https", ("foo.com", 443)) -    assert cmdline.parse_server_spec_special( -        "https2http://foo.com") == ("https2http", ("foo.com", 80)) -    assert cmdline.parse_server_spec_special( -        "http2https://foo.com") == ("http2https", ("foo.com", 443))      tutils.raises(          "Invalid server specification",          cmdline.parse_server_spec, diff --git a/test/test_server.py b/test/test_server.py index 7b66c582..b691804b 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -490,10 +490,6 @@ class TestHttps2Http(tservers.ReverseProxTest):          assert p.request("get:'/p/200'").status_code == 200          assert all("Error in handle_sni" not in msg for msg in self.proxy.log) -    def test_http(self): -        p = self.pathoc(ssl=False) -        assert p.request("get:'/p/200'").status_code == 502 -  class TestTransparent(tservers.TransparentProxTest, CommonMixin, TcpMixin):      ssl = False  | 
