diff options
Diffstat (limited to 'libmproxy/proxy')
-rw-r--r-- | libmproxy/proxy/config.py | 60 | ||||
-rw-r--r-- | libmproxy/proxy/primitives.py | 43 | ||||
-rw-r--r-- | libmproxy/proxy/server.py | 72 |
3 files changed, 85 insertions, 90 deletions
diff --git a/libmproxy/proxy/config.py b/libmproxy/proxy/config.py index 38c6ce89..ae24d4c9 100644 --- a/libmproxy/proxy/config.py +++ b/libmproxy/proxy/config.py @@ -1,7 +1,7 @@ import os from .. import utils, platform from netlib import http_auth, certutils - +from .primitives import ConstUpstreamServerResolver, TransparentUpstreamServerResolver TRANSPARENT_SSL_PORTS = [443, 8443] CONF_BASENAME = "mitmproxy" @@ -10,17 +10,17 @@ CONF_DIR = "~/.mitmproxy" class ProxyConfig: def __init__(self, confdir=CONF_DIR, clientcerts=None, - no_upstream_cert=False, body_size_limit=None, reverse_proxy=None, - forward_proxy=None, transparent_proxy=None, authenticator=None, + no_upstream_cert=False, body_size_limit=None, get_upstream_server=None, + http_form_in="absolute", http_form_out="relative", authenticator=None, ciphers=None, certs=None ): self.ciphers = ciphers self.clientcerts = clientcerts self.no_upstream_cert = no_upstream_cert self.body_size_limit = body_size_limit - self.reverse_proxy = reverse_proxy - self.forward_proxy = forward_proxy - self.transparent_proxy = transparent_proxy + self.get_upstream_server = get_upstream_server + self.http_form_in = http_form_in + self.http_form_out = http_form_out self.authenticator = authenticator self.confdir = os.path.expanduser(confdir) self.certstore = certutils.CertStore.from_store(self.confdir, CONF_BASENAME) @@ -28,32 +28,34 @@ class ProxyConfig: def process_proxy_options(parser, options): body_size_limit = utils.parse_size(options.body_size_limit) - if options.reverse_proxy and options.transparent_proxy: - return parser.error("Can't set both reverse proxy and transparent proxy.") + c = 0 + http_form_in, http_form_out = "absolute", "relative" + get_upstream_server = None if options.transparent_proxy: + c += 1 if not platform.resolver: return parser.error("Transparent mode not supported on this platform.") - trans = dict( - resolver=platform.resolver(), - sslports=TRANSPARENT_SSL_PORTS - ) - else: - trans = None - + get_upstream_server = TransparentUpstreamServerResolver(platform.resolver(), TRANSPARENT_SSL_PORTS) + http_form_in, http_form_out = "relative", "relative" if options.reverse_proxy: - rp = utils.parse_proxy_spec(options.reverse_proxy) - if not rp: - return parser.error("Invalid reverse proxy specification: %s" % options.reverse_proxy) - else: - rp = None - + c += 1 + get_upstream_server = ConstUpstreamServerResolver(options.reverse_proxy) + http_form_in, http_form_out = "relative", "relative" if options.forward_proxy: - fp = utils.parse_proxy_spec(options.forward_proxy) - if not fp: - return parser.error("Invalid forward proxy specification: %s" % options.forward_proxy) - else: - fp = None + c += 1 + get_upstream_server = ConstUpstreamServerResolver(options.forward_proxy) + http_form_in, http_form_out = "absolute", "absolute" + if options.manual_upstream_server: + c += 1 + get_upstream_server = ConstUpstreamServerResolver(options.manual_upstream_server) + if c > 1: + return parser.error("Transparent mode, reverse mode, forward mode and " + "specification of an upstream server are mutually exclusive.") + if options.http_form_in: + http_form_in = options.http_form_in + if options.http_form_out: + http_form_out = options.http_form_out if options.clientcerts: options.clientcerts = os.path.expanduser(options.clientcerts) @@ -93,9 +95,9 @@ def process_proxy_options(parser, options): clientcerts=options.clientcerts, body_size_limit=body_size_limit, no_upstream_cert=options.no_upstream_cert, - reverse_proxy=rp, - forward_proxy=fp, - transparent_proxy=trans, + get_upstream_server=get_upstream_server, + http_form_in=http_form_in, + http_form_out=http_form_out, authenticator=authenticator, ciphers=options.ciphers, certs = certs, diff --git a/libmproxy/proxy/primitives.py b/libmproxy/proxy/primitives.py index 8dd0e16a..e49d9bb4 100644 --- a/libmproxy/proxy/primitives.py +++ b/libmproxy/proxy/primitives.py @@ -18,19 +18,48 @@ class ProxyServerError(Exception): pass +class UpstreamServerResolver(object): + def __call__(self, conn): + """ + Returns the address of the server to connect to. + """ + raise NotImplementedError + + +class ConstUpstreamServerResolver(UpstreamServerResolver): + def __init__(self, dst): + self.dst = dst + + def __call__(self, conn): + return self.dst + + +class TransparentUpstreamServerResolver(UpstreamServerResolver): + def __init__(self, resolver, sslports): + self.resolver = resolver + self.sslports = sslports + + def __call__(self, conn): + dst = self.resolver.original_addr(conn) + if not dst: + raise ProxyError(502, "Transparent mode failure: could not resolve original destination.") + + if dst[1] in self.sslports: + ssl = True + else: + ssl = False + return [ssl, ssl] + list(dst) + + class AddressPriority(object): """ Enum that signifies the priority of the given address when choosing the destination host. Higher is better (None < i) """ - FORCE = 5 - """forward mode""" - MANUALLY_CHANGED = 4 + MANUALLY_CHANGED = 3 """user changed the target address in the ui""" - FROM_SETTINGS = 3 - """reverse proxy mode""" - FROM_CONNECTION = 2 - """derived from transparent resolver""" + FROM_SETTINGS = 2 + """upstream server from arguments (reverse proxy, forward proxy or from transparent resolver)""" FROM_PROTOCOL = 1 """derived from protocol (e.g. absolute-form http requests)""" diff --git a/libmproxy/proxy/server.py b/libmproxy/proxy/server.py index 37ec7758..c77ab2a8 100644 --- a/libmproxy/proxy/server.py +++ b/libmproxy/proxy/server.py @@ -1,10 +1,9 @@ import socket -from .. import version, protocol -from libmproxy.proxy.primitives import Log -from .primitives import ProxyServerError -from .connection import ClientConnection, ServerConnection -from .primitives import ProxyError, ConnectionTypeChange, AddressPriority +from OpenSSL import SSL from netlib import tcp +from .primitives import ProxyServerError, Log, ProxyError, ConnectionTypeChange, AddressPriority +from .connection import ClientConnection, ServerConnection +from .. import version, protocol class DummyServer: @@ -23,6 +22,7 @@ class DummyServer: class ProxyServer(tcp.TCPServer): allow_reuse_address = True bound = True + def __init__(self, config, port, host='', server_version=version.NAMEVERSION): """ Raises ProxyServerError if there's a startup problem. @@ -51,20 +51,17 @@ class ProxyServer(tcp.TCPServer): class ConnectionHandler: def __init__(self, config, client_connection, client_address, server, channel, server_version): self.config = config + """@type: libmproxy.proxy.config.ProxyConfig""" self.client_conn = ClientConnection(client_connection, client_address, server) + """@type: libmproxy.proxy.connection.ClientConnection""" self.server_conn = None + """@type: libmproxy.proxy.connection.ServerConnection""" self.channel, self.server_version = channel, server_version self.close = False self.conntype = None self.sni = None - self.mode = "regular" - if self.config.reverse_proxy: - self.mode = "reverse" - if self.config.transparent_proxy: - self.mode = "transparent" - def handle(self): self.log("clientconnect") self.channel.ask("clientconnect", self) @@ -74,25 +71,13 @@ class ConnectionHandler: try: try: # Can we already identify the target server and connect to it? - server_address = None - address_priority = None - if self.config.forward_proxy: - server_address = self.config.forward_proxy[1:] - address_priority = AddressPriority.FORCE - elif self.config.reverse_proxy: - server_address = self.config.reverse_proxy[1:] - address_priority = AddressPriority.FROM_SETTINGS - elif self.config.transparent_proxy: - server_address = self.config.transparent_proxy["resolver"].original_addr( - self.client_conn.connection) - if not server_address: - raise ProxyError(502, "Transparent mode failure: could not resolve original destination.") - address_priority = AddressPriority.FROM_CONNECTION - self.log("transparent to %s:%s" % server_address) - - if server_address: - self.set_server_address(server_address, address_priority) - self._handle_ssl() + if self.config.get_upstream_server: + upstream_info = self.config.get_upstream_server(self.client_conn.connection) + self.set_server_address(upstream_info[2:], AddressPriority.FROM_SETTINGS) + client_ssl, server_ssl = upstream_info[:2] + if client_ssl or server_ssl: + self.establish_server_connection() + self.establish_ssl(client=client_ssl, server=server_ssl) while not self.close: try: @@ -114,28 +99,9 @@ class ConnectionHandler: self.log("clientdisconnect") self.channel.tell("clientdisconnect", self) - def _handle_ssl(self): - """ - Helper function of .handle() - Check if we can already identify SSL connections. - If so, connect to the server and establish an SSL connection - """ - client_ssl = False - server_ssl = False - - if self.config.transparent_proxy: - client_ssl = server_ssl = (self.server_conn.address.port in self.config.transparent_proxy["sslports"]) - elif self.config.reverse_proxy: - client_ssl = server_ssl = (self.config.reverse_proxy[0] == "https") - # TODO: Make protocol generic (as with transparent proxies) - # TODO: Add SSL-terminating capatbility (SSL -> mitmproxy -> plain and vice versa) - if client_ssl or server_ssl: - self.establish_server_connection() - self.establish_ssl(client=client_ssl, server=server_ssl) - def del_server_connection(self): """ - Deletes an existing server connection. + Deletes (and closes) an existing server connection. """ if self.server_conn and self.server_conn.connection: self.server_conn.finish() @@ -152,7 +118,6 @@ class ConnectionHandler: """ Sets a new server address with the given priority. Does not re-establish either connection or SSL handshake. - @type priority: libmproxy.proxy.primitives.AddressPriority """ address = tcp.Address.wrap(address) @@ -188,8 +153,7 @@ class ConnectionHandler: """ Establishes SSL on the existing connection(s) to the server or the client, as specified by the parameters. If the target server is on the pass-through list, - the conntype attribute will be changed and the SSL connection won't be wrapped. - A protocol handler must raise a ConnTypeChanged exception if it detects that this is happening + the conntype attribute will be changed and a ConnTypeChanged exception will be raised. """ # TODO: Implement SSL pass-through handling and change conntype passthrough = [ @@ -198,7 +162,7 @@ class ConnectionHandler: ] if self.server_conn.address.host in passthrough or self.sni in passthrough: self.conntype = "tcp" - return + raise ConnectionTypeChange # Logging if client or server: |