mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-27 02:24:18 +00:00
cleaner Exceptions, ssl -> tls, upstream proxy mode
This commit is contained in:
parent
314e0f5839
commit
026330a3b0
22
libmproxy/exceptions.py
Normal file
22
libmproxy/exceptions.py
Normal file
@ -0,0 +1,22 @@
|
||||
from __future__ import (absolute_import, print_function, division)
|
||||
|
||||
|
||||
class ProxyException(Exception):
|
||||
"""
|
||||
Base class for all exceptions thrown by libmproxy.
|
||||
"""
|
||||
def __init__(self, message, cause=None):
|
||||
"""
|
||||
:param message: Error Message
|
||||
:param cause: Exception object that caused this exception to be thrown.
|
||||
"""
|
||||
super(ProxyException, self).__init__(message)
|
||||
self.cause = cause
|
||||
|
||||
|
||||
class ProtocolException(ProxyException):
|
||||
pass
|
||||
|
||||
|
||||
class ServerException(ProxyException):
|
||||
pass
|
@ -2,6 +2,7 @@ from __future__ import (absolute_import, print_function, division)
|
||||
from .layer import RootContext
|
||||
from .socks import Socks5IncomingLayer
|
||||
from .reverse_proxy import ReverseProxy
|
||||
from .upstream_proxy import UpstreamProxy
|
||||
from .rawtcp import TcpLayer
|
||||
from .auto import AutoLayer
|
||||
__all__ = ["Socks5IncomingLayer", "TcpLayer", "AutoLayer", "RootContext", "ReverseProxy"]
|
||||
__all__ = ["Socks5IncomingLayer", "TcpLayer", "AutoLayer", "RootContext", "ReverseProxy", "UpstreamProxy"]
|
||||
|
@ -10,11 +10,11 @@ class AutoLayer(Layer):
|
||||
return
|
||||
# TLS ClientHello magic, see http://www.moserware.com/2009/06/first-few-milliseconds-of-https.html#client-hello
|
||||
if d[0] == "\x16":
|
||||
layer = SslLayer(self, True, True)
|
||||
layer = TlsLayer(self, True, True)
|
||||
else:
|
||||
layer = TcpLayer(self)
|
||||
for m in layer():
|
||||
yield m
|
||||
|
||||
from .rawtcp import TcpLayer
|
||||
from .ssl import SslLayer
|
||||
from .tls import TlsLayer
|
||||
|
@ -35,9 +35,10 @@ from __future__ import (absolute_import, print_function, division)
|
||||
import Queue
|
||||
import threading
|
||||
from netlib import tcp
|
||||
from ..proxy import ProxyError2, Log
|
||||
from ..proxy import Log
|
||||
from ..proxy.connection import ServerConnection
|
||||
from .messages import Connect, Reconnect, ChangeServer
|
||||
from ..exceptions import ProtocolException
|
||||
|
||||
|
||||
class RootContext(object):
|
||||
@ -51,6 +52,9 @@ class RootContext(object):
|
||||
self.channel = channel # provides .ask() method to communicate with FlowMaster
|
||||
self.config = config # Proxy Configuration
|
||||
|
||||
def next_layer(self):
|
||||
print(type(self))
|
||||
|
||||
|
||||
class _LayerCodeCompletion(object):
|
||||
"""
|
||||
@ -149,7 +153,7 @@ class ServerConnectionMixin(object):
|
||||
try:
|
||||
self.server_conn.connect()
|
||||
except tcp.NetLibError as e:
|
||||
raise ProxyError2("Server connection to '%s' failed: %s" % (self.server_address, e), e)
|
||||
raise ProtocolException("Server connection to '%s' failed: %s" % (self.server_address, e), e)
|
||||
|
||||
|
||||
def yield_from_callback(fun):
|
||||
@ -197,7 +201,7 @@ def yield_from_callback(fun):
|
||||
break
|
||||
elif isinstance(msg, Exception):
|
||||
# TODO: Include func name?
|
||||
raise ProxyError2("Error in %s: %s" % (fun.__name__, repr(msg)), msg)
|
||||
raise ProtocolException("Error in %s: %s" % (fun.__name__, repr(msg)), msg)
|
||||
else:
|
||||
yield msg
|
||||
yield_queue.put(None)
|
||||
|
@ -32,9 +32,9 @@ class ChangeServer(_Message):
|
||||
Change the upstream server.
|
||||
"""
|
||||
|
||||
def __init__(self, address, server_ssl, sni, depth=1):
|
||||
def __init__(self, address, server_tls, sni, depth=1):
|
||||
self.address = address
|
||||
self.server_ssl = server_ssl
|
||||
self.server_tls = server_tls
|
||||
self.sni = sni
|
||||
|
||||
# upstream proxy scenario: you may want to change either the final target or the upstream proxy.
|
||||
|
@ -1,4 +1,6 @@
|
||||
from __future__ import (absolute_import, print_function, division)
|
||||
import OpenSSL
|
||||
from ..exceptions import ProtocolException
|
||||
from ..protocol.tcp import TCPHandler
|
||||
from .layer import Layer
|
||||
from .messages import Connect
|
||||
@ -8,7 +10,11 @@ class TcpLayer(Layer):
|
||||
def __call__(self):
|
||||
yield Connect()
|
||||
tcp_handler = TCPHandler(self)
|
||||
tcp_handler.handle_messages()
|
||||
try:
|
||||
tcp_handler.handle_messages()
|
||||
except OpenSSL.SSL.Error as e:
|
||||
raise ProtocolException("SSL error: %s" % repr(e), e)
|
||||
|
||||
|
||||
def establish_server_connection(self):
|
||||
pass
|
||||
|
@ -1,19 +1,19 @@
|
||||
from __future__ import (absolute_import, print_function, division)
|
||||
|
||||
from .layer import Layer, ServerConnectionMixin
|
||||
from .ssl import SslLayer
|
||||
from .tls import TlsLayer
|
||||
|
||||
|
||||
class ReverseProxy(Layer, ServerConnectionMixin):
|
||||
|
||||
def __init__(self, ctx, server_address, client_ssl, server_ssl):
|
||||
def __init__(self, ctx, server_address, client_tls, server_tls):
|
||||
super(ReverseProxy, self).__init__(ctx)
|
||||
self.server_address = server_address
|
||||
self.client_ssl = client_ssl
|
||||
self.server_ssl = server_ssl
|
||||
self._client_tls = client_tls
|
||||
self._server_tls = server_tls
|
||||
|
||||
def __call__(self):
|
||||
layer = SslLayer(self, self.client_ssl, self.server_ssl)
|
||||
layer = TlsLayer(self, self._client_tls, self._server_tls)
|
||||
for message in layer():
|
||||
if not self._handle_server_message(message):
|
||||
yield message
|
||||
|
@ -1,10 +1,10 @@
|
||||
from __future__ import (absolute_import, print_function, division)
|
||||
|
||||
from ..proxy import ProxyError, Socks5ProxyMode, ProxyError2
|
||||
from ..exceptions import ProtocolException
|
||||
from ..proxy import ProxyError, Socks5ProxyMode
|
||||
from .layer import Layer, ServerConnectionMixin
|
||||
from .auto import AutoLayer
|
||||
|
||||
|
||||
class Socks5IncomingLayer(Layer, ServerConnectionMixin):
|
||||
def __call__(self):
|
||||
try:
|
||||
@ -12,7 +12,7 @@ class Socks5IncomingLayer(Layer, ServerConnectionMixin):
|
||||
address = s5mode.get_upstream_server(self.client_conn)[2:]
|
||||
except ProxyError as e:
|
||||
# TODO: Unmonkeypatch
|
||||
raise ProxyError2(str(e), e)
|
||||
raise ProtocolException(str(e), e)
|
||||
|
||||
self.server_address = address
|
||||
|
||||
|
@ -2,17 +2,17 @@ from __future__ import (absolute_import, print_function, division)
|
||||
import traceback
|
||||
from netlib import tcp
|
||||
|
||||
from ..proxy import ProxyError2
|
||||
from ..exceptions import ProtocolException
|
||||
from .layer import Layer, yield_from_callback
|
||||
from .messages import Connect, Reconnect, ChangeServer
|
||||
from .auto import AutoLayer
|
||||
|
||||
|
||||
class SslLayer(Layer):
|
||||
def __init__(self, ctx, client_ssl, server_ssl):
|
||||
super(SslLayer, self).__init__(ctx)
|
||||
self._client_ssl = client_ssl
|
||||
self._server_ssl = server_ssl
|
||||
class TlsLayer(Layer):
|
||||
def __init__(self, ctx, client_tls, server_tls):
|
||||
super(TlsLayer, self).__init__(ctx)
|
||||
self._client_tls = client_tls
|
||||
self._server_tls = server_tls
|
||||
self._connected = False
|
||||
self.client_sni = None
|
||||
self._sni_from_server_change = None
|
||||
@ -41,33 +41,34 @@ class SslLayer(Layer):
|
||||
https://www.openssl.org/docs/ssl/SSL_CTX_set_cert_cb.html
|
||||
- The original mitmproxy issue is https://github.com/mitmproxy/mitmproxy/issues/427
|
||||
"""
|
||||
client_ssl_requires_server_cert = (
|
||||
self._client_ssl and self._server_ssl and not self.config.no_upstream_cert
|
||||
client_tls_requires_server_cert = (
|
||||
self._client_tls and self._server_tls and not self.config.no_upstream_cert
|
||||
)
|
||||
lazy_server_ssl = (
|
||||
self._server_ssl and not client_ssl_requires_server_cert
|
||||
lazy_server_tls = (
|
||||
self._server_tls and not client_tls_requires_server_cert
|
||||
)
|
||||
|
||||
if client_ssl_requires_server_cert:
|
||||
for m in self._establish_ssl_with_client_and_server():
|
||||
if client_tls_requires_server_cert:
|
||||
for m in self._establish_tls_with_client_and_server():
|
||||
yield m
|
||||
elif self.client_ssl:
|
||||
for m in self._establish_ssl_with_client():
|
||||
elif self._client_tls:
|
||||
for m in self._establish_tls_with_client():
|
||||
yield m
|
||||
|
||||
self.next_layer()
|
||||
layer = AutoLayer(self)
|
||||
for message in layer():
|
||||
if message != Connect or not self._connected:
|
||||
yield message
|
||||
if message == Connect:
|
||||
if lazy_server_ssl:
|
||||
self._establish_ssl_with_server()
|
||||
if lazy_server_tls:
|
||||
self._establish_tls_with_server()
|
||||
if message == ChangeServer and message.depth == 1:
|
||||
self.server_ssl = message.server_ssl
|
||||
self._server_tls = message.server_tls
|
||||
self._sni_from_server_change = message.sni
|
||||
if message == Reconnect or message == ChangeServer:
|
||||
if self.server_ssl:
|
||||
self._establish_ssl_with_server()
|
||||
if self._server_tls:
|
||||
self._establish_tls_with_server()
|
||||
|
||||
@property
|
||||
def sni_for_upstream_connection(self):
|
||||
@ -76,7 +77,7 @@ class SslLayer(Layer):
|
||||
else:
|
||||
return self._sni_from_server_change or self.client_sni
|
||||
|
||||
def _establish_ssl_with_client_and_server(self):
|
||||
def _establish_tls_with_client_and_server(self):
|
||||
"""
|
||||
This function deals with the problem that the server may require a SNI value from the client.
|
||||
"""
|
||||
@ -86,14 +87,14 @@ class SslLayer(Layer):
|
||||
self._connected = True
|
||||
server_err = None
|
||||
try:
|
||||
self._establish_ssl_with_server()
|
||||
except ProxyError2 as e:
|
||||
self._establish_tls_with_server()
|
||||
except ProtocolException as e:
|
||||
server_err = e
|
||||
|
||||
for message in self._establish_ssl_with_client():
|
||||
for message in self._establish_tls_with_client():
|
||||
if message == Reconnect:
|
||||
yield message
|
||||
self._establish_ssl_with_server()
|
||||
self._establish_tls_with_server()
|
||||
else:
|
||||
raise RuntimeError("Unexpected Message: %s" % message)
|
||||
|
||||
@ -102,7 +103,7 @@ class SslLayer(Layer):
|
||||
|
||||
def handle_sni(self, connection):
|
||||
"""
|
||||
This callback gets called during the SSL handshake with the client.
|
||||
This callback gets called during the TLS handshake with the client.
|
||||
The client has just sent the Sever Name Indication (SNI).
|
||||
"""
|
||||
try:
|
||||
@ -115,7 +116,7 @@ class SslLayer(Layer):
|
||||
|
||||
if old_upstream_sni != self.sni_for_upstream_connection:
|
||||
# Perform reconnect
|
||||
if self.server_ssl:
|
||||
if self._server_tls:
|
||||
self.yield_from_callback(Reconnect())
|
||||
|
||||
if self.client_sni:
|
||||
@ -136,8 +137,8 @@ class SslLayer(Layer):
|
||||
self.log("Error in handle_sni:\r\n" + traceback.format_exc(), "error")
|
||||
|
||||
@yield_from_callback
|
||||
def _establish_ssl_with_client(self):
|
||||
self.log("Establish SSL with client", "debug")
|
||||
def _establish_tls_with_client(self):
|
||||
self.log("Establish TLS with client", "debug")
|
||||
cert, key, chain_file = self.find_cert()
|
||||
try:
|
||||
self.client_conn.convert_to_ssl(
|
||||
@ -150,10 +151,10 @@ class SslLayer(Layer):
|
||||
chain_file=chain_file
|
||||
)
|
||||
except tcp.NetLibError as e:
|
||||
raise ProxyError2(repr(e), e)
|
||||
raise ProtocolException(repr(e), e)
|
||||
|
||||
def _establish_ssl_with_server(self):
|
||||
self.log("Establish SSL with server", "debug")
|
||||
def _establish_tls_with_server(self):
|
||||
self.log("Establish TLS with server", "debug")
|
||||
try:
|
||||
self.server_conn.establish_ssl(
|
||||
self.config.clientcerts,
|
||||
@ -165,30 +166,29 @@ class SslLayer(Layer):
|
||||
ca_pemfile=self.config.openssl_trusted_ca_server,
|
||||
cipher_list=self.config.ciphers_server,
|
||||
)
|
||||
ssl_cert_err = self.server_conn.ssl_verification_error
|
||||
if ssl_cert_err is not None:
|
||||
tls_cert_err = self.server_conn.ssl_verification_error
|
||||
if tls_cert_err is not None:
|
||||
self.log(
|
||||
"SSL verification failed for upstream server at depth %s with error: %s" %
|
||||
(ssl_cert_err['depth'], ssl_cert_err['errno']),
|
||||
"TLS verification failed for upstream server at depth %s with error: %s" %
|
||||
(tls_cert_err['depth'], tls_cert_err['errno']),
|
||||
"error")
|
||||
self.log("Ignoring server verification error, continuing with connection", "error")
|
||||
except tcp.NetLibInvalidCertificateError as e:
|
||||
ssl_cert_err = self.server_conn.ssl_verification_error
|
||||
tls_cert_err = self.server_conn.ssl_verification_error
|
||||
self.log(
|
||||
"SSL verification failed for upstream server at depth %s with error: %s" %
|
||||
(ssl_cert_err['depth'], ssl_cert_err['errno']),
|
||||
"TLS verification failed for upstream server at depth %s with error: %s" %
|
||||
(tls_cert_err['depth'], tls_cert_err['errno']),
|
||||
"error")
|
||||
self.log("Aborting connection attempt", "error")
|
||||
raise ProxyError2(repr(e), e)
|
||||
raise ProtocolException(repr(e), e)
|
||||
except tcp.NetLibError as e:
|
||||
raise ProxyError2(repr(e), e)
|
||||
raise ProtocolException(repr(e), e)
|
||||
|
||||
def find_cert(self):
|
||||
host = self.server_conn.address.host
|
||||
# TODO: Better use an OrderedSet here
|
||||
sans = set()
|
||||
# Incorporate upstream certificate
|
||||
if self.server_conn.ssl_established and (not self.config.no_upstream_cert):
|
||||
if self.server_conn.tls_established and (not self.config.no_upstream_cert):
|
||||
upstream_cert = self.server_conn.cert
|
||||
sans.update(upstream_cert.altnames)
|
||||
if upstream_cert.cn:
|
24
libmproxy/protocol2/transparent_proxy.py
Normal file
24
libmproxy/protocol2/transparent_proxy.py
Normal file
@ -0,0 +1,24 @@
|
||||
from __future__ import (absolute_import, print_function, division)
|
||||
|
||||
from ..exceptions import ProtocolException
|
||||
from .. import platform
|
||||
from .layer import Layer, ServerConnectionMixin
|
||||
from .auto import AutoLayer
|
||||
|
||||
|
||||
class TransparentProxy(Layer, ServerConnectionMixin):
|
||||
|
||||
def __init__(self, ctx):
|
||||
super(TransparentProxy, self).__init__(ctx)
|
||||
self.resolver = platform.resolver()
|
||||
|
||||
def __call__(self):
|
||||
try:
|
||||
self.server_address = self.resolver.original_addr(self.client_conn.connection)
|
||||
except Exception as e:
|
||||
raise ProtocolException("Transparent mode failure: %s" % repr(e), e)
|
||||
|
||||
layer = AutoLayer(self)
|
||||
for message in layer():
|
||||
if not self._handle_server_message(message):
|
||||
yield message
|
18
libmproxy/protocol2/upstream_proxy.py
Normal file
18
libmproxy/protocol2/upstream_proxy.py
Normal file
@ -0,0 +1,18 @@
|
||||
from __future__ import (absolute_import, print_function, division)
|
||||
|
||||
from .layer import Layer, ServerConnectionMixin
|
||||
#from .http import HttpLayer
|
||||
|
||||
|
||||
class UpstreamProxy(Layer, ServerConnectionMixin):
|
||||
|
||||
def __init__(self, ctx, server_address):
|
||||
super(UpstreamProxy, self).__init__(ctx)
|
||||
self.server_address = server_address
|
||||
|
||||
def __call__(self):
|
||||
#layer = HttpLayer(self)
|
||||
layer = None
|
||||
for message in layer():
|
||||
if not self._handle_server_message(message):
|
||||
yield message
|
@ -32,6 +32,10 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
|
||||
port=self.address.port
|
||||
)
|
||||
|
||||
@property
|
||||
def tls_established(self):
|
||||
return self.ssl_established
|
||||
|
||||
_stateobject_attributes = dict(
|
||||
ssl_established=bool,
|
||||
timestamp_start=float,
|
||||
@ -112,6 +116,10 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
|
||||
port=self.address.port
|
||||
)
|
||||
|
||||
@property
|
||||
def tls_established(self):
|
||||
return self.ssl_established
|
||||
|
||||
_stateobject_attributes = dict(
|
||||
state=list,
|
||||
timestamp_start=float,
|
||||
|
@ -2,12 +2,6 @@ from __future__ import absolute_import
|
||||
from netlib import socks, tcp
|
||||
|
||||
|
||||
class ProxyError2(Exception):
|
||||
def __init__(self, message, cause=None):
|
||||
super(ProxyError2, self).__init__(message)
|
||||
self.cause = cause
|
||||
|
||||
|
||||
class ProxyError(Exception):
|
||||
def __init__(self, code, message, headers=None):
|
||||
super(ProxyError, self).__init__(message)
|
||||
|
@ -7,7 +7,7 @@ from netlib import tcp
|
||||
|
||||
from ..protocol.handle import protocol_handler
|
||||
from .. import protocol2
|
||||
from .primitives import ProxyServerError, Log, ProxyError, ProxyError2
|
||||
from .primitives import ProxyServerError, Log, ProxyError
|
||||
from .connection import ClientConnection, ServerConnection
|
||||
|
||||
|
||||
@ -79,12 +79,12 @@ class ConnectionHandler2:
|
||||
self.config,
|
||||
self.channel
|
||||
)
|
||||
root_layer = protocol2.ReverseProxy(root_context, ("localhost", 5000), True, True)
|
||||
root_layer = protocol2.Socks5IncomingLayer(root_context)
|
||||
|
||||
try:
|
||||
for message in root_layer():
|
||||
print("Root layer receveived: %s" % message)
|
||||
except ProxyError2 as e:
|
||||
except protocol2.ProtocolException as e:
|
||||
self.log(e, "info")
|
||||
except Exception:
|
||||
self.log(traceback.format_exc(), "error")
|
||||
|
Loading…
Reference in New Issue
Block a user