From f4272de5ec77fb57723e2274e4ddc50d73489e1e Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Thu, 3 Sep 2015 17:01:25 +0200 Subject: remove ServerConnectionMixin.reconnect --- libmproxy/protocol/base.py | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) (limited to 'libmproxy/protocol/base.py') diff --git a/libmproxy/protocol/base.py b/libmproxy/protocol/base.py index 40ec0536..f1718065 100644 --- a/libmproxy/protocol/base.py +++ b/libmproxy/protocol/base.py @@ -81,15 +81,6 @@ class Layer(_LayerCodeCompletion): """ return getattr(self.ctx, name) - def log(self, msg, level, subs=()): - full_msg = [ - "{}: {}".format(repr(self.client_conn.address), msg) - ] - for i in subs: - full_msg.append(" -> " + i) - full_msg = "\n".join(full_msg) - self.channel.tell("log", Log(full_msg, level)) - @property def layers(self): return [self] + self.ctx.layers @@ -106,15 +97,9 @@ class ServerConnectionMixin(object): def __init__(self, server_address=None): super(ServerConnectionMixin, self).__init__() self.server_conn = ServerConnection(server_address) - self._check_self_connect() - - def reconnect(self): - address = self.server_conn.address - self._disconnect() - self.server_conn.address = address - self.connect() + self.__check_self_connect() - def _check_self_connect(self): + def __check_self_connect(self): """ We try to protect the proxy from _accidentally_ connecting to itself, e.g. because of a failed transparent lookup or an invalid configuration. @@ -134,10 +119,10 @@ class ServerConnectionMixin(object): def set_server(self, address, server_tls=None, sni=None, depth=1): if depth == 1: if self.server_conn: - self._disconnect() + self.disconnect() self.log("Set new server address: " + repr(address), "debug") self.server_conn.address = address - self._check_self_connect() + self.__check_self_connect() if server_tls: raise ProtocolException( "Cannot upgrade to TLS, no TLS layer on the protocol stack." @@ -145,15 +130,16 @@ class ServerConnectionMixin(object): else: self.ctx.set_server(address, server_tls, sni, depth - 1) - def _disconnect(self): + def disconnect(self): """ Deletes (and closes) an existing server connection. """ self.log("serverdisconnect", "debug", [repr(self.server_conn.address)]) + address = self.server_conn.address self.server_conn.finish() self.server_conn.close() self.channel.tell("serverdisconnect", self.server_conn) - self.server_conn = ServerConnection(None) + self.server_conn = ServerConnection(address) def connect(self): if not self.server_conn.address: @@ -167,12 +153,6 @@ class ServerConnectionMixin(object): "Server connection to %s failed: %s" % (repr(self.server_conn.address), e), e) -class Log(object): - def __init__(self, msg, level="info"): - self.msg = msg - self.level = level - - class Kill(Exception): """ Kill a connection. -- cgit v1.2.3 From 99126f62ed947847eba4cfa687cb0b0f012092bb Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Thu, 3 Sep 2015 18:25:36 +0200 Subject: remove depth attribute from set_server --- libmproxy/protocol/base.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'libmproxy/protocol/base.py') diff --git a/libmproxy/protocol/base.py b/libmproxy/protocol/base.py index f1718065..f27cb04b 100644 --- a/libmproxy/protocol/base.py +++ b/libmproxy/protocol/base.py @@ -116,19 +116,16 @@ class ServerConnectionMixin(object): "The proxy shall not connect to itself.".format(repr(address)) ) - def set_server(self, address, server_tls=None, sni=None, depth=1): - if depth == 1: - if self.server_conn: - self.disconnect() - self.log("Set new server address: " + repr(address), "debug") - self.server_conn.address = address - self.__check_self_connect() - if server_tls: - raise ProtocolException( - "Cannot upgrade to TLS, no TLS layer on the protocol stack." - ) - else: - self.ctx.set_server(address, server_tls, sni, depth - 1) + def set_server(self, address, server_tls=None, sni=None): + if self.server_conn: + self.disconnect() + self.log("Set new server address: " + repr(address), "debug") + self.server_conn.address = address + self.__check_self_connect() + if server_tls: + raise ProtocolException( + "Cannot upgrade to TLS, no TLS layer on the protocol stack." + ) def disconnect(self): """ -- cgit v1.2.3 From 14457f29b3d89e234d0791c4980e5cf9514185dd Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Thu, 3 Sep 2015 18:55:38 +0200 Subject: docs++ --- libmproxy/protocol/base.py | 116 +++++++++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 45 deletions(-) (limited to 'libmproxy/protocol/base.py') diff --git a/libmproxy/protocol/base.py b/libmproxy/protocol/base.py index f27cb04b..9d8c8bfe 100644 --- a/libmproxy/protocol/base.py +++ b/libmproxy/protocol/base.py @@ -1,37 +1,3 @@ -""" -mitmproxy protocol architecture - -In mitmproxy, protocols are implemented as a set of layers, which are composed on top each other. -For example, the following scenarios depict possible settings (lowest layer first): - -Transparent HTTP proxy, no SSL: - TransparentProxy - Http1Layer - HttpLayer - -Regular proxy, CONNECT request with WebSockets over SSL: - HttpProxy - Http1Layer - HttpLayer - SslLayer - WebsocketLayer (or TcpLayer) - -Automated protocol detection by peeking into the buffer: - TransparentProxy - TLSLayer - Http2Layer - HttpLayer - -Communication between layers is done as follows: - - lower layers provide context information to higher layers - - higher layers can call functions provided by lower layers, - which are propagated until they reach a suitable layer. - -Further goals: - - Connections should always be peekable to make automatic protocol detection work. - - Upstream connections should be established as late as possible; - inline scripts shall have a chance to handle everything locally. -""" from __future__ import (absolute_import, print_function, division) from netlib import tcp from ..models import ServerConnection @@ -43,8 +9,8 @@ class _LayerCodeCompletion(object): Dummy class that provides type hinting in PyCharm, which simplifies development a lot. """ - def __init__(self, *args, **kwargs): # pragma: nocover - super(_LayerCodeCompletion, self).__init__(*args, **kwargs) + def __init__(self, **mixin_args): # pragma: nocover + super(_LayerCodeCompletion, self).__init__(**mixin_args) if True: return self.config = None @@ -55,34 +21,64 @@ class _LayerCodeCompletion(object): """@type: libmproxy.models.ServerConnection""" self.channel = None """@type: libmproxy.controller.Channel""" + self.ctx = None + """@type: libmproxy.protocol.Layer""" class Layer(_LayerCodeCompletion): - def __init__(self, ctx, *args, **kwargs): + """ + Base class for all layers. All other protocol layers should inherit from this class. + """ + + def __init__(self, ctx, **mixin_args): """ + Each layer usually passes itself to its child layers as a context. Properties of the + context are transparently mapped to the layer, so that the following works: + + .. code-block:: python + + root_layer = Layer(None) + root_layer.client_conn = 42 + sub_layer = Layer(root_layer) + print(sub_layer.client_conn) # 42 + + The root layer is passed a :py:class:`libmproxy.proxy.RootContext` object, + which provides access to :py:attr:`.client_conn `, + :py:attr:`.next_layer ` and other basic attributes. + Args: - ctx: The (read-only) higher layer. + ctx: The (read-only) parent layer / context. """ self.ctx = ctx - """@type: libmproxy.protocol.Layer""" - super(Layer, self).__init__(*args, **kwargs) + """ + The parent layer. - def __call__(self): + :type: :py:class:`Layer` """ - Logic of the layer. + super(Layer, self).__init__(**mixin_args) + + def __call__(self): + """Logic of the layer. + + Returns: + Once the protocol has finished without exceptions. + Raises: - ProtocolException in case of protocol exceptions. + ~libmproxy.exceptions.ProtocolException: if an exception occurs. No other exceptions must be raised. """ raise NotImplementedError() def __getattr__(self, name): """ - Attributes not present on the current layer may exist on a higher layer. + Attributes not present on the current layer are looked up on the context. """ return getattr(self.ctx, name) @property def layers(self): + """ + List of all layers, including the current layer (``[self, self.ctx, self.ctx.ctx, ...]``) + """ return [self] + self.ctx.layers def __repr__(self): @@ -92,6 +88,20 @@ class Layer(_LayerCodeCompletion): class ServerConnectionMixin(object): """ Mixin that provides a layer with the capabilities to manage a server connection. + The server address can be passed in the constructor or set by calling :py:meth:`set_server`. + Subclasses are responsible for calling :py:meth:`disconnect` before returning. + + Recommended Usage: + + .. code-block:: python + + class MyLayer(Layer, ServerConnectionMixin): + def __call__(self): + try: + # Do something. + finally: + if self.server_conn: + self.disconnect() """ def __init__(self, server_address=None): @@ -117,6 +127,14 @@ class ServerConnectionMixin(object): ) def set_server(self, address, server_tls=None, sni=None): + """ + Sets a new server address. If there is an existing connection, it will be closed. + + Raises: + ~libmproxy.exceptions.ProtocolException: + if ``server_tls`` is ``True``, but there was no TLS layer on the + protocol stack which could have processed this. + """ if self.server_conn: self.disconnect() self.log("Set new server address: " + repr(address), "debug") @@ -130,6 +148,7 @@ class ServerConnectionMixin(object): def disconnect(self): """ Deletes (and closes) an existing server connection. + Must not be called if there is no existing connection. """ self.log("serverdisconnect", "debug", [repr(self.server_conn.address)]) address = self.server_conn.address @@ -139,6 +158,13 @@ class ServerConnectionMixin(object): self.server_conn = ServerConnection(address) def connect(self): + """ + Establishes a server connection. + Must not be called if there is an existing connection. + + Raises: + ~libmproxy.exceptions.ProtocolException: if the connection could not be established. + """ if not self.server_conn.address: raise ProtocolException("Cannot connect to server, no server address given.") self.log("serverconnect", "debug", [repr(self.server_conn.address)]) @@ -152,5 +178,5 @@ class ServerConnectionMixin(object): class Kill(Exception): """ - Kill a connection. + Signal that both client and server connection(s) should be killed immediately. """ -- cgit v1.2.3 From ffdf143be42490f05cb2b69cdb83e74264d6070a Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Fri, 11 Sep 2015 01:39:33 +0200 Subject: better exception handling --- libmproxy/protocol/base.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'libmproxy/protocol/base.py') diff --git a/libmproxy/protocol/base.py b/libmproxy/protocol/base.py index 9d8c8bfe..6793d3df 100644 --- a/libmproxy/protocol/base.py +++ b/libmproxy/protocol/base.py @@ -1,4 +1,6 @@ from __future__ import (absolute_import, print_function, division) +import six +import sys from netlib import tcp from ..models import ServerConnection from ..exceptions import ProtocolException @@ -172,8 +174,12 @@ class ServerConnectionMixin(object): try: self.server_conn.connect() except tcp.NetLibError as e: - raise ProtocolException( - "Server connection to %s failed: %s" % (repr(self.server_conn.address), e), e) + six.reraise( + ProtocolException, + ProtocolException("Server connection to %s failed: %s" % + (repr(self.server_conn.address), e), e), + sys.exc_info()[2] + ) class Kill(Exception): -- cgit v1.2.3 From dd414e485212e3cab612a66d5d858c1a766ace04 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Fri, 11 Sep 2015 02:17:04 +0200 Subject: better error messages, remove error cause --- libmproxy/protocol/base.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'libmproxy/protocol/base.py') diff --git a/libmproxy/protocol/base.py b/libmproxy/protocol/base.py index 6793d3df..b92aeea1 100644 --- a/libmproxy/protocol/base.py +++ b/libmproxy/protocol/base.py @@ -1,6 +1,8 @@ from __future__ import (absolute_import, print_function, division) -import six import sys + +import six + from netlib import tcp from ..models import ServerConnection from ..exceptions import ProtocolException @@ -176,8 +178,11 @@ class ServerConnectionMixin(object): except tcp.NetLibError as e: six.reraise( ProtocolException, - ProtocolException("Server connection to %s failed: %s" % - (repr(self.server_conn.address), e), e), + ProtocolException( + "Server connection to {} failed: {}".format( + repr(self.server_conn.address), str(e) + ) + ), sys.exc_info()[2] ) -- cgit v1.2.3