From fc4fe83eafc68ebb9763fa5cbee1ed7e16964c9c Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Sun, 9 Mar 2014 21:13:08 +0100 Subject: split up proxy.py --- libmproxy/flow.py | 1 - libmproxy/protocol/__init__.py | 9 +- libmproxy/protocol/http.py | 10 ++- libmproxy/protocol/primitives.py | 2 +- libmproxy/proxy.py | 179 ++++----------------------------------- libmproxy/prxy/__init__.py | 0 libmproxy/prxy/connection.py | 138 ++++++++++++++++++++++++++++++ libmproxy/prxy/exception.py | 14 +++ libmproxy/prxy/server.py | 18 ++++ test/test_dump.py | 4 +- test/test_flow.py | 5 +- test/test_proxy.py | 8 +- test/tutils.py | 7 +- 13 files changed, 207 insertions(+), 188 deletions(-) create mode 100644 libmproxy/prxy/__init__.py create mode 100644 libmproxy/prxy/connection.py create mode 100644 libmproxy/prxy/exception.py create mode 100644 libmproxy/prxy/server.py diff --git a/libmproxy/flow.py b/libmproxy/flow.py index f8ad2444..c362465c 100644 --- a/libmproxy/flow.py +++ b/libmproxy/flow.py @@ -9,7 +9,6 @@ import flask import requests import tnetstring, filt, script from netlib import odict, wsgi -from .proxy import ClientConnection, ServerConnection # FIXME: remove circular dependency import controller, version, protocol import app from .protocol import KILL diff --git a/libmproxy/protocol/__init__.py b/libmproxy/protocol/__init__.py index 2c2e7285..392a8e3d 100644 --- a/libmproxy/protocol/__init__.py +++ b/libmproxy/protocol/__init__.py @@ -1,14 +1,7 @@ -from ..proxy import ServerConnection, AddressPriority +from ..prxy.server import AddressPriority KILL = 0 # const for killed requests -class ConnectionTypeChange(Exception): - """ - Gets raised if the connetion type has been changed (e.g. after HTTP/1.1 101 Switching Protocols). - It's up to the raising ProtocolHandler to specify the new conntype before raising the exception. - """ - pass - class ProtocolHandler(object): def __init__(self, c): diff --git a/libmproxy/protocol/http.py b/libmproxy/protocol/http.py index 8a2583b1..3f9668ae 100644 --- a/libmproxy/protocol/http.py +++ b/libmproxy/protocol/http.py @@ -1,11 +1,13 @@ import Cookie, urllib, urlparse, time, copy from email.utils import parsedate_tz, formatdate, mktime_tz +from ..prxy.connection import ServerConnection +from ..prxy.exception import ProxyError, ConnectionTypeChange +from ..prxy.server import AddressPriority import netlib.utils -from netlib import http, tcp, http_status, odict +from netlib import http, tcp, http_status from netlib.odict import ODict, ODictCaseless -from . import ProtocolHandler, ConnectionTypeChange, KILL, TemporaryServerChangeMixin -from .. import encoding, utils, version, filt, controller, stateobject -from ..proxy import ProxyError, AddressPriority, ServerConnection +from . import ProtocolHandler, KILL, TemporaryServerChangeMixin +from .. import encoding, utils, filt, controller, stateobject from .primitives import Flow, Error diff --git a/libmproxy/protocol/primitives.py b/libmproxy/protocol/primitives.py index 90191eeb..7cee074d 100644 --- a/libmproxy/protocol/primitives.py +++ b/libmproxy/protocol/primitives.py @@ -1,5 +1,5 @@ from .. import stateobject, utils, version -from ..proxy import ServerConnection, ClientConnection +from ..prxy.connection import ClientConnection, ServerConnection import copy diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 6dd37752..ccb47c26 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -1,7 +1,18 @@ -import os, socket, time, threading, copy +import os +import socket +import threading + from OpenSSL import SSL + +from .prxy.connection import ClientConnection, ServerConnection +from .prxy.exception import ProxyError, ConnectionTypeChange +from .prxy.server import AddressPriority from netlib import tcp, http, certutils, http_auth -import utils, version, platform, controller, stateobject +import utils +import version +import platform +import controller + TRANSPARENT_SSL_PORTS = [443, 8443] CONF_BASENAME = "mitmproxy" @@ -9,32 +20,6 @@ CONF_DIR = "~/.mitmproxy" CA_CERT_NAME = "mitmproxy-ca.pem" - -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 - """user changed the target address in the ui""" - FROM_SETTINGS = 3 - """reverse proxy mode""" - FROM_CONNECTION = 2 - """derived from transparent resolver""" - FROM_PROTOCOL = 1 - """derived from protocol (e.g. absolute-form http requests)""" - - -class ProxyError(Exception): - def __init__(self, code, msg, headers=None): - self.code, self.msg, self.headers = code, msg, headers - - def __str__(self): - return "ProxyError(%s, %s)" % (self.code, self.msg) - - class Log: def __init__(self, msg): self.msg = msg @@ -58,140 +43,6 @@ class ProxyConfig: self.certstore = certutils.CertStore.from_store(self.confdir, CONF_BASENAME) - -class ClientConnection(tcp.BaseHandler, stateobject.SimpleStateObject): - def __init__(self, client_connection, address, server): - if client_connection: # Eventually, this object is restored from state. We don't have a connection then. - tcp.BaseHandler.__init__(self, client_connection, address, server) - else: - self.connection = None - self.server = None - self.wfile = None - self.rfile = None - self.address = None - self.clientcert = None - - self.timestamp_start = utils.timestamp() - self.timestamp_end = None - self.timestamp_ssl_setup = None - - _stateobject_attributes = dict( - timestamp_start=float, - timestamp_end=float, - timestamp_ssl_setup=float - ) - - def _get_state(self): - d = super(ClientConnection, self)._get_state() - d.update( - address={"address": self.address(), "use_ipv6": self.address.use_ipv6}, - clientcert=self.cert.to_pem() if self.clientcert else None - ) - return d - - def _load_state(self, state): - super(ClientConnection, self)._load_state(state) - self.address = tcp.Address(**state["address"]) if state["address"] else None - self.clientcert = certutils.SSLCert.from_pem(state["clientcert"]) if state["clientcert"] else None - - def copy(self): - return copy.copy(self) - - def send(self, message): - self.wfile.write(message) - self.wfile.flush() - - @classmethod - def _from_state(cls, state): - f = cls(None, tuple(), None) - f._load_state(state) - return f - - def convert_to_ssl(self, *args, **kwargs): - tcp.BaseHandler.convert_to_ssl(self, *args, **kwargs) - self.timestamp_ssl_setup = utils.timestamp() - - def finish(self): - tcp.BaseHandler.finish(self) - self.timestamp_end = utils.timestamp() - - -class ServerConnection(tcp.TCPClient, stateobject.SimpleStateObject): - def __init__(self, address, priority): - tcp.TCPClient.__init__(self, address) - self.priority = priority - - self.peername = None - self.timestamp_start = None - self.timestamp_end = None - self.timestamp_tcp_setup = None - self.timestamp_ssl_setup = None - - _stateobject_attributes = dict( - peername=tuple, - timestamp_start=float, - timestamp_end=float, - timestamp_tcp_setup=float, - timestamp_ssl_setup=float, - address=tcp.Address, - source_address=tcp.Address, - cert=certutils.SSLCert, - ssl_established=bool, - sni=str - ) - - def _get_state(self): - d = super(ServerConnection, self)._get_state() - d.update( - address={"address": self.address(), "use_ipv6": self.address.use_ipv6}, - source_address= {"address": self.source_address(), - "use_ipv6": self.source_address.use_ipv6} if self.source_address else None, - cert=self.cert.to_pem() if self.cert else None - ) - return d - - def _load_state(self, state): - super(ServerConnection, self)._load_state(state) - - self.address = tcp.Address(**state["address"]) if state["address"] else None - self.source_address = tcp.Address(**state["source_address"]) if state["source_address"] else None - self.cert = certutils.SSLCert.from_pem(state["cert"]) if state["cert"] else None - - @classmethod - def _from_state(cls, state): - f = cls(tuple(), None) - f._load_state(state) - return f - - def copy(self): - return copy.copy(self) - - def connect(self): - self.timestamp_start = utils.timestamp() - tcp.TCPClient.connect(self) - self.peername = self.connection.getpeername() - self.timestamp_tcp_setup = utils.timestamp() - - def send(self, message): - self.wfile.write(message) - self.wfile.flush() - - def establish_ssl(self, clientcerts, sni): - clientcert = None - if clientcerts: - path = os.path.join(clientcerts, self.address.host.encode("idna")) + ".pem" - if os.path.exists(path): - clientcert = path - try: - self.convert_to_ssl(cert=clientcert, sni=sni) - self.timestamp_ssl_setup = utils.timestamp() - except tcp.NetLibError, v: - raise ProxyError(400, str(v)) - - def finish(self): - tcp.TCPClient.finish(self) - self.timestamp_end = utils.timestamp() - from . import protocol from .protocol.http import HTTPResponse @@ -268,7 +119,7 @@ class ConnectionHandler: while not self.close: try: protocol.handle_messages(self.conntype, self) - except protocol.ConnectionTypeChange: + except ConnectionTypeChange: self.log("Connection Type Changed: %s" % self.conntype) continue @@ -323,7 +174,7 @@ class ConnectionHandler: """ Sets a new server address with the given priority. Does not re-establish either connection or SSL handshake. - @type priority: AddressPriority + @type priority: libmproxy.prxy.server.AddressPriority """ address = tcp.Address.wrap(address) diff --git a/libmproxy/prxy/__init__.py b/libmproxy/prxy/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/libmproxy/prxy/connection.py b/libmproxy/prxy/connection.py new file mode 100644 index 00000000..b1040c1c --- /dev/null +++ b/libmproxy/prxy/connection.py @@ -0,0 +1,138 @@ +import copy +import os +from .. import stateobject, utils +from .exception import ProxyError +from netlib import tcp, certutils + +class ClientConnection(tcp.BaseHandler, stateobject.SimpleStateObject): + def __init__(self, client_connection, address, server): + if client_connection: # Eventually, this object is restored from state. We don't have a connection then. + tcp.BaseHandler.__init__(self, client_connection, address, server) + else: + self.connection = None + self.server = None + self.wfile = None + self.rfile = None + self.address = None + self.clientcert = None + + self.timestamp_start = utils.timestamp() + self.timestamp_end = None + self.timestamp_ssl_setup = None + + _stateobject_attributes = dict( + timestamp_start=float, + timestamp_end=float, + timestamp_ssl_setup=float + ) + + def _get_state(self): + d = super(ClientConnection, self)._get_state() + d.update( + address={"address": self.address(), "use_ipv6": self.address.use_ipv6}, + clientcert=self.cert.to_pem() if self.clientcert else None + ) + return d + + def _load_state(self, state): + super(ClientConnection, self)._load_state(state) + self.address = tcp.Address(**state["address"]) if state["address"] else None + self.clientcert = certutils.SSLCert.from_pem(state["clientcert"]) if state["clientcert"] else None + + def copy(self): + return copy.copy(self) + + def send(self, message): + self.wfile.write(message) + self.wfile.flush() + + @classmethod + def _from_state(cls, state): + f = cls(None, tuple(), None) + f._load_state(state) + return f + + def convert_to_ssl(self, *args, **kwargs): + tcp.BaseHandler.convert_to_ssl(self, *args, **kwargs) + self.timestamp_ssl_setup = utils.timestamp() + + def finish(self): + tcp.BaseHandler.finish(self) + self.timestamp_end = utils.timestamp() + + +class ServerConnection(tcp.TCPClient, stateobject.SimpleStateObject): + def __init__(self, address, priority): + tcp.TCPClient.__init__(self, address) + self.priority = priority + + self.peername = None + self.timestamp_start = None + self.timestamp_end = None + self.timestamp_tcp_setup = None + self.timestamp_ssl_setup = None + + _stateobject_attributes = dict( + peername=tuple, + timestamp_start=float, + timestamp_end=float, + timestamp_tcp_setup=float, + timestamp_ssl_setup=float, + address=tcp.Address, + source_address=tcp.Address, + cert=certutils.SSLCert, + ssl_established=bool, + sni=str + ) + + def _get_state(self): + d = super(ServerConnection, self)._get_state() + d.update( + address={"address": self.address(), "use_ipv6": self.address.use_ipv6}, + source_address= {"address": self.source_address(), + "use_ipv6": self.source_address.use_ipv6} if self.source_address else None, + cert=self.cert.to_pem() if self.cert else None + ) + return d + + def _load_state(self, state): + super(ServerConnection, self)._load_state(state) + + self.address = tcp.Address(**state["address"]) if state["address"] else None + self.source_address = tcp.Address(**state["source_address"]) if state["source_address"] else None + self.cert = certutils.SSLCert.from_pem(state["cert"]) if state["cert"] else None + + @classmethod + def _from_state(cls, state): + f = cls(tuple(), None) + f._load_state(state) + return f + + def copy(self): + return copy.copy(self) + + def connect(self): + self.timestamp_start = utils.timestamp() + tcp.TCPClient.connect(self) + self.peername = self.connection.getpeername() + self.timestamp_tcp_setup = utils.timestamp() + + def send(self, message): + self.wfile.write(message) + self.wfile.flush() + + def establish_ssl(self, clientcerts, sni): + clientcert = None + if clientcerts: + path = os.path.join(clientcerts, self.address.host.encode("idna")) + ".pem" + if os.path.exists(path): + clientcert = path + try: + self.convert_to_ssl(cert=clientcert, sni=sni) + self.timestamp_ssl_setup = utils.timestamp() + except tcp.NetLibError, v: + raise ProxyError(400, str(v)) + + def finish(self): + tcp.TCPClient.finish(self) + self.timestamp_end = utils.timestamp() \ No newline at end of file diff --git a/libmproxy/prxy/exception.py b/libmproxy/prxy/exception.py new file mode 100644 index 00000000..c43a5d75 --- /dev/null +++ b/libmproxy/prxy/exception.py @@ -0,0 +1,14 @@ +class ProxyError(Exception): + def __init__(self, code, msg, headers=None): + self.code, self.msg, self.headers = code, msg, headers + + def __str__(self): + return "ProxyError(%s, %s)" % (self.code, self.msg) + + +class ConnectionTypeChange(Exception): + """ + Gets raised if the connection type has been changed (e.g. after HTTP/1.1 101 Switching Protocols). + It's up to the raising ProtocolHandler to specify the new conntype before raising the exception. + """ + pass \ No newline at end of file diff --git a/libmproxy/prxy/server.py b/libmproxy/prxy/server.py new file mode 100644 index 00000000..441b29b4 --- /dev/null +++ b/libmproxy/prxy/server.py @@ -0,0 +1,18 @@ +__author__ = 'user' + + +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 + """user changed the target address in the ui""" + FROM_SETTINGS = 3 + """reverse proxy mode""" + FROM_CONNECTION = 2 + """derived from transparent resolver""" + FROM_PROTOCOL = 1 + """derived from protocol (e.g. absolute-form http requests)""" \ No newline at end of file diff --git a/test/test_dump.py b/test/test_dump.py index 8b4b9aa5..c5c231fa 100644 --- a/test/test_dump.py +++ b/test/test_dump.py @@ -1,6 +1,6 @@ import os from cStringIO import StringIO -from libmproxy import dump, flow, proxy +from libmproxy import dump, flow, proxy, prxy import tutils import mock @@ -27,7 +27,7 @@ class TestDumpMaster: cc = req.flow.client_conn cc.reply = mock.MagicMock() m.handle_clientconnect(cc) - sc = proxy.ServerConnection((req.get_host(), req.get_port()), None) + sc = prxy.connection.ServerConnection((req.get_host(), req.get_port()), None) sc.reply = mock.MagicMock() m.handle_serverconnection(sc) m.handle_request(req) diff --git a/test/test_flow.py b/test/test_flow.py index fbead1ca..c7e39f73 100644 --- a/test/test_flow.py +++ b/test/test_flow.py @@ -4,6 +4,7 @@ import email.utils from libmproxy import filt, protocol, controller, utils, tnetstring, proxy, flow from libmproxy.protocol.primitives import Error, Flow from libmproxy.protocol.http import decoded +from libmproxy.prxy.connection import ClientConnection, ServerConnection from netlib import tcp import tutils @@ -586,7 +587,7 @@ class TestFlowMaster: req = tutils.treq() fm.handle_clientconnect(req.flow.client_conn) assert fm.scripts[0].ns["log"][-1] == "clientconnect" - sc = proxy.ServerConnection((req.get_host(), req.get_port()), None) + sc = ServerConnection((req.get_host(), req.get_port()), None) sc.reply = controller.DummyReply() fm.handle_serverconnection(sc) assert fm.scripts[0].ns["log"][-1] == "serverconnect" @@ -1159,7 +1160,7 @@ class TestClientConnection: def test_state(self): c = tutils.tclient_conn() - assert proxy.ClientConnection._from_state(c._get_state()) == c + assert ClientConnection._from_state(c._get_state()) == c c2 = tutils.tclient_conn() c2.address.address = (c2.address.host, 4242) diff --git a/test/test_proxy.py b/test/test_proxy.py index b15e3f84..2a4a250e 100644 --- a/test/test_proxy.py +++ b/test/test_proxy.py @@ -1,5 +1,7 @@ import argparse from libmproxy import proxy, flow, cmdline +from libmproxy.prxy.connection import ServerConnection +from libmproxy.prxy.exception import ProxyError import tutils from libpathod import test from netlib import http, tcp @@ -7,7 +9,7 @@ import mock def test_proxy_error(): - p = proxy.ProxyError(111, "msg") + p = ProxyError(111, "msg") assert str(p) @@ -19,7 +21,7 @@ class TestServerConnection: self.d.shutdown() def test_simple(self): - sc = proxy.ServerConnection((self.d.IFACE, self.d.port), None) + sc = ServerConnection((self.d.IFACE, self.d.port), None) sc.connect() r = tutils.treq() r.flow.server_conn = sc @@ -31,7 +33,7 @@ class TestServerConnection: sc.finish() def test_terminate_error(self): - sc = proxy.ServerConnection((self.d.IFACE, self.d.port), None) + sc = ServerConnection((self.d.IFACE, self.d.port), None) sc.connect() sc.connection = mock.Mock() sc.connection.recv = mock.Mock(return_value=False) diff --git a/test/tutils.py b/test/tutils.py index b1bfc831..8690d67d 100644 --- a/test/tutils.py +++ b/test/tutils.py @@ -2,6 +2,7 @@ import os, shutil, tempfile from contextlib import contextmanager from libmproxy import flow, utils, controller, proxy from libmproxy.protocol import http +from libmproxy.prxy.connection import ClientConnection, ServerConnection import mock_urwid from libmproxy.console.flowview import FlowView from libmproxy.console import ConsoleState @@ -21,7 +22,7 @@ def SkipWindows(fn): def tclient_conn(): - c = proxy.ClientConnection._from_state(dict( + c = ClientConnection._from_state(dict( address=dict(address=("address", 22), use_ipv6=True), clientcert=None )) @@ -30,7 +31,7 @@ def tclient_conn(): def tserver_conn(): - c = proxy.ServerConnection._from_state(dict( + c = ServerConnection._from_state(dict( address=dict(address=("address", 22), use_ipv6=True), source_address=dict(address=("address", 22), use_ipv6=True), cert=None @@ -69,7 +70,7 @@ def tresp(req=None, content="message"): headers = flow.ODictCaseless() headers["header_response"] = ["svalue"] cert = certutils.SSLCert.from_der(file(test_data.path("data/dercert"), "rb").read()) - f.server_conn = proxy.ServerConnection._from_state(dict( + f.server_conn = ServerConnection._from_state(dict( address=dict(address=("address", 22), use_ipv6=True), source_address=None, cert=cert.to_pem())) -- cgit v1.2.3