from __future__ import (absolute_import, print_function, division) import traceback import sys import socket import six from netlib import tcp from netlib.exceptions import TcpException from netlib.http.http1 import assemble_response from ..exceptions import ProtocolException, ServerException, ClientHandshakeException from ..protocol import Kill from ..models import ClientConnection, make_error_response from .modes import HttpUpstreamProxy, HttpProxy, ReverseProxy, TransparentProxy, Socks5Proxy from .root_context import RootContext, Log class DummyServer: bound = False def __init__(self, config): self.config = config def start_slave(self, *args): pass def shutdown(self): pass class ProxyServer(tcp.TCPServer): allow_reuse_address = True bound = True def __init__(self, config): """ Raises ProxyServerError if there's a startup problem. """ self.config = config try: super(ProxyServer, self).__init__((config.host, config.port)) except socket.error as e: six.reraise( ServerException, ServerException('Error starting proxy server: ' + repr(e)), sys.exc_info()[2] ) self.channel = None def start_slave(self, klass, channel): slave = klass(channel, self) slave.start() def set_channel(self, channel): self.channel = channel def handle_client_connection(self, conn, client_address): h = ConnectionHandler( conn, client_address, self.config, self.channel ) h.handle() class ConnectionHandler(object): def __init__(self, client_conn, client_address, config, channel): self.config = config """@type: libmproxy.proxy.config.ProxyConfig""" self.client_conn = ClientConnection( client_conn, client_address, None) """@type: libmproxy.proxy.connection.ClientConnection""" self.channel = channel """@type: libmproxy.controller.Channel""" def _create_root_layer(self): root_context = RootContext( self.client_conn, self.config, self.channel ) mode = self.config.mode if mode == "upstream": return HttpUpstreamProxy( root_context, self.config.upstream_server.address ) elif mode == "transparent": return TransparentProxy(root_context) elif mode == "reverse": server_tls = self.config.upstream_server.scheme == "https" return ReverseProxy( root_context, self.config.upstream_server.address, server_tls ) elif mode == "socks5": return Socks5Proxy(root_context) elif mode == "regular": return HttpProxy(root_context) elif callable(mode): # pragma: nocover return mode(root_context) else: # pragma: nocover raise ValueError("Unknown proxy mode: %s" % mode) def handle(self): self.log("clientconnect", "info") root_layer = self._create_root_layer() root_layer = self.channel.ask("clientconnect", root_layer) if root_layer == Kill: def root_layer(): raise Kill() try: root_layer() except Kill: self.log("Connection killed", "info") except ProtocolException as e: if isinstance(e, ClientHandshakeException): self.log( "Client Handshake failed. " "The client may not trust the proxy's certificate for {}.".format(e.server), "error" ) self.log(repr(e), "debug") else: self.log(repr(e), "info") self.log(traceback.format_exc(), "debug") # If an error propagates to the topmost level, # we send an HTTP error response, which is both # understandable by HTTP clients and humans. try: error_response = make_error_response(502, repr(e)) self.client_conn.send(assemble_response(error_response)) except TcpException: pass except Exception: self.log(traceback.format_exc(), "error") print(traceback.format_exc(), file=sys.stderr) print("mitmproxy has crashed!", file=sys.stderr) print("Please lodge a bug report at: https://github.com/mitmproxy/mitmproxy", file=sys.stderr) self.log("clientdisconnect", "info") self.channel.tell("clientdisconnect", root_layer) self.client_conn.finish() def log(self, msg, level): msg = "{}: {}".format(repr(self.client_conn.address), msg) self.channel.tell("log", Log(msg, level))