mitmproxy/libmproxy/proxy/server.py
2015-09-17 02:13:28 +02:00

157 lines
4.9 KiB
Python

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), "error")
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))