From cbc0b4be4cf3e6fc9f3559bc9c7129dcadffdd21 Mon Sep 17 00:00:00 2001 From: Rob Glew Date: Mon, 31 Oct 2016 11:47:35 -0500 Subject: [PATCH 1/2] Version 0.2.14 --- README.md | 2 + pappyproxy/__init__.py | 2 +- pappyproxy/http.py | 7 +- pappyproxy/macros.py | 67 +++ pappyproxy/proxy.py | 64 ++- pappyproxy/tests/test_http_proxy.py | 421 +++++++++++++++ pappyproxy/tests/test_proxy.py | 770 +++++++++++++--------------- pappyproxy/tests/testutil.py | 78 ++- 8 files changed, 980 insertions(+), 431 deletions(-) create mode 100644 pappyproxy/tests/test_http_proxy.py diff --git a/README.md b/README.md index eb8b09f..a83a6a1 100644 --- a/README.md +++ b/README.md @@ -1455,6 +1455,8 @@ Changelog --------- The boring part of the readme +* 0.2.14 + * Critical bugfixes * 0.2.13 * Refactor proxy core * WEBSOCKETS diff --git a/pappyproxy/__init__.py b/pappyproxy/__init__.py index a53c568..10d10d2 100644 --- a/pappyproxy/__init__.py +++ b/pappyproxy/__init__.py @@ -1 +1 @@ -__version__ = '0.2.13' +__version__ = '0.2.14' diff --git a/pappyproxy/http.py b/pappyproxy/http.py index a7c46b2..b6c7f75 100644 --- a/pappyproxy/http.py +++ b/pappyproxy/http.py @@ -3387,7 +3387,12 @@ class HTTPProtocolProxy(ProtocolProxy): self.conn_is_ssl != use_ssl: self.log("Closing connection to old server") self.close_server_connection() - self.connect(host, port, use_ssl, use_socks=use_socks) + # we don't use SSL because maybe_use_ssl takes care of setting + # it up if we end up using it + if self.conn_is_maybe_ssl: + self.connect(host, port, False, use_socks=use_socks) + else: + self.connect(host, port, use_ssl, use_socks=use_socks) def server_connection_lost(self, reason): self.log("Connection to server lost: %s" % str(reason)) diff --git a/pappyproxy/macros.py b/pappyproxy/macros.py index 2ab6bc3..40ea2fa 100644 --- a/pappyproxy/macros.py +++ b/pappyproxy/macros.py @@ -436,6 +436,73 @@ class MacroTemplate(object): def template_argstring(cls, template): return cls._template_data[template][2] +## Other functions + + @defer.inlineCallbacks + def async_mangle_ws(self, request, message): + if hasattr(self.source, 'async_mangle_ws'): + mangled_ws = yield self.source.async_mangle_ws(request, message) + defer.returnValue(mangled_ws) + defer.returnValue(message) + +class MacroTemplate(object): + _template_data = { + 'macro': ('macro.py.template', + 'Generic macro template', + '[reqids]', + 'macro_{fname}.py', + gen_template_args_macro), + + 'intmacro': ('intmacro.py.template', + 'Generic intercepting macro template', + '', + 'int_{fname}.py', + gen_template_generator_noargs('intmacro')), + + 'modheader': ('macro_header.py.template', + 'Modify a header in the request and the response if it exists.', + '', + 'int_{fname}.py', + gen_template_generator_noargs('modheader')), + + 'resubmit': ('macro_resubmit.py.template', + 'Resubmit all in-context requests', + '', + 'macro_{fname}.py', + gen_template_generator_noargs('resubmit')), + } + + @classmethod + def fill_template(cls, template, subs): + loader = FileSystemLoader(session.config.pappy_dir+'/templates') + env = Environment(loader=loader) + template = env.get_template(cls._template_data[template][0]) + return template.render(zip=zip, **subs) + + @classmethod + @defer.inlineCallbacks + def fill_template_args(cls, template, args=[]): + ret = cls._template_data[template][4](args) + if isinstance(ret, defer.Deferred): + ret = yield ret + defer.returnValue(ret) + + @classmethod + def template_filename(cls, template, fname): + return cls._template_data[template][3].format(fname=fname) + + @classmethod + def template_list(cls): + return [k for k, v in cls._template_data.iteritems()] + + @classmethod + def template_description(cls, template): + return cls._template_data[template][1] + + @classmethod + def template_argstring(cls, template): + return cls._template_data[template][2] + ## Other functions def load_macros(loc): diff --git a/pappyproxy/proxy.py b/pappyproxy/proxy.py index 73c784f..8f233c4 100644 --- a/pappyproxy/proxy.py +++ b/pappyproxy/proxy.py @@ -307,17 +307,20 @@ class ProtocolProxy(object): self.client_connected = False self.client_buffer = '' self.client_start_tls = False + self.client_tls_host = '' self.client_protocol = None + self.client_do_maybe_tls = False self.server_transport = None self.server_connected = False self.server_buffer = '' self.server_start_tls = False + self.conn_is_maybe_ssl = False self.server_protocol = None self.conn_host = None self.conn_port = None - self.conn_is_ssl = None + self.conn_is_ssl = False self.connection_id = get_next_connection_id() def log(self, message, symbol='*', verbosity_level=3): @@ -330,23 +333,37 @@ class ProtocolProxy(object): from pappyproxy.pappy import session self.connecting = True - self.log("Connecting to %s:%d ssl=%s" % (host, port, use_ssl)) + + connect_with_ssl = use_ssl + if self.conn_is_maybe_ssl: + connect_with_ssl = False + + self.log("Connecting to %s:%d ssl=%s (maybe_ssl=%s)" % (host, port, connect_with_ssl, self.conn_is_maybe_ssl)) factory = PassthroughProtocolFactory(self.server_data_received, self.server_connection_made, self.server_connection_lost) self.conn_host = host self.conn_port = port - self.conn_is_ssl = use_ssl + if self.conn_is_maybe_ssl: + self.conn_is_ssl = False + else: + self.conn_is_ssl = use_ssl if use_socks: socks_config = session.config.socks_proxy else: socks_config = None - make_proxied_connection(factory, host, port, use_ssl, socks_config=socks_config, + make_proxied_connection(factory, host, port, connect_with_ssl, socks_config=socks_config, log_id=self.connection_id, http_error_transport=self.client_transport) ## Client interactions + def client_data_received(self, data): + """ + Implemented by child class + """ + pass + def send_client_data(self, data): self.log("pc< %s" % short_data(data)) if self.client_connected: @@ -355,12 +372,17 @@ class ProtocolProxy(object): self.client_buffer += data def client_connection_made(self, protocol): + self.log("Client connection made") + self.client_protocol = protocol self.client_transport = self.client_protocol.transport self.client_connected = True self.connecting = False if self.client_start_tls: - self.start_client_tls() + if self.client_do_maybe_tls: + self.start_client_maybe_tls(self.client_tls_host) + else: + self.start_client_tls(self.client_tls_host) if self.client_buffer != '': self.client_transport.write(self.client_buffer) self.client_buffer = '' @@ -377,16 +399,25 @@ class ProtocolProxy(object): def start_server_tls(self): if self.server_connected: self.log("Starting TLS on server transport") + self.conn_is_ssl = True self.server_transport.startTLS(ssl.ClientContextFactory()) else: self.log("Server not yet connected, will start TLS on connect") - self.start_server_tls = True + self.server_start_tls = True def start_client_maybe_tls(self, cert_host): ctx = generate_tls_context(cert_host) - start_maybe_tls(self.client_transport, - tls_host=cert_host, - start_tls_callback=self.start_server_tls) + if self.client_connected: + self.log("Starting maybe TLS on client transport") + self.conn_is_maybe_ssl = True + start_maybe_tls(self.client_transport, + tls_host=cert_host, + start_tls_callback=self.start_server_tls) + else: + self.log("Client not yet connected, will start maybe TLS on connect") + self.client_do_maybe_tls = True + self.client_start_tls = True + self.client_tls_host = cert_host def start_client_tls(self, cert_host): if self.client_connected: @@ -395,10 +426,17 @@ class ProtocolProxy(object): self.client_transport.startTLS(ctx) else: self.log("Client not yet connected, will start TLS on connect") - self.start_client_tls = True + self.client_start_tls = True + self.client_tls_host = cert_host ## Server interactions + def server_data_received(self, data): + """ + Implemented by child class + """ + pass + def send_server_data(self, data): if self.server_connected: self.log("ps> %s" % short_data(data)) @@ -412,7 +450,7 @@ class ProtocolProxy(object): """ self.server_protocol must be set before calling """ - self.log("Connection made") + self.log("Server connection made") self.server_protocol = protocol self.server_transport = protocol.transport self.server_connected = True @@ -441,6 +479,7 @@ class ProtocolProxy(object): self.server_transport = None self.server_connected = False self.server_buffer = '' + self.server_start_tls = False self.server_protocol = None def close_client_connection(self): @@ -450,7 +489,10 @@ class ProtocolProxy(object): self.client_transport = None self.client_connected = False self.client_buffer = '' + self.client_start_tls = False + self.client_tls_host = '' self.client_protocol = None + self.client_do_maybe_tls = False def close_connections(self): self.close_server_connection() diff --git a/pappyproxy/tests/test_http_proxy.py b/pappyproxy/tests/test_http_proxy.py new file mode 100644 index 0000000..43af86a --- /dev/null +++ b/pappyproxy/tests/test_http_proxy.py @@ -0,0 +1,421 @@ +import pytest +import mock +import random +import datetime +import pappyproxy +import base64 +import collections + +from pappyproxy import http +from pappyproxy.proxy import ProxyClientFactory, ProxyServerFactory, UpstreamHTTPProxyClient +from pappyproxy.http import Request, Response +from pappyproxy.macros import InterceptMacro +from testutil import mock_deferred, func_deleted, TLSStringTransport, freeze, mock_int_macro, no_tcp +from twisted.internet import defer + +class InterceptMacroTest(InterceptMacro): + + def __init__(self, new_req=None, new_rsp=None): + InterceptMacro.__init__(self) + + self.new_req = None + self.new_rsp = None + if new_req: + self.intercept_requests = True + self.new_req = new_req + if new_rsp: + self.intercept_responses = True + self.new_rsp = new_rsp + + def mangle_request(self, request): + if self.intercept_requests: + return self.new_req + else: + return request + + def mangle_response(self, request): + if self.intercept_responses: + return self.new_rsp + else: + return request.response + +class TestProxyConnection(object): + + @property + def client_protocol(self): + if 'protocol' not in self.conn_info: + raise Exception('Connection to server not made. Cannot write data as server.') + return self.conn_info['protocol'] + + @property + def client_factory(self): + if 'protocol' not in self.conn_info: + raise Exception('Connection to server not made. Cannot write data as server.') + return self.conn_info['factory'] + + def setUp(self, mocker, int_macros={}, socks_config=None, http_config=None, in_scope=True): + self.mocker = mocker + self.conn_info = {} + + # Mock config + self.mock_config = pappyproxy.config.PappyConfig() + self.mock_config.socks_proxy = socks_config + self.mock_config.http_proxy = http_config + self.mock_session = pappyproxy.pappy.PappySession(self.mock_config) + mocker.patch.object(pappyproxy.pappy, 'session', new=self.mock_session) + mocker.patch("pappyproxy.proxy.load_certs_from_dir", new=mock_generate_cert) + + # Listening server + ## self.server_factory = ProxyServerFactory() + ## self.server_factory.save_all = True + ## self.server_factory.intercepting_macros = int_macros + ## self.server_protocol = self.server_factory.buildProtocol(('127.0.0.1', 0)) + ## self.server_transport = TLSStringTransport() + ## self.server_protocol.makeConnection(self.server_transport) + + # Other mocks + self.req_save = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) + self.submit_request = mocker.patch('pappyproxy.http.Request.submit_request', + new=self.gen_mock_submit_request()) + self.make_proxied_connection = mocker.patch('pappyproxy.proxy.make_proxied_connection') + self.in_scope = mocker.patch('pappyproxy.context.in_scope').return_value = in_scope + + def gen_mock_submit_request(self): + orig = Request.submit_request + def f(request, save_request=False, intercepting_macros={}, stream_transport=None): + return orig(request, save_request=save_request, + intercepting_macros=intercepting_macros, + stream_transport=stream_transport, + _factory_string_transport=True, + _conn_info=self.conn_info) + return f + + def perform_connect_request(self): + self.write_as_browser('CONNECT https://www.AAAA.BBBB:443 HTTP/1.1\r\n\r\n') + assert self.read_as_browser() == 'HTTP/1.1 200 Connection established\r\n\r\n' + + def write_as_browser(self, data): + self.server_protocol.dataReceived(data) + + def read_as_browser(self): + s = self.server_protocol.transport.value() + self.server_protocol.transport.clear() + return s + + def write_as_server(self, data): + self.client_protocol.dataReceived(data) + + def read_as_server(self): + s = self.client_protocol.transport.value() + self.client_protocol.transport.clear() + return s + + +def mock_req_async_save(req): + req.reqid = str(random.randint(1,1000000)) + return mock_deferred() + +def mock_mangle_response_side_effect(new_rsp): + def f(request, mangle_macros): + request.response = new_rsp + return mock_deferred(True) + return f + +def mock_generate_cert(cert_dir): + private_key = ('-----BEGIN PRIVATE KEY-----\n' + 'MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDAoClrYUEB7lM0\n' + 'zQaKkXZVG2d1Bu9hV8urpx0gNXMbyZ2m3xb+sKZju/FHPuWenA4KaN5gRUT+oLfv\n' + 'tnF6Ia0jpRNWnX0Fyn/irdg1BWGJn7k7mJ2D0NXZQczn2+xxY05599NfGWqNKCYy\n' + 'jhSwPsUK+sGJqi7aSDdlS97ZTjrQVTTFsC0+kSu4lS5fsWXxqrKLa6Ao8W7abVRO\n' + 'JHazh/cxM4UKpgWU+E6yD4o4ZgHY+SMTVUh/IOM8DuOVyLEWtx4oLNiLMlpWT3qy\n' + '4IMpOF6VuU6JF2HGV13SoJfhsLXsPRbLVTAnZvJcZwtgDm6NfKapU8W8olkDV1Bf\n' + 'YQEMSNX7AgMBAAECggEBAII0wUrAdrzjaIMsg9tu8FofKBPHGFDok9f4Iov/FUwX\n' + 'QOXnrxeCOTb5d+L89SH9ws/ui0LwD+8+nJcA8DvqP6r0jtnhov0jIMcNVDSi6oeo\n' + '3AEY7ICJzcQJ4oRn+K+8vPNdPhfuikPYe9l4iSuJgpAlaGWyD/GlFyz12DFz2/Wu\n' + 'NIcqR1ucvezRHn3eGMtvDv2WGaN4ifUc30k8XgSUesmwSI6beb5+hxq7wXfsurnP\n' + 'EUrPY9ts3lfiAgxzTKOuj1VR5hn7cJyLN8jF0mZs4D6eSSHorIddhmaNiCq5ZbMd\n' + 'QdlDiPvnXHT41OoXOb7tDEt7SGoiRh2noCZ1aZiSziECgYEA+tuPPLYWU6JRB6EW\n' + 'PhbcXQbh3vML7eT1q7DOz0jYCojgT2+k7EWSI8T830oQyjbpe3Z86XEgH7UBjUgq\n' + '27nJ4E6dQDYGbYCKEklOoCGLE7A60i1feIz8otOQRrbQ4jcpibEgscA6gzHmunYf\n' + 'De5euUgYW+Rq2Vmr6/NzUaUgui8CgYEAxJMDwPOGgiLM1cczlaSIU9Obz+cVnwWn\n' + 'nsdKYMto2V3yKLydDfjsgOgzxHOxxy+5L645TPxK6CkiISuhJ93kAFFtx+1sCBCT\n' + 'tVzY5robVAekxA9tlPIxtsn3+/axx3n6HnV0oA/XtxkuOS5JImgEdXqFwJZkerGE\n' + 'waftIU2FCfUCgYEArl8+ErJzlJEIiCgWIPSdGuD00pfZW/TCPCT7rKRy3+fDHBR7\n' + '7Gxzp/9+0utV/mnrJBH5w/8JmGCmgoF+oRtk01FyBzdGgolN8GYajD6kwPvH917o\n' + 'tRAzcC9lY3IigoxbiEWid0wqoBVoz4XaEkH2gA44OG/vQcQOOEYSi9cfh6sCgYBg\n' + 'KLaOXdJvuIxRCzgNvMW/k+VFh3pJJx//COg2f2qT4mQCT3nYiutOh8hDEoFluc+y\n' + 'Jlz7bvNJrE14wnn8IYxWJ383bMoLC+jlsDyeaW3S5kZQbmehk/SDwTrg86W1udKD\n' + 'sdtSLU3N0LCO4jh+bzm3Ki9hrXALoOkbPoU+ZEhvPQKBgQDf79XQ3RNxZSk+eFyq\n' + 'qD8ytVqxEoD+smPDflXXseVH6o+pNWrF8+A0KqmO8c+8KVzWj/OfULO6UbKd3E+x\n' + '4JGkWu9yF1lEgtHgibF2ER8zCSIL4ikOEasPCkrKj5SrS4Q+j4u5ha76dIc2CVu1\n' + 'hkX2PQ1xU4ocu06k373sf73A4Q==\n' + '-----END PRIVATE KEY-----') + ca_key = ('-----BEGIN CERTIFICATE-----\n' + 'MIIDjzCCAncCFQCjC8r+I4xa7JoGUJYGOTcqDROA0DANBgkqhkiG9w0BAQsFADBg\n' + 'MQswCQYDVQQGEwJVUzERMA8GA1UECBMITWljaGlnYW4xEjAQBgNVBAcTCUFubiBB\n' + 'cmJvcjEUMBIGA1UEChMLUGFwcHkgUHJveHkxFDASBgNVBAMTC1BhcHB5IFByb3h5\n' + 'MB4XDTE1MTEyMDIxMTEzOVoXDTI1MTExNzIxMTEzOVowYDELMAkGA1UEBhMCVVMx\n' + 'ETAPBgNVBAgTCE1pY2hpZ2FuMRIwEAYDVQQHEwlBbm4gQXJib3IxFDASBgNVBAoT\n' + 'C1BhcHB5IFByb3h5MRQwEgYDVQQDEwtQYXBweSBQcm94eTCCASIwDQYJKoZIhvcN\n' + 'AQEBBQADggEPADCCAQoCggEBAMCgKWthQQHuUzTNBoqRdlUbZ3UG72FXy6unHSA1\n' + 'cxvJnabfFv6wpmO78Uc+5Z6cDgpo3mBFRP6gt++2cXohrSOlE1adfQXKf+Kt2DUF\n' + 'YYmfuTuYnYPQ1dlBzOfb7HFjTnn3018Zao0oJjKOFLA+xQr6wYmqLtpIN2VL3tlO\n' + 'OtBVNMWwLT6RK7iVLl+xZfGqsotroCjxbtptVE4kdrOH9zEzhQqmBZT4TrIPijhm\n' + 'Adj5IxNVSH8g4zwO45XIsRa3Higs2IsyWlZPerLggyk4XpW5TokXYcZXXdKgl+Gw\n' + 'tew9FstVMCdm8lxnC2AObo18pqlTxbyiWQNXUF9hAQxI1fsCAwEAAaNFMEMwEgYD\n' + 'VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNo5o+5e\n' + 'a0sNMlW/75VgGJCv2AcJMA0GCSqGSIb3DQEBCwUAA4IBAQBdJDhxbmoEe27bD8me\n' + 'YTcLGjs/StKkSil7rLbX+tBCwtkm5UEEejBuAhKk2FuAXW8yR1FqKJSZwVCAocBT\n' + 'Bo/+97Ee+h7ywrRFhATEr9D/TbbHKOjCjDzOMl9yLZa2DKErZjbI30ZD6NafWS/X\n' + 'hx5X1cGohHcVVzT4jIgUEU70vvYfNn8CTZm4oJ7qqRe/uQPUYy0rwvbd60oprtGg\n' + 'jNv1H5R4ODHUMBXAI9H7ft9cWrd0fBQjxhoj8pvgJXEZ52flXSqQc7qHLg1wO/zC\n' + 'RUgpTcNAb2qCssBKbj+c1vKEPRUJfw6UYb0s1462rQNc8BgZiKaNbwokFmkAnjUg\n' + 'AvnX\n' + '-----END CERTIFICATE-----') + return (ca_key, private_key) + +######## +## Tests + +def test_no_tcp(): + from twisted.internet.endpoints import SSL4ClientEndpoint, TCP4ClientEndpoint + from txsocksx.client import SOCKS5ClientEndpoint + from txsocksx.tls import TLSWrapClientEndpoint + with pytest.raises(NotImplementedError): + SSL4ClientEndpoint('aasdfasdf.sdfwerqwer') + with pytest.raises(NotImplementedError): + TCP4ClientEndpoint('aasdfasdf.sdfwerqwer') + with pytest.raises(NotImplementedError): + SOCKS5ClientEndpoint('aasdfasdf.sdfwerqwer') + with pytest.raises(NotImplementedError): + TLSWrapClientEndpoint('asdf.2341') + +def test_proxy_server_connect(mocker): + proxy = TestProxyConnection() + proxy.setUp(mocker) + proxy.write_as_browser('CONNECT https://www.AAAA.BBBB:443 HTTP/1.1\r\n\r\n') + rsp = proxy.read_as_browser() + print rsp + assert rsp == 'HTTP/1.1 200 Connection established\r\n\r\n' + +def test_proxy_server_forward_basic(mocker): + proxy = TestProxyConnection() + proxy.setUp(mocker) + req_contents = ('POST /fooo HTTP/1.1\r\n' + 'Test-Header: foo\r\n' + 'Content-Length: 4\r\n' + 'Host: www.AAAA.BBBB\r\n' + '\r\n' + 'ABCD') + rsp_contents = ('HTTP/1.1 200 OK\r\n\r\n') + proxy.write_as_browser(req_contents) + assert proxy.read_as_server() == req_contents + proxy.write_as_server(rsp_contents) + assert proxy.read_as_browser() == rsp_contents + proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 80, False, socks_config=None, use_http_proxy=True) + assert proxy.req_save.called + +def test_proxy_server_forward_basic_ssl(mocker): + proxy = TestProxyConnection() + proxy.setUp(mocker) + proxy.perform_connect_request() + req_contents = ('POST /fooo HTTP/1.1\r\n' + 'Test-Header: foo\r\n' + 'Content-Length: 4\r\n' + '\r\n' + 'ABCD') + rsp_contents = ('HTTP/1.1 200 OK\r\n\r\n') + proxy.write_as_browser(req_contents) + assert proxy.read_as_server() == req_contents + proxy.write_as_server(rsp_contents) + assert proxy.read_as_browser() == rsp_contents + assert proxy.req_save.called + proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 443, True, socks_config=None, use_http_proxy=True) + +def test_proxy_server_connect_uri(mocker): + proxy = TestProxyConnection() + proxy.setUp(mocker) + proxy.write_as_browser('CONNECT https://www.AAAA.BBBB:443 HTTP/1.1\r\n\r\n') + proxy.read_as_browser() + req_contents = ('POST /fooo HTTP/1.1\r\n' + 'Test-Header: foo\r\n' + 'Content-Length: 4\r\n' + '\r\n' + 'ABCD') + proxy.write_as_browser(req_contents) + assert proxy.client_protocol.transport.startTLS.called + assert proxy.client_factory.request.host == 'www.AAAA.BBBB' + assert proxy.client_factory.request.port == 443 + assert proxy.client_factory.request.is_ssl == True + assert proxy.read_as_server() == req_contents + assert proxy.client_protocol.transport.startTLS.called + assert proxy.req_save.called + proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 443, True, socks_config=None, use_http_proxy=True) + +def test_proxy_server_connect_uri_alt_port(mocker): + proxy = TestProxyConnection() + proxy.setUp(mocker) + proxy.write_as_browser('CONNECT https://www.AAAA.BBBB:80085 HTTP/1.1\r\n\r\n') + proxy.read_as_browser() + req_contents = ('POST /fooo HTTP/1.1\r\n' + 'Test-Header: foo\r\n' + 'Content-Length: 4\r\n' + '\r\n' + 'ABCD') + proxy.write_as_browser(req_contents) + assert proxy.client_factory.request.host == 'www.AAAA.BBBB' + assert proxy.client_factory.request.port == 80085 + assert proxy.client_factory.request.is_ssl == True + assert proxy.req_save.called + proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 80085, True, socks_config=None, use_http_proxy=True) + +def test_proxy_server_socks_basic(mocker): + proxy = TestProxyConnection() + proxy.setUp(mocker, socks_config={'host': 'www.banana.faketld', 'port': 1337}) + proxy.write_as_browser('CONNECT https://www.AAAA.BBBB:80085 HTTP/1.1\r\n\r\n') + proxy.read_as_browser() + req_contents = ('POST /fooo HTTP/1.1\r\n' + 'Test-Header: foo\r\n' + 'Content-Length: 4\r\n' + '\r\n' + 'ABCD') + proxy.write_as_browser(req_contents) + proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 80085, True, + socks_config={'host':'www.banana.faketld', 'port':1337}, + use_http_proxy=True) + +def test_proxy_server_http_basic(mocker): + proxy = TestProxyConnection() + proxy.setUp(mocker, http_config={'host': 'www.banana.faketld', 'port': 1337}) + proxy.write_as_browser('CONNECT https://www.AAAA.BBBB:80085 HTTP/1.1\r\n\r\n') + proxy.read_as_browser() + req_contents = ('POST /fooo HTTP/1.1\r\n' + 'Test-Header: foo\r\n' + 'Content-Length: 4\r\n' + '\r\n' + 'ABCD') + proxy.write_as_browser(req_contents) + assert proxy.req_save.called + proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 80085, True, + socks_config=None, + use_http_proxy=True) + +def test_proxy_server_360_noscope(mocker): + proxy = TestProxyConnection() + proxy.setUp(mocker, in_scope=False, socks_config={'host': 'www.banana.faketld', 'port': 1337}) + proxy.write_as_browser('CONNECT https://www.AAAA.BBBB:80085 HTTP/1.1\r\n\r\n') + proxy.read_as_browser() + req_contents = ('POST /fooo HTTP/1.1\r\n' + 'Test-Header: foo\r\n' + 'Content-Length: 4\r\n' + '\r\n' + 'ABCD') + proxy.write_as_browser(req_contents) + assert not proxy.req_save.called + proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 80085, True, + socks_config=None, + use_http_proxy=False) + +def test_proxy_server_macro_simple(mocker): + proxy = TestProxyConnection() + + new_req_contents = 'GET / HTTP/1.1\r\nMangled: Very yes\r\n\r\n' + new_rsp_contents = 'HTTP/1.1 200 OKILIE DOKILIE\r\nMangled: Very yes\r\n\r\n' + new_req = Request(new_req_contents) + new_rsp = Response(new_rsp_contents) + test_macro = InterceptMacroTest(new_req=new_req, new_rsp=new_rsp) + proxy.setUp(mocker, int_macros={'test_macro': test_macro}) + proxy.write_as_browser('GET /serious.php HTTP/1.1\r\n\r\n') + assert proxy.read_as_server() == new_req_contents + proxy.write_as_server('HTTP/1.1 404 NOT FOUND\r\n\r\n') + assert proxy.read_as_browser() == new_rsp_contents + +def test_proxy_server_macro_multiple(mocker): + proxy = TestProxyConnection() + + new_req_contents1 = 'GET / HTTP/1.1\r\nMangled: Very yes\r\n\r\n' + new_rsp_contents1 = 'HTTP/1.1 200 OKILIE DOKILIE\r\nMangled: Very yes\r\n\r\n' + new_req1 = Request(new_req_contents1) + new_rsp1 = Response(new_rsp_contents1) + + new_req_contents2 = 'GET / HTTP/1.1\r\nMangled: Very very yes\r\n\r\n' + new_rsp_contents2 = 'HTTP/1.1 200 OKILIE DOKILIE\r\nMangled: Very very yes\r\n\r\n' + new_req2 = Request(new_req_contents2) + new_rsp2 = Response(new_rsp_contents2) + + test_macro1 = InterceptMacroTest(new_req=new_req1, new_rsp=new_rsp1) + test_macro2 = InterceptMacroTest(new_req=new_req2, new_rsp=new_rsp2) + + macros = collections.OrderedDict() + macros['macro1'] = test_macro1 + macros['macro2'] = test_macro2 + + proxy.setUp(mocker, int_macros=macros) + proxy.write_as_browser('GET /serious.php HTTP/1.1\r\n\r\n') + assert proxy.read_as_server() == new_req_contents2 + proxy.write_as_server('HTTP/1.1 404 NOT FOUND\r\n\r\n') + assert proxy.read_as_browser() == new_rsp_contents2 + +def test_proxy_server_macro_360_noscope(mocker): + proxy = TestProxyConnection() + + new_req_contents = 'GET / HTTP/1.1\r\nMangled: Very yes\r\n\r\n' + new_rsp_contents = 'HTTP/1.1 200 OKILIE DOKILIE\r\nMangled: Very yes\r\n\r\n' + new_req = Request(new_req_contents) + new_rsp = Response(new_rsp_contents) + test_macro = InterceptMacroTest(new_req=new_req, new_rsp=new_rsp) + proxy.setUp(mocker, int_macros={'test_macro': test_macro}, in_scope=False) + proxy.write_as_browser('GET /serious.php HTTP/1.1\r\n\r\n') + assert proxy.read_as_server() == 'GET /serious.php HTTP/1.1\r\n\r\n' + proxy.write_as_server('HTTP/1.1 404 NOT FOUND\r\n\r\n') + assert proxy.read_as_browser() == 'HTTP/1.1 404 NOT FOUND\r\n\r\n' + +def test_proxy_server_stream_simple(mocker): + proxy = TestProxyConnection() + proxy.setUp(mocker) + req_contents = ('POST /fooo HTTP/1.1\r\n' + 'Test-Header: foo\r\n' + 'Content-Length: 4\r\n' + 'Host: www.AAAA.BBBB\r\n' + '\r\n' + 'ABCD') + rsp_contents = ('HTTP/1.1 200 OK\r\n\r\n') + proxy.write_as_browser(req_contents) + assert proxy.read_as_server() == req_contents + proxy.write_as_server(rsp_contents[:20]) + assert proxy.read_as_browser() == rsp_contents[:20] + proxy.write_as_server(rsp_contents[20:]) + assert proxy.read_as_browser() == rsp_contents[20:] + +def test_proxy_server_macro_stream(mocker): + proxy = TestProxyConnection() + + new_req_contents = 'GET / HTTP/1.1\r\nMangled: Very yes\r\n\r\n' + new_rsp_contents = 'HTTP/1.1 200 OKILIE DOKILIE\r\nMangled: Very yes\r\n\r\n' + new_req = Request(new_req_contents) + new_rsp = Response(new_rsp_contents) + test_macro = InterceptMacroTest(new_req=new_req, new_rsp=new_rsp) + proxy.setUp(mocker, int_macros={'test_macro': test_macro}) + proxy.write_as_browser('GET /serious.php HTTP/1.1\r\n\r\n') + assert proxy.read_as_server() == new_req_contents + proxy.write_as_server('HTTP/1.1 404 ') + assert proxy.read_as_browser() == '' + proxy.write_as_server('NOT FOUND\r\n\r\n') + assert proxy.read_as_browser() == new_rsp_contents + +# It doesn't stream if out of scope and macros are active, but whatever. +# def test_proxy_server_macro_stream_360_noscope(mocker): +# proxy = TestProxyConnection() + +# new_req_contents = 'GET / HTTP/1.1\r\nMangled: Very yes\r\n\r\n' +# new_rsp_contents = 'HTTP/1.1 200 OKILIE DOKILIE\r\nMangled: Very yes\r\n\r\n' +# new_req = Request(new_req_contents) +# new_rsp = Response(new_rsp_contents) +# test_macro = InterceptMacroTest(new_req=new_req, new_rsp=new_rsp) +# proxy.setUp(mocker, int_macros={'test_macro': test_macro}, in_scope=False) +# proxy.write_as_browser('GET /serious.php HTTP/1.1\r\n\r\n') +# assert proxy.read_as_server() == 'GET /serious.php HTTP/1.1\r\n\r\n' +# proxy.write_as_server('HTTP/1.1 404 ') +# assert proxy.read_as_browser() == 'HTTP/1.1 404 ' +# proxy.write_as_server('NOT FOUND\r\n\r\n') +# assert proxy.read_as_browser() == 'NOT FOUND\r\n\r\n' diff --git a/pappyproxy/tests/test_proxy.py b/pappyproxy/tests/test_proxy.py index 4d2d9f6..a51d7a0 100644 --- a/pappyproxy/tests/test_proxy.py +++ b/pappyproxy/tests/test_proxy.py @@ -1,422 +1,370 @@ -import pytest import mock -import random -import datetime -import pappyproxy -import base64 -import collections +import pytest +import twisted.internet.endpoints from pappyproxy import http -from pappyproxy.proxy import ProxyClientFactory, ProxyServerFactory, UpstreamHTTPProxyClient -from pappyproxy.http import Request, Response -from pappyproxy.macros import InterceptMacro -from testutil import mock_deferred, func_deleted, TLSStringTransport, freeze, mock_int_macro, no_tcp -from twisted.internet import defer - -class InterceptMacroTest(InterceptMacro): - - def __init__(self, new_req=None, new_rsp=None): - InterceptMacro.__init__(self) - - self.new_req = None - self.new_rsp = None - if new_req: - self.intercept_requests = True - self.new_req = new_req - if new_rsp: - self.intercept_responses = True - self.new_rsp = new_rsp - - def mangle_request(self, request): - if self.intercept_requests: - return self.new_req - else: - return request - - def mangle_response(self, request): - if self.intercept_responses: - return self.new_rsp - else: - return request.response - -class TestProxyConnection(object): - - @property - def client_protocol(self): - if 'protocol' not in self.conn_info: - raise Exception('Connection to server not made. Cannot write data as server.') - return self.conn_info['protocol'] - - @property - def client_factory(self): - if 'protocol' not in self.conn_info: - raise Exception('Connection to server not made. Cannot write data as server.') - return self.conn_info['factory'] +from pappyproxy.proxy import MaybeTLSProtocol, start_maybe_tls, PassthroughProtocolFactory, make_proxied_connection +from testutil import mock_deferred, func_deleted, TLSStringTransport, freeze, mock_int_macro, no_tcp, mock_config +from pappyproxy.util import PappyStringTransport +from twisted.internet import defer, ssl + +############################### +## Helper functions and classes + +def gen_debug_protocol(mocker): + def fake_start_tls(transport, context): + transport.protocol = mock.MagicMock() + mocker.patch("pappyproxy.util.PappyStringTransport.startTLS", new=fake_start_tls) + mocker.patch("pappyproxy.proxy.generate_tls_context") + + t = PappyStringTransport() + def server_data_received(s): + t.write(s) + factory = PassthroughProtocolFactory(server_data_received, None, None) + p = factory.buildProtocol(None) + p.transport = t + t.protocol= p + return p, t + +def mock_protocol_proxy(mocker): + from pappyproxy import proxy + mock_make_proxied_connection = mocker.patch("pappyproxy.proxy.make_proxied_connection") + p = proxy.ProtocolProxy() + p.client_transport = PappyStringTransport() + p.server_transport = PappyStringTransport() + + client_protocol, _ = gen_debug_protocol(mocker) + server_protocol, _ = gen_debug_protocol(mocker) + + return p, client_protocol, server_protocol, mock_make_proxied_connection + +########################## +## Tests for ProtocolProxy + +def test_simple(mocker): + mock_config(mocker) + proxy, _, _, _ = mock_protocol_proxy(mocker) + proxy.send_client_data("foobar") + assert proxy.client_buffer == "foobar" + proxy.send_server_data("barfoo") + assert proxy.server_buffer == "barfoo" + +def test_connect(mocker): + mock_config(mocker) + proxy, _, _, mock_make_proxied_connection = mock_protocol_proxy(mocker) + proxy.send_client_data("foobar") + proxy.send_server_data("barfoo") + proxy.connect("fakehost", 1337, False) + assert len(mock_make_proxied_connection.mock_calls) == 1 + callargs = mock_make_proxied_connection.mock_calls[0][1] + assert callargs[1] == "fakehost" + assert callargs[2] == 1337 + assert callargs[3] == False - def setUp(self, mocker, int_macros={}, socks_config=None, http_config=None, in_scope=True): - self.mocker = mocker - self.conn_info = {} - - # Mock config - self.mock_config = pappyproxy.config.PappyConfig() - self.mock_config.socks_proxy = socks_config - self.mock_config.http_proxy = http_config - self.mock_session = pappyproxy.pappy.PappySession(self.mock_config) - mocker.patch.object(pappyproxy.pappy, 'session', new=self.mock_session) - mocker.patch("pappyproxy.proxy.load_certs_from_dir", new=mock_generate_cert) - - # Listening server - self.server_factory = ProxyServerFactory() - self.server_factory.save_all = True - self.server_factory.intercepting_macros = int_macros - - self.server_protocol = self.server_factory.buildProtocol(('127.0.0.1', 0)) - self.server_transport = TLSStringTransport() - self.server_protocol.makeConnection(self.server_transport) - - # Other mocks - self.req_save = mocker.patch.object(pappyproxy.http.Request, 'async_deep_save', autospec=True, side_effect=mock_req_async_save) - self.submit_request = mocker.patch('pappyproxy.http.Request.submit_request', - new=self.gen_mock_submit_request()) - self.get_endpoint = mocker.patch('pappyproxy.proxy.get_endpoint') - self.in_scope = mocker.patch('pappyproxy.context.in_scope').return_value = in_scope - - def gen_mock_submit_request(self): - orig = Request.submit_request - def f(request, save_request=False, intercepting_macros={}, stream_transport=None): - return orig(request, save_request=save_request, - intercepting_macros=intercepting_macros, - stream_transport=stream_transport, - _factory_string_transport=True, - _conn_info=self.conn_info) - return f - - def perform_connect_request(self): - self.write_as_browser('CONNECT https://www.AAAA.BBBB:443 HTTP/1.1\r\n\r\n') - assert self.read_as_browser() == 'HTTP/1.1 200 Connection established\r\n\r\n' +def test_send_before_connect(mocker): + mock_config(mocker) + proxy, client_protocol, server_protocol, mock_make_proxied_connection = mock_protocol_proxy(mocker) + proxy.send_client_data("foobar") + proxy.send_server_data("barfoo") + + proxy.connect("fakehost", 1337, False) - def write_as_browser(self, data): - self.server_protocol.dataReceived(data) + proxy.client_connection_made(client_protocol) + assert proxy.client_buffer == '' + assert client_protocol.transport.pop_value() == 'foobar' - def read_as_browser(self): - s = self.server_protocol.transport.value() - self.server_protocol.transport.clear() - return s + assert proxy.server_buffer == 'barfoo' - def write_as_server(self, data): - self.client_protocol.dataReceived(data) + proxy.server_connection_made(server_protocol) + assert proxy.server_buffer == '' + assert server_protocol.transport.pop_value() == 'barfoo' - def read_as_server(self): - s = self.client_protocol.transport.value() - self.client_protocol.transport.clear() - return s +def test_send_after_connect(mocker): + mock_config(mocker) + proxy, client_protocol, server_protocol, mock_make_proxied_connection = mock_protocol_proxy(mocker) + proxy.connect("fakehost", 1337, False) + + proxy.client_connection_made(client_protocol) + proxy.send_client_data("foobar") + assert client_protocol.transport.pop_value() == 'foobar' + + proxy.server_connection_made(server_protocol) + proxy.send_server_data("barfoo") + assert server_protocol.transport.pop_value() == 'barfoo' -def mock_req_async_save(req): - req.reqid = str(random.randint(1,1000000)) - return mock_deferred() - -def mock_mangle_response_side_effect(new_rsp): - def f(request, mangle_macros): - request.response = new_rsp - return mock_deferred(True) - return f - -def mock_generate_cert(cert_dir): - private_key = ('-----BEGIN PRIVATE KEY-----\n' - 'MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDAoClrYUEB7lM0\n' - 'zQaKkXZVG2d1Bu9hV8urpx0gNXMbyZ2m3xb+sKZju/FHPuWenA4KaN5gRUT+oLfv\n' - 'tnF6Ia0jpRNWnX0Fyn/irdg1BWGJn7k7mJ2D0NXZQczn2+xxY05599NfGWqNKCYy\n' - 'jhSwPsUK+sGJqi7aSDdlS97ZTjrQVTTFsC0+kSu4lS5fsWXxqrKLa6Ao8W7abVRO\n' - 'JHazh/cxM4UKpgWU+E6yD4o4ZgHY+SMTVUh/IOM8DuOVyLEWtx4oLNiLMlpWT3qy\n' - '4IMpOF6VuU6JF2HGV13SoJfhsLXsPRbLVTAnZvJcZwtgDm6NfKapU8W8olkDV1Bf\n' - 'YQEMSNX7AgMBAAECggEBAII0wUrAdrzjaIMsg9tu8FofKBPHGFDok9f4Iov/FUwX\n' - 'QOXnrxeCOTb5d+L89SH9ws/ui0LwD+8+nJcA8DvqP6r0jtnhov0jIMcNVDSi6oeo\n' - '3AEY7ICJzcQJ4oRn+K+8vPNdPhfuikPYe9l4iSuJgpAlaGWyD/GlFyz12DFz2/Wu\n' - 'NIcqR1ucvezRHn3eGMtvDv2WGaN4ifUc30k8XgSUesmwSI6beb5+hxq7wXfsurnP\n' - 'EUrPY9ts3lfiAgxzTKOuj1VR5hn7cJyLN8jF0mZs4D6eSSHorIddhmaNiCq5ZbMd\n' - 'QdlDiPvnXHT41OoXOb7tDEt7SGoiRh2noCZ1aZiSziECgYEA+tuPPLYWU6JRB6EW\n' - 'PhbcXQbh3vML7eT1q7DOz0jYCojgT2+k7EWSI8T830oQyjbpe3Z86XEgH7UBjUgq\n' - '27nJ4E6dQDYGbYCKEklOoCGLE7A60i1feIz8otOQRrbQ4jcpibEgscA6gzHmunYf\n' - 'De5euUgYW+Rq2Vmr6/NzUaUgui8CgYEAxJMDwPOGgiLM1cczlaSIU9Obz+cVnwWn\n' - 'nsdKYMto2V3yKLydDfjsgOgzxHOxxy+5L645TPxK6CkiISuhJ93kAFFtx+1sCBCT\n' - 'tVzY5robVAekxA9tlPIxtsn3+/axx3n6HnV0oA/XtxkuOS5JImgEdXqFwJZkerGE\n' - 'waftIU2FCfUCgYEArl8+ErJzlJEIiCgWIPSdGuD00pfZW/TCPCT7rKRy3+fDHBR7\n' - '7Gxzp/9+0utV/mnrJBH5w/8JmGCmgoF+oRtk01FyBzdGgolN8GYajD6kwPvH917o\n' - 'tRAzcC9lY3IigoxbiEWid0wqoBVoz4XaEkH2gA44OG/vQcQOOEYSi9cfh6sCgYBg\n' - 'KLaOXdJvuIxRCzgNvMW/k+VFh3pJJx//COg2f2qT4mQCT3nYiutOh8hDEoFluc+y\n' - 'Jlz7bvNJrE14wnn8IYxWJ383bMoLC+jlsDyeaW3S5kZQbmehk/SDwTrg86W1udKD\n' - 'sdtSLU3N0LCO4jh+bzm3Ki9hrXALoOkbPoU+ZEhvPQKBgQDf79XQ3RNxZSk+eFyq\n' - 'qD8ytVqxEoD+smPDflXXseVH6o+pNWrF8+A0KqmO8c+8KVzWj/OfULO6UbKd3E+x\n' - '4JGkWu9yF1lEgtHgibF2ER8zCSIL4ikOEasPCkrKj5SrS4Q+j4u5ha76dIc2CVu1\n' - 'hkX2PQ1xU4ocu06k373sf73A4Q==\n' - '-----END PRIVATE KEY-----') - ca_key = ('-----BEGIN CERTIFICATE-----\n' - 'MIIDjzCCAncCFQCjC8r+I4xa7JoGUJYGOTcqDROA0DANBgkqhkiG9w0BAQsFADBg\n' - 'MQswCQYDVQQGEwJVUzERMA8GA1UECBMITWljaGlnYW4xEjAQBgNVBAcTCUFubiBB\n' - 'cmJvcjEUMBIGA1UEChMLUGFwcHkgUHJveHkxFDASBgNVBAMTC1BhcHB5IFByb3h5\n' - 'MB4XDTE1MTEyMDIxMTEzOVoXDTI1MTExNzIxMTEzOVowYDELMAkGA1UEBhMCVVMx\n' - 'ETAPBgNVBAgTCE1pY2hpZ2FuMRIwEAYDVQQHEwlBbm4gQXJib3IxFDASBgNVBAoT\n' - 'C1BhcHB5IFByb3h5MRQwEgYDVQQDEwtQYXBweSBQcm94eTCCASIwDQYJKoZIhvcN\n' - 'AQEBBQADggEPADCCAQoCggEBAMCgKWthQQHuUzTNBoqRdlUbZ3UG72FXy6unHSA1\n' - 'cxvJnabfFv6wpmO78Uc+5Z6cDgpo3mBFRP6gt++2cXohrSOlE1adfQXKf+Kt2DUF\n' - 'YYmfuTuYnYPQ1dlBzOfb7HFjTnn3018Zao0oJjKOFLA+xQr6wYmqLtpIN2VL3tlO\n' - 'OtBVNMWwLT6RK7iVLl+xZfGqsotroCjxbtptVE4kdrOH9zEzhQqmBZT4TrIPijhm\n' - 'Adj5IxNVSH8g4zwO45XIsRa3Higs2IsyWlZPerLggyk4XpW5TokXYcZXXdKgl+Gw\n' - 'tew9FstVMCdm8lxnC2AObo18pqlTxbyiWQNXUF9hAQxI1fsCAwEAAaNFMEMwEgYD\n' - 'VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNo5o+5e\n' - 'a0sNMlW/75VgGJCv2AcJMA0GCSqGSIb3DQEBCwUAA4IBAQBdJDhxbmoEe27bD8me\n' - 'YTcLGjs/StKkSil7rLbX+tBCwtkm5UEEejBuAhKk2FuAXW8yR1FqKJSZwVCAocBT\n' - 'Bo/+97Ee+h7ywrRFhATEr9D/TbbHKOjCjDzOMl9yLZa2DKErZjbI30ZD6NafWS/X\n' - 'hx5X1cGohHcVVzT4jIgUEU70vvYfNn8CTZm4oJ7qqRe/uQPUYy0rwvbd60oprtGg\n' - 'jNv1H5R4ODHUMBXAI9H7ft9cWrd0fBQjxhoj8pvgJXEZ52flXSqQc7qHLg1wO/zC\n' - 'RUgpTcNAb2qCssBKbj+c1vKEPRUJfw6UYb0s1462rQNc8BgZiKaNbwokFmkAnjUg\n' - 'AvnX\n' - '-----END CERTIFICATE-----') - return (ca_key, private_key) - -######## -## Tests - -def test_no_tcp(): - from twisted.internet.endpoints import SSL4ClientEndpoint, TCP4ClientEndpoint - from txsocksx.client import SOCKS5ClientEndpoint - from txsocksx.tls import TLSWrapClientEndpoint - with pytest.raises(NotImplementedError): - SSL4ClientEndpoint('aasdfasdf.sdfwerqwer') - with pytest.raises(NotImplementedError): - TCP4ClientEndpoint('aasdfasdf.sdfwerqwer') - with pytest.raises(NotImplementedError): - SOCKS5ClientEndpoint('aasdfasdf.sdfwerqwer') - with pytest.raises(NotImplementedError): - TLSWrapClientEndpoint('asdf.2341') - -def test_proxy_server_connect(mocker): - proxy = TestProxyConnection() - proxy.setUp(mocker) - proxy.write_as_browser('CONNECT https://www.AAAA.BBBB:443 HTTP/1.1\r\n\r\n') - rsp = proxy.read_as_browser() - print rsp - assert rsp == 'HTTP/1.1 200 Connection established\r\n\r\n' - -def test_proxy_server_forward_basic(mocker): - proxy = TestProxyConnection() - proxy.setUp(mocker) - req_contents = ('POST /fooo HTTP/1.1\r\n' - 'Test-Header: foo\r\n' - 'Content-Length: 4\r\n' - 'Host: www.AAAA.BBBB\r\n' - '\r\n' - 'ABCD') - rsp_contents = ('HTTP/1.1 200 OK\r\n\r\n') - proxy.write_as_browser(req_contents) - assert proxy.read_as_server() == req_contents - proxy.write_as_server(rsp_contents) - assert proxy.read_as_browser() == rsp_contents - proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 80, False, socks_config=None, use_http_proxy=True) - assert proxy.req_save.called - -def test_proxy_server_forward_basic_ssl(mocker): - proxy = TestProxyConnection() - proxy.setUp(mocker) - proxy.perform_connect_request() - req_contents = ('POST /fooo HTTP/1.1\r\n' - 'Test-Header: foo\r\n' - 'Content-Length: 4\r\n' - '\r\n' - 'ABCD') - rsp_contents = ('HTTP/1.1 200 OK\r\n\r\n') - proxy.write_as_browser(req_contents) - assert proxy.read_as_server() == req_contents - proxy.write_as_server(rsp_contents) - assert proxy.read_as_browser() == rsp_contents - assert proxy.req_save.called - proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 443, True, socks_config=None, use_http_proxy=True) - -def test_proxy_server_connect_uri(mocker): - proxy = TestProxyConnection() - proxy.setUp(mocker) - proxy.write_as_browser('CONNECT https://www.AAAA.BBBB:443 HTTP/1.1\r\n\r\n') - proxy.read_as_browser() - req_contents = ('POST /fooo HTTP/1.1\r\n' - 'Test-Header: foo\r\n' - 'Content-Length: 4\r\n' - '\r\n' - 'ABCD') - proxy.write_as_browser(req_contents) - assert proxy.client_protocol.transport.startTLS.called - assert proxy.client_factory.request.host == 'www.AAAA.BBBB' - assert proxy.client_factory.request.port == 443 - assert proxy.client_factory.request.is_ssl == True - assert proxy.read_as_server() == req_contents - assert proxy.client_protocol.transport.startTLS.called - assert proxy.req_save.called - proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 443, True, socks_config=None, use_http_proxy=True) - -def test_proxy_server_connect_uri_alt_port(mocker): - proxy = TestProxyConnection() - proxy.setUp(mocker) - proxy.write_as_browser('CONNECT https://www.AAAA.BBBB:80085 HTTP/1.1\r\n\r\n') - proxy.read_as_browser() - req_contents = ('POST /fooo HTTP/1.1\r\n' - 'Test-Header: foo\r\n' - 'Content-Length: 4\r\n' - '\r\n' - 'ABCD') - proxy.write_as_browser(req_contents) - assert proxy.client_factory.request.host == 'www.AAAA.BBBB' - assert proxy.client_factory.request.port == 80085 - assert proxy.client_factory.request.is_ssl == True - assert proxy.req_save.called - proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 80085, True, socks_config=None, use_http_proxy=True) - -def test_proxy_server_socks_basic(mocker): - proxy = TestProxyConnection() - proxy.setUp(mocker, socks_config={'host': 'www.banana.faketld', 'port': 1337}) - proxy.write_as_browser('CONNECT https://www.AAAA.BBBB:80085 HTTP/1.1\r\n\r\n') - proxy.read_as_browser() - req_contents = ('POST /fooo HTTP/1.1\r\n' - 'Test-Header: foo\r\n' - 'Content-Length: 4\r\n' - '\r\n' - 'ABCD') - proxy.write_as_browser(req_contents) - proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 80085, True, - socks_config={'host':'www.banana.faketld', 'port':1337}, - use_http_proxy=True) - -def test_proxy_server_http_basic(mocker): - proxy = TestProxyConnection() - proxy.setUp(mocker, http_config={'host': 'www.banana.faketld', 'port': 1337}) - proxy.write_as_browser('CONNECT https://www.AAAA.BBBB:80085 HTTP/1.1\r\n\r\n') - proxy.read_as_browser() - req_contents = ('POST /fooo HTTP/1.1\r\n' - 'Test-Header: foo\r\n' - 'Content-Length: 4\r\n' - '\r\n' - 'ABCD') - proxy.write_as_browser(req_contents) - assert proxy.req_save.called - proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 80085, True, - socks_config=None, - use_http_proxy=True) - -def test_proxy_server_360_noscope(mocker): - proxy = TestProxyConnection() - proxy.setUp(mocker, in_scope=False, socks_config={'host': 'www.banana.faketld', 'port': 1337}) - proxy.write_as_browser('CONNECT https://www.AAAA.BBBB:80085 HTTP/1.1\r\n\r\n') - proxy.read_as_browser() - req_contents = ('POST /fooo HTTP/1.1\r\n' - 'Test-Header: foo\r\n' - 'Content-Length: 4\r\n' - '\r\n' - 'ABCD') - proxy.write_as_browser(req_contents) - assert not proxy.req_save.called - proxy.get_endpoint.assert_called_with('www.AAAA.BBBB', 80085, True, - socks_config=None, - use_http_proxy=False) +def test_start_tls_before_connect(mocker): + mock_config(mocker) + proxy, client_protocol, server_protocol, mock_make_proxied_connection = mock_protocol_proxy(mocker) + client_protocol.transport.startTLS = mock.MagicMock() + server_protocol.transport.startTLS = mock.MagicMock() + + server_protocol.transport.startTLS.assert_not_called() + client_protocol.transport.startTLS.assert_not_called() + proxy.start_server_tls() + proxy.start_client_tls("fakehost") + + server_protocol.transport.startTLS.assert_not_called() + client_protocol.transport.startTLS.assert_not_called() + proxy.connect("fakehost", 1337, False) + server_protocol.transport.startTLS.assert_not_called() + client_protocol.transport.startTLS.assert_not_called() + + proxy.server_connection_made(server_protocol) + assert len(server_protocol.transport.startTLS.mock_calls) == 1 + client_protocol.transport.startTLS.assert_not_called() + + proxy.client_connection_made(client_protocol) + assert len(client_protocol.transport.startTLS.mock_calls) == 1 + +def test_start_tls_after_connect(mocker): + mock_config(mocker) + proxy, client_protocol, server_protocol, mock_make_proxied_connection = mock_protocol_proxy(mocker) + client_protocol.transport.startTLS = mock.MagicMock() + server_protocol.transport.startTLS = mock.MagicMock() + + server_protocol.transport.startTLS.assert_not_called() + client_protocol.transport.startTLS.assert_not_called() + + proxy.connect("fakehost", 1337, False) + + server_protocol.transport.startTLS.assert_not_called() + client_protocol.transport.startTLS.assert_not_called() + + proxy.server_connection_made(server_protocol) + proxy.start_server_tls() + assert len(server_protocol.transport.startTLS.mock_calls) == 1 + + proxy.client_connection_made(client_protocol) + proxy.start_client_tls("fakehost") + assert len(client_protocol.transport.startTLS.mock_calls) == 1 + +def test_start_maybe_tls_before_connect(mocker): + mock_config(mocker) + proxy, client_protocol, server_protocol, mock_make_proxied_connection = mock_protocol_proxy(mocker) + mock_maybe_tls = mocker.patch("pappyproxy.proxy.start_maybe_tls") + client_protocol.transport.startTLS = mock.MagicMock() + server_protocol.transport.startTLS = mock.MagicMock() + + proxy.start_client_maybe_tls("fakehost") + + client_protocol.transport.startTLS.assert_not_called() + proxy.connect("fakehost", 1337, False) + client_protocol.transport.startTLS.assert_not_called() + + proxy.client_connection_made(client_protocol) + client_protocol.transport.startTLS.assert_not_called() + assert len(mock_maybe_tls.mock_calls) == 1 + + callargs = mock_maybe_tls.mock_calls[0][2] + assert callargs['tls_host'] == 'fakehost' + assert callargs['start_tls_callback'] == proxy.start_server_tls + +def test_start_maybe_tls_before_connect(mocker): + mock_config(mocker) + proxy, client_protocol, server_protocol, mock_make_proxied_connection = mock_protocol_proxy(mocker) + mock_maybe_tls = mocker.patch("pappyproxy.proxy.start_maybe_tls") + client_protocol.transport.startTLS = mock.MagicMock() + server_protocol.transport.startTLS = mock.MagicMock() + + + client_protocol.transport.startTLS.assert_not_called() + proxy.connect("fakehost", 1337, False) + client_protocol.transport.startTLS.assert_not_called() + + proxy.client_connection_made(client_protocol) + proxy.start_client_maybe_tls("fakehost") + client_protocol.transport.startTLS.assert_not_called() + assert len(mock_maybe_tls.mock_calls) == 1 + + callargs = mock_maybe_tls.mock_calls[0][2] + assert callargs['tls_host'] == 'fakehost' + assert callargs['start_tls_callback'] == proxy.start_server_tls -def test_proxy_server_macro_simple(mocker): - proxy = TestProxyConnection() - - new_req_contents = 'GET / HTTP/1.1\r\nMangled: Very yes\r\n\r\n' - new_rsp_contents = 'HTTP/1.1 200 OKILIE DOKILIE\r\nMangled: Very yes\r\n\r\n' - new_req = Request(new_req_contents) - new_rsp = Response(new_rsp_contents) - test_macro = InterceptMacroTest(new_req=new_req, new_rsp=new_rsp) - proxy.setUp(mocker, int_macros={'test_macro': test_macro}) - proxy.write_as_browser('GET /serious.php HTTP/1.1\r\n\r\n') - assert proxy.read_as_server() == new_req_contents - proxy.write_as_server('HTTP/1.1 404 NOT FOUND\r\n\r\n') - assert proxy.read_as_browser() == new_rsp_contents - -def test_proxy_server_macro_multiple(mocker): - proxy = TestProxyConnection() - - new_req_contents1 = 'GET / HTTP/1.1\r\nMangled: Very yes\r\n\r\n' - new_rsp_contents1 = 'HTTP/1.1 200 OKILIE DOKILIE\r\nMangled: Very yes\r\n\r\n' - new_req1 = Request(new_req_contents1) - new_rsp1 = Response(new_rsp_contents1) - - new_req_contents2 = 'GET / HTTP/1.1\r\nMangled: Very very yes\r\n\r\n' - new_rsp_contents2 = 'HTTP/1.1 200 OKILIE DOKILIE\r\nMangled: Very very yes\r\n\r\n' - new_req2 = Request(new_req_contents2) - new_rsp2 = Response(new_rsp_contents2) - - test_macro1 = InterceptMacroTest(new_req=new_req1, new_rsp=new_rsp1) - test_macro2 = InterceptMacroTest(new_req=new_req2, new_rsp=new_rsp2) - - macros = collections.OrderedDict() - macros['macro1'] = test_macro1 - macros['macro2'] = test_macro2 - - proxy.setUp(mocker, int_macros=macros) - proxy.write_as_browser('GET /serious.php HTTP/1.1\r\n\r\n') - assert proxy.read_as_server() == new_req_contents2 - proxy.write_as_server('HTTP/1.1 404 NOT FOUND\r\n\r\n') - assert proxy.read_as_browser() == new_rsp_contents2 - -def test_proxy_server_macro_360_noscope(mocker): - proxy = TestProxyConnection() - - new_req_contents = 'GET / HTTP/1.1\r\nMangled: Very yes\r\n\r\n' - new_rsp_contents = 'HTTP/1.1 200 OKILIE DOKILIE\r\nMangled: Very yes\r\n\r\n' - new_req = Request(new_req_contents) - new_rsp = Response(new_rsp_contents) - test_macro = InterceptMacroTest(new_req=new_req, new_rsp=new_rsp) - proxy.setUp(mocker, int_macros={'test_macro': test_macro}, in_scope=False) - proxy.write_as_browser('GET /serious.php HTTP/1.1\r\n\r\n') - assert proxy.read_as_server() == 'GET /serious.php HTTP/1.1\r\n\r\n' - proxy.write_as_server('HTTP/1.1 404 NOT FOUND\r\n\r\n') - assert proxy.read_as_browser() == 'HTTP/1.1 404 NOT FOUND\r\n\r\n' - -def test_proxy_server_stream_simple(mocker): - proxy = TestProxyConnection() - proxy.setUp(mocker) - req_contents = ('POST /fooo HTTP/1.1\r\n' - 'Test-Header: foo\r\n' - 'Content-Length: 4\r\n' - 'Host: www.AAAA.BBBB\r\n' - '\r\n' - 'ABCD') - rsp_contents = ('HTTP/1.1 200 OK\r\n\r\n') - proxy.write_as_browser(req_contents) - assert proxy.read_as_server() == req_contents - proxy.write_as_server(rsp_contents[:20]) - assert proxy.read_as_browser() == rsp_contents[:20] - proxy.write_as_server(rsp_contents[20:]) - assert proxy.read_as_browser() == rsp_contents[20:] - -def test_proxy_server_macro_stream(mocker): - proxy = TestProxyConnection() - - new_req_contents = 'GET / HTTP/1.1\r\nMangled: Very yes\r\n\r\n' - new_rsp_contents = 'HTTP/1.1 200 OKILIE DOKILIE\r\nMangled: Very yes\r\n\r\n' - new_req = Request(new_req_contents) - new_rsp = Response(new_rsp_contents) - test_macro = InterceptMacroTest(new_req=new_req, new_rsp=new_rsp) - proxy.setUp(mocker, int_macros={'test_macro': test_macro}) - proxy.write_as_browser('GET /serious.php HTTP/1.1\r\n\r\n') - assert proxy.read_as_server() == new_req_contents - proxy.write_as_server('HTTP/1.1 404 ') - assert proxy.read_as_browser() == '' - proxy.write_as_server('NOT FOUND\r\n\r\n') - assert proxy.read_as_browser() == new_rsp_contents - -# It doesn't stream if out of scope and macros are active, but whatever. -# def test_proxy_server_macro_stream_360_noscope(mocker): -# proxy = TestProxyConnection() - -# new_req_contents = 'GET / HTTP/1.1\r\nMangled: Very yes\r\n\r\n' -# new_rsp_contents = 'HTTP/1.1 200 OKILIE DOKILIE\r\nMangled: Very yes\r\n\r\n' -# new_req = Request(new_req_contents) -# new_rsp = Response(new_rsp_contents) -# test_macro = InterceptMacroTest(new_req=new_req, new_rsp=new_rsp) -# proxy.setUp(mocker, int_macros={'test_macro': test_macro}, in_scope=False) -# proxy.write_as_browser('GET /serious.php HTTP/1.1\r\n\r\n') -# assert proxy.read_as_server() == 'GET /serious.php HTTP/1.1\r\n\r\n' -# proxy.write_as_server('HTTP/1.1 404 ') -# assert proxy.read_as_browser() == 'HTTP/1.1 404 ' -# proxy.write_as_server('NOT FOUND\r\n\r\n') -# assert proxy.read_as_browser() == 'NOT FOUND\r\n\r\n' + +############################# +## Tests for MaybeTLSProtocol +def test_maybe_tls_plaintext(mocker): + mock_config(mocker) + tls_callback = mock.MagicMock() + p, t = gen_debug_protocol(mocker) + start_maybe_tls(p.transport, 'www.foo.faketld') + p.dataReceived("Hello world!") + assert p.transport.pop_value() == "Hello world!" + +def test_maybe_tls_use_tls(mocker): + mock_config(mocker) + tls_callback = mock.MagicMock() + p, t = gen_debug_protocol(mocker) + start_maybe_tls(p.transport, 'www.foo.faketld') + maybe_tls_prot = t.protocol + assert isinstance(maybe_tls_prot, MaybeTLSProtocol) + assert maybe_tls_prot.state == MaybeTLSProtocol.STATE_DECIDING + t.protocol.dataReceived("\x16") + assert not isinstance(t.protocol, MaybeTLSProtocol) + assert maybe_tls_prot.state == MaybeTLSProtocol.STATE_PASSTHROUGH + +#################################### +## Tests for make_proxied_connection +def test_mpc_simple(mocker): + mock_config(mocker) + endpoint_instance = mock.MagicMock() + endpoint = mocker.patch('twisted.internet.endpoints.TCP4ClientEndpoint', return_value=endpoint_instance) + + make_proxied_connection('fakefactory', 'fakehost', 1337, False) + + endpointcalls = endpoint.mock_calls[0] + assert endpointcalls[1][1] == 'fakehost' + assert endpointcalls[1][2] == 1337 + + connectcall = endpoint_instance.connect + assert len(connectcall.mock_calls) == 1 + +def test_mpc_ssl(mocker): + mock_config(mocker) + endpoint_instance = mock.MagicMock() + endpoint = mocker.patch('twisted.internet.endpoints.SSL4ClientEndpoint', return_value=endpoint_instance) + + make_proxied_connection('fakefactory', 'fakehost', 1337, True) + + endpointcalls = endpoint.mock_calls[0] + assert endpointcalls[1][1] == 'fakehost' + assert endpointcalls[1][2] == 1337 + assert isinstance(endpointcalls[1][3], ssl.ClientContextFactory) + + connectcall = endpoint_instance.connect + assert len(connectcall.mock_calls) == 1 + +def test_mpc_socks(mocker): + mock_config(mocker) + tcp_endpoint_instance = mock.MagicMock() + socks_endpoint_instance = mock.MagicMock() + tcp_endpoint = mocker.patch('twisted.internet.endpoints.TCP4ClientEndpoint', return_value=tcp_endpoint_instance) + socks_endpoint = mocker.patch('txsocksx.client.SOCKS5ClientEndpoint', return_value=socks_endpoint_instance) + + target_host = 'fakehost' + target_port = 1337 + + socks_host = 'fakesockshost' + socks_port = 1447 + socks_config = {'host':socks_host, 'port':socks_port} + + make_proxied_connection('fakefactory', target_host, target_port, False, socks_config=socks_config) + + tcp_ep_calls = tcp_endpoint.mock_calls[0] + assert tcp_ep_calls[1][1] == socks_host + assert tcp_ep_calls[1][2] == socks_port + + socks_ep_calls = socks_endpoint.mock_calls[0] + assert socks_ep_calls[1][0] == target_host + assert socks_ep_calls[1][1] == target_port + assert socks_ep_calls[1][2] is tcp_endpoint_instance + assert socks_ep_calls[2]['methods'] == {'anonymous': ()} + + connectcall = socks_endpoint_instance.connect + assert len(connectcall.mock_calls) == 1 + +def test_mpc_socks_creds(mocker): + mock_config(mocker) + tcp_endpoint_instance = mock.MagicMock() + socks_endpoint_instance = mock.MagicMock() + tcp_endpoint = mocker.patch('twisted.internet.endpoints.TCP4ClientEndpoint', return_value=tcp_endpoint_instance) + socks_endpoint = mocker.patch('txsocksx.client.SOCKS5ClientEndpoint', return_value=socks_endpoint_instance) + + target_host = 'fakehost' + target_port = 1337 + + socks_host = 'fakesockshost' + socks_port = 1447 + socks_user = 'username' + socks_password = 'password' + socks_config = {'host':socks_host, 'port':socks_port, + 'username':socks_user, 'password':socks_password} + + make_proxied_connection('fakefactory', target_host, target_port, False, socks_config=socks_config) + + tcp_ep_calls = tcp_endpoint.mock_calls[0] + assert tcp_ep_calls[1][1] == socks_host + assert tcp_ep_calls[1][2] == socks_port + + socks_ep_calls = socks_endpoint.mock_calls[0] + assert socks_ep_calls[1][0] == target_host + assert socks_ep_calls[1][1] == target_port + assert socks_ep_calls[1][2] is tcp_endpoint_instance + assert socks_ep_calls[2]['methods'] == {'login': (socks_user, socks_password), 'anonymous': ()} + + connectcall = socks_endpoint_instance.connect + assert len(connectcall.mock_calls) == 1 + +def test_mpc_socks_ssl(mocker): + mock_config(mocker) + tcp_endpoint_instance = mock.MagicMock() + socks_endpoint_instance = mock.MagicMock() + wrapper_instance = mock.MagicMock() + tcp_endpoint = mocker.patch('twisted.internet.endpoints.TCP4ClientEndpoint', return_value=tcp_endpoint_instance) + socks_endpoint = mocker.patch('txsocksx.client.SOCKS5ClientEndpoint', return_value=socks_endpoint_instance) + wrapper_endpoint = mocker.patch('txsocksx.tls.TLSWrapClientEndpoint', return_value=wrapper_instance) + + target_host = 'fakehost' + target_port = 1337 + + socks_host = 'fakesockshost' + socks_port = 1447 + socks_config = {'host':socks_host, 'port':socks_port} + + make_proxied_connection('fakefactory', target_host, target_port, True, socks_config=socks_config) + + tcp_ep_calls = tcp_endpoint.mock_calls[0] + assert tcp_ep_calls[1][1] == socks_host + assert tcp_ep_calls[1][2] == socks_port + + socks_ep_calls = socks_endpoint.mock_calls[0] + assert socks_ep_calls[1][0] == target_host + assert socks_ep_calls[1][1] == target_port + assert socks_ep_calls[1][2] is tcp_endpoint_instance + assert socks_ep_calls[2]['methods'] == {'anonymous': ()} + + wrapper_calls = wrapper_endpoint.mock_calls[0] + assert isinstance(wrapper_calls[1][0], ssl.ClientContextFactory) + assert wrapper_calls[1][1] is socks_endpoint_instance + + connectcall = wrapper_instance.connect + assert len(connectcall.mock_calls) == 1 + +def test_mpc_socks_ssl_creds(mocker): + mock_config(mocker) + tcp_endpoint_instance = mock.MagicMock() + socks_endpoint_instance = mock.MagicMock() + wrapper_instance = mock.MagicMock() + tcp_endpoint = mocker.patch('twisted.internet.endpoints.TCP4ClientEndpoint', return_value=tcp_endpoint_instance) + socks_endpoint = mocker.patch('txsocksx.client.SOCKS5ClientEndpoint', return_value=socks_endpoint_instance) + wrapper_endpoint = mocker.patch('txsocksx.tls.TLSWrapClientEndpoint', return_value=wrapper_instance) + + target_host = 'fakehost' + target_port = 1337 + + socks_host = 'fakesockshost' + socks_port = 1447 + socks_user = 'username' + socks_password = 'password' + socks_config = {'host':socks_host, 'port':socks_port, + 'username':socks_user, 'password':socks_password} + + make_proxied_connection('fakefactory', target_host, target_port, True, socks_config=socks_config) + + tcp_ep_calls = tcp_endpoint.mock_calls[0] + assert tcp_ep_calls[1][1] == socks_host + assert tcp_ep_calls[1][2] == socks_port + + socks_ep_calls = socks_endpoint.mock_calls[0] + assert socks_ep_calls[1][0] == target_host + assert socks_ep_calls[1][1] == target_port + assert socks_ep_calls[1][2] is tcp_endpoint_instance + assert socks_ep_calls[2]['methods'] == {'login': (socks_user, socks_password), 'anonymous': ()} + + wrapper_calls = wrapper_endpoint.mock_calls[0] + assert isinstance(wrapper_calls[1][0], ssl.ClientContextFactory) + assert wrapper_calls[1][1] is socks_endpoint_instance + + connectcall = wrapper_instance.connect + assert len(connectcall.mock_calls) == 1 diff --git a/pappyproxy/tests/testutil.py b/pappyproxy/tests/testutil.py index 1893d43..42e04a7 100644 --- a/pappyproxy/tests/testutil.py +++ b/pappyproxy/tests/testutil.py @@ -4,7 +4,7 @@ import pytest import StringIO from twisted.internet import defer from twisted.test.proto_helpers import StringTransport -from pappyproxy import http +from pappyproxy import http, config, pappy next_mock_id = 0 @@ -56,12 +56,13 @@ def mock_deferred(value=None): @pytest.fixture(autouse=True) def no_tcp(mocker): # Don't make tcp connections - mocker.patch("twisted.internet.reactor.connectTCP", new=func_deleted) - mocker.patch("twisted.internet.reactor.connectSSL", new=func_deleted) - mocker.patch("twisted.internet.endpoints.SSL4ClientEndpoint", new=func_deleted) - mocker.patch("twisted.internet.endpoints.TCP4ClientEndpoint", new=func_deleted) - mocker.patch("txsocksx.client.SOCKS5ClientEndpoint", new=func_deleted) - mocker.patch("txsocksx.tls.TLSWrapClientEndpoint", new=func_deleted) + #mocker.patch("twisted.internet.reactor.connectTCP", new=func_deleted) + #mocker.patch("twisted.internet.reactor.connectSSL", new=func_deleted) + #mocker.patch("twisted.internet.endpoints.SSL4ClientEndpoint", new=func_deleted) + #mocker.patch("twisted.internet.endpoints.TCP4ClientEndpoint", new=func_deleted) + #mocker.patch("txsocksx.client.SOCKS5ClientEndpoint", new=func_deleted) + #mocker.patch("txsocksx.tls.TLSWrapClientEndpoint", new=func_deleted) + pass @pytest.fixture def ignore_tcp(mocker): @@ -170,3 +171,66 @@ def mock_int_macro(modified_req=None, modified_rsp=None, else: macro.intercept_responses = False return macro + +def mock_generate_cert(cert_dir): + private_key = ('-----BEGIN PRIVATE KEY-----\n' + 'MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDAoClrYUEB7lM0\n' + 'zQaKkXZVG2d1Bu9hV8urpx0gNXMbyZ2m3xb+sKZju/FHPuWenA4KaN5gRUT+oLfv\n' + 'tnF6Ia0jpRNWnX0Fyn/irdg1BWGJn7k7mJ2D0NXZQczn2+xxY05599NfGWqNKCYy\n' + 'jhSwPsUK+sGJqi7aSDdlS97ZTjrQVTTFsC0+kSu4lS5fsWXxqrKLa6Ao8W7abVRO\n' + 'JHazh/cxM4UKpgWU+E6yD4o4ZgHY+SMTVUh/IOM8DuOVyLEWtx4oLNiLMlpWT3qy\n' + '4IMpOF6VuU6JF2HGV13SoJfhsLXsPRbLVTAnZvJcZwtgDm6NfKapU8W8olkDV1Bf\n' + 'YQEMSNX7AgMBAAECggEBAII0wUrAdrzjaIMsg9tu8FofKBPHGFDok9f4Iov/FUwX\n' + 'QOXnrxeCOTb5d+L89SH9ws/ui0LwD+8+nJcA8DvqP6r0jtnhov0jIMcNVDSi6oeo\n' + '3AEY7ICJzcQJ4oRn+K+8vPNdPhfuikPYe9l4iSuJgpAlaGWyD/GlFyz12DFz2/Wu\n' + 'NIcqR1ucvezRHn3eGMtvDv2WGaN4ifUc30k8XgSUesmwSI6beb5+hxq7wXfsurnP\n' + 'EUrPY9ts3lfiAgxzTKOuj1VR5hn7cJyLN8jF0mZs4D6eSSHorIddhmaNiCq5ZbMd\n' + 'QdlDiPvnXHT41OoXOb7tDEt7SGoiRh2noCZ1aZiSziECgYEA+tuPPLYWU6JRB6EW\n' + 'PhbcXQbh3vML7eT1q7DOz0jYCojgT2+k7EWSI8T830oQyjbpe3Z86XEgH7UBjUgq\n' + '27nJ4E6dQDYGbYCKEklOoCGLE7A60i1feIz8otOQRrbQ4jcpibEgscA6gzHmunYf\n' + 'De5euUgYW+Rq2Vmr6/NzUaUgui8CgYEAxJMDwPOGgiLM1cczlaSIU9Obz+cVnwWn\n' + 'nsdKYMto2V3yKLydDfjsgOgzxHOxxy+5L645TPxK6CkiISuhJ93kAFFtx+1sCBCT\n' + 'tVzY5robVAekxA9tlPIxtsn3+/axx3n6HnV0oA/XtxkuOS5JImgEdXqFwJZkerGE\n' + 'waftIU2FCfUCgYEArl8+ErJzlJEIiCgWIPSdGuD00pfZW/TCPCT7rKRy3+fDHBR7\n' + '7Gxzp/9+0utV/mnrJBH5w/8JmGCmgoF+oRtk01FyBzdGgolN8GYajD6kwPvH917o\n' + 'tRAzcC9lY3IigoxbiEWid0wqoBVoz4XaEkH2gA44OG/vQcQOOEYSi9cfh6sCgYBg\n' + 'KLaOXdJvuIxRCzgNvMW/k+VFh3pJJx//COg2f2qT4mQCT3nYiutOh8hDEoFluc+y\n' + 'Jlz7bvNJrE14wnn8IYxWJ383bMoLC+jlsDyeaW3S5kZQbmehk/SDwTrg86W1udKD\n' + 'sdtSLU3N0LCO4jh+bzm3Ki9hrXALoOkbPoU+ZEhvPQKBgQDf79XQ3RNxZSk+eFyq\n' + 'qD8ytVqxEoD+smPDflXXseVH6o+pNWrF8+A0KqmO8c+8KVzWj/OfULO6UbKd3E+x\n' + '4JGkWu9yF1lEgtHgibF2ER8zCSIL4ikOEasPCkrKj5SrS4Q+j4u5ha76dIc2CVu1\n' + 'hkX2PQ1xU4ocu06k373sf73A4Q==\n' + '-----END PRIVATE KEY-----') + ca_key = ('-----BEGIN CERTIFICATE-----\n' + 'MIIDjzCCAncCFQCjC8r+I4xa7JoGUJYGOTcqDROA0DANBgkqhkiG9w0BAQsFADBg\n' + 'MQswCQYDVQQGEwJVUzERMA8GA1UECBMITWljaGlnYW4xEjAQBgNVBAcTCUFubiBB\n' + 'cmJvcjEUMBIGA1UEChMLUGFwcHkgUHJveHkxFDASBgNVBAMTC1BhcHB5IFByb3h5\n' + 'MB4XDTE1MTEyMDIxMTEzOVoXDTI1MTExNzIxMTEzOVowYDELMAkGA1UEBhMCVVMx\n' + 'ETAPBgNVBAgTCE1pY2hpZ2FuMRIwEAYDVQQHEwlBbm4gQXJib3IxFDASBgNVBAoT\n' + 'C1BhcHB5IFByb3h5MRQwEgYDVQQDEwtQYXBweSBQcm94eTCCASIwDQYJKoZIhvcN\n' + 'AQEBBQADggEPADCCAQoCggEBAMCgKWthQQHuUzTNBoqRdlUbZ3UG72FXy6unHSA1\n' + 'cxvJnabfFv6wpmO78Uc+5Z6cDgpo3mBFRP6gt++2cXohrSOlE1adfQXKf+Kt2DUF\n' + 'YYmfuTuYnYPQ1dlBzOfb7HFjTnn3018Zao0oJjKOFLA+xQr6wYmqLtpIN2VL3tlO\n' + 'OtBVNMWwLT6RK7iVLl+xZfGqsotroCjxbtptVE4kdrOH9zEzhQqmBZT4TrIPijhm\n' + 'Adj5IxNVSH8g4zwO45XIsRa3Higs2IsyWlZPerLggyk4XpW5TokXYcZXXdKgl+Gw\n' + 'tew9FstVMCdm8lxnC2AObo18pqlTxbyiWQNXUF9hAQxI1fsCAwEAAaNFMEMwEgYD\n' + 'VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNo5o+5e\n' + 'a0sNMlW/75VgGJCv2AcJMA0GCSqGSIb3DQEBCwUAA4IBAQBdJDhxbmoEe27bD8me\n' + 'YTcLGjs/StKkSil7rLbX+tBCwtkm5UEEejBuAhKk2FuAXW8yR1FqKJSZwVCAocBT\n' + 'Bo/+97Ee+h7ywrRFhATEr9D/TbbHKOjCjDzOMl9yLZa2DKErZjbI30ZD6NafWS/X\n' + 'hx5X1cGohHcVVzT4jIgUEU70vvYfNn8CTZm4oJ7qqRe/uQPUYy0rwvbd60oprtGg\n' + 'jNv1H5R4ODHUMBXAI9H7ft9cWrd0fBQjxhoj8pvgJXEZ52flXSqQc7qHLg1wO/zC\n' + 'RUgpTcNAb2qCssBKbj+c1vKEPRUJfw6UYb0s1462rQNc8BgZiKaNbwokFmkAnjUg\n' + 'AvnX\n' + '-----END CERTIFICATE-----') + return (ca_key, private_key) + +def mock_config(mocker, http_config=None, socks_config=None): + # Mock config + mock_config = config.PappyConfig() + mock_config.socks_proxy = socks_config + mock_config.http_proxy = http_config + mock_session = pappy.PappySession(mock_config) + mocker.patch.object(pappy, 'session', new=mock_session) + mocker.patch("pappyproxy.proxy.load_certs_from_dir", new=mock_generate_cert) + From 1f9a28e3b05f5f287317f3ac3f25ff40883e412d Mon Sep 17 00:00:00 2001 From: zk-ncc Date: Wed, 4 Jan 2017 15:03:12 -0600 Subject: [PATCH 2/2] fix plugin.plugin_by_name() --- pappyproxy/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pappyproxy/plugin.py b/pappyproxy/plugin.py index 5b5a98e..b09e7f7 100644 --- a/pappyproxy/plugin.py +++ b/pappyproxy/plugin.py @@ -74,8 +74,8 @@ def plugin_by_name(name): plugin you would call ``plugin_by_name('bar').foo()``. """ import pappyproxy.pappy - if name in pappyproxy.pappy.plugin_loader.plugins_by_name: - return pappyproxy.pappy.plugin_loader.plugins_by_name[name].source + if name in pappyproxy.pappy.session.plugin_loader.plugins_by_name: + return pappyproxy.pappy.session.plugin_loader.plugins_by_name[name].source else: raise PappyException('No plugin with name %s is loaded' % name)