Example #1
0
    def test_man_in_the_middle_plugin(self) -> None:
        request = build_http_request(b'GET',
                                     b'/',
                                     headers={
                                         b'Host': b'uni.corn',
                                     })
        self.client_ssl_connection.recv.return_value = request

        # Client read
        self.protocol_handler.run_once()
        self.server.queue.assert_called_once_with(request)

        # Server write
        self.protocol_handler.run_once()
        self.server.flush.assert_called_once()

        # Server read
        self.server.recv.return_value = \
            build_http_response(
                httpStatusCodes.OK,
                reason=b'OK', body=b'Original Response From Upstream')
        self.protocol_handler.run_once()
        self.assertEqual(
            self.protocol_handler.client.buffer[0].tobytes(),
            build_http_response(httpStatusCodes.OK,
                                reason=b'OK',
                                body=b'Hello from man in the middle'))
 def test_build_response(self) -> None:
     self.assertEqual(
         build_http_response(200,
                             reason=b'OK',
                             protocol_version=b'HTTP/1.1'),
         CRLF.join([b'HTTP/1.1 200 OK', CRLF]))
     self.assertEqual(
         build_http_response(200,
                             reason=b'OK',
                             protocol_version=b'HTTP/1.1',
                             headers={b'key': b'value'}),
         CRLF.join([b'HTTP/1.1 200 OK', b'key: value', CRLF]))
 def test_body_response(self) -> None:
     e = HttpRequestRejected(
         status_code=httpStatusCodes.NOT_FOUND, reason=b'NOT FOUND',
         body=b'Nothing here')
     self.assertEqual(
         e.response(self.request),
         build_http_response(httpStatusCodes.NOT_FOUND, reason=b'NOT FOUND', body=b'Nothing here'))
Example #4
0
    async def test_filter_by_upstream_host_plugin(self) -> None:
        request = build_http_request(
            b'GET',
            b'http://facebook.com/',
            headers={
                b'Host': b'facebook.com',
            },
        )
        self._conn.recv.return_value = request
        self.mock_selector.return_value.select.side_effect = [
            [(
                selectors.SelectorKey(
                    fileobj=self._conn.fileno(),
                    fd=self._conn.fileno(),
                    events=selectors.EVENT_READ,
                    data=None,
                ),
                selectors.EVENT_READ,
            )],
        ]
        await self.protocol_handler._run_once()

        self.mock_server_conn.assert_not_called()
        self.assertEqual(
            self.protocol_handler.work.buffer[0],
            build_http_response(
                status_code=httpStatusCodes.I_AM_A_TEAPOT,
                reason=b'I\'m a tea pot',
                conn_close=True,
            ),
        )
Example #5
0
    async def test_shortlink_plugin(self) -> None:
        request = build_http_request(
            b'GET',
            b'http://t/',
            headers={
                b'Host': b't',
            },
        )
        self._conn.recv.return_value = request

        self.mock_selector.return_value.select.side_effect = [
            [(
                selectors.SelectorKey(
                    fileobj=self._conn.fileno(),
                    fd=self._conn.fileno(),
                    events=selectors.EVENT_READ,
                    data=None,
                ),
                selectors.EVENT_READ,
            )],
        ]

        await self.protocol_handler._run_once()
        self.assertEqual(
            self.protocol_handler.work.buffer[0].tobytes(),
            build_http_response(
                status_code=httpStatusCodes.SEE_OTHER,
                reason=b'See Other',
                headers={
                    b'Location': b'http://twitter.com/',
                },
                conn_close=True,
            ),
        )
Example #6
0
    def test_proposed_rest_api_plugin(self,
                                      mock_server_conn: mock.Mock) -> None:
        path = b'/v1/users/'
        self._conn.recv.return_value = build_http_request(
            b'GET',
            b'http://%s%s' % (ProposedRestApiPlugin.API_SERVER, path),
            headers={
                b'Host': ProposedRestApiPlugin.API_SERVER,
            })
        self.mock_selector.return_value.select.side_effect = [
            [(selectors.SelectorKey(fileobj=self._conn,
                                    fd=self._conn.fileno,
                                    events=selectors.EVENT_READ,
                                    data=None), selectors.EVENT_READ)],
        ]
        self.protocol_handler.run_once()

        mock_server_conn.assert_not_called()
        self.assertEqual(
            self.protocol_handler.client.buffer[0].tobytes(),
            build_http_response(
                httpStatusCodes.OK,
                reason=b'OK',
                headers={b'Content-Type': b'application/json'},
                body=bytes_(
                    json.dumps(ProposedRestApiPlugin.REST_API_SPEC[path]))))
Example #7
0
    async def test_filter_by_url_regex_plugin(self) -> None:
        request = build_http_request(
            b'GET',
            b'http://www.facebook.com/tr/',
            headers={
                b'Host': b'www.facebook.com',
            },
        )
        self._conn.recv.return_value = request
        self.mock_selector.return_value.select.side_effect = [
            [(
                selectors.SelectorKey(
                    fileobj=self._conn.fileno(),
                    fd=self._conn.fileno(),
                    events=selectors.EVENT_READ,
                    data=None,
                ),
                selectors.EVENT_READ,
            )],
        ]
        await self.protocol_handler._run_once()

        self.assertEqual(
            self.protocol_handler.work.buffer[0],
            build_http_response(
                status_code=httpStatusCodes.NOT_FOUND,
                reason=b'Blocked',
                conn_close=True,
            ),
        )
Example #8
0
 def handle_mc_listmaps(self):
     # A list of maps has been requested, return an empty list
     self.client.queue(
         memoryview(
             build_http_response(status_code=200,
                                 body=b'-;-;-;-;-',
                                 headers={b'Connection': b'close'})))
Example #9
0
    def test_static_web_server_serves(self, mock_fromfd: mock.Mock,
                                      mock_selector: mock.Mock) -> None:
        # Setup a static directory
        static_server_dir = os.path.join(tempfile.gettempdir(), 'static')
        index_file_path = os.path.join(static_server_dir, 'index.html')
        html_file_content = b'''<html><head></head><body><h1>Proxy.py Testing</h1></body></html>'''
        os.makedirs(static_server_dir, exist_ok=True)
        with open(index_file_path, 'wb') as f:
            f.write(html_file_content)

        self._conn = mock_fromfd.return_value
        self._conn.recv.return_value = build_http_request(
            b'GET', b'/index.html')

        mock_selector.return_value.select.side_effect = [
            [(selectors.SelectorKey(fileobj=self._conn,
                                    fd=self._conn.fileno,
                                    events=selectors.EVENT_READ,
                                    data=None), selectors.EVENT_READ)],
            [(selectors.SelectorKey(fileobj=self._conn,
                                    fd=self._conn.fileno,
                                    events=selectors.EVENT_WRITE,
                                    data=None), selectors.EVENT_WRITE)],
        ]

        flags = Flags(enable_static_server=True,
                      static_server_dir=static_server_dir)
        flags.plugins = Flags.load_plugins(
            b'proxy.http.proxy.HttpProxyPlugin,proxy.http.server.HttpWebServerPlugin'
        )

        self.protocol_handler = HttpProtocolHandler(TcpClientConnection(
            self._conn, self._addr),
                                                    flags=flags)
        self.protocol_handler.initialize()

        self.protocol_handler.run_once()
        self.protocol_handler.run_once()

        self.assertEqual(mock_selector.return_value.select.call_count, 2)
        self.assertEqual(self._conn.send.call_count, 1)
        encoded_html_file_content = gzip.compress(html_file_content)
        self.assertEqual(
            self._conn.send.call_args[0][0],
            build_http_response(200,
                                reason=b'OK',
                                headers={
                                    b'Content-Type':
                                    b'text/html',
                                    b'Cache-Control':
                                    b'max-age=86400',
                                    b'Content-Encoding':
                                    b'gzip',
                                    b'Connection':
                                    b'close',
                                    b'Content-Length':
                                    bytes_(len(encoded_html_file_content)),
                                },
                                body=encoded_html_file_content))
 def test_pipelined_response_parse(self) -> None:
     response = build_http_response(
         httpStatusCodes.OK,
         reason=b'OK',
         headers={b'Content-Length': b'15'},
         body=b'{"key":"value"}',
     )
     self.assert_pipeline_response(response)
Example #11
0
 def handle_mc_auth(self):
     # Old Minecraft wants to authenticate, it is enough to reply with a 0
     print('Authentication requested')
     self.client.queue(
         memoryview(
             build_http_response(status_code=200,
                                 body=b'0',
                                 headers={b'Connection': b'close'})))
Example #12
0
 def handle_client_request(self, request: HttpParser) -> Optional[HttpParser]:
     if request.host != self.API_SERVER:
         return request
     assert request.path
     if request.path in self.REST_API_SPEC:
         self.client.queue(memoryview(build_http_response(
             httpStatusCodes.OK,
             reason=b'OK',
             headers={b'Content-Type': b'application/json'},
             body=bytes_(json.dumps(
                 self.REST_API_SPEC[request.path]))
         )))
     else:
         self.client.queue(memoryview(build_http_response(
             httpStatusCodes.NOT_FOUND,
             reason=b'NOT FOUND', body=b'Not Found'
         )))
     return None
 def test_pipelined_chunked_response_parse(self) -> None:
     response = build_http_response(
         httpStatusCodes.OK,
         reason=b'OK',
         headers={
             b'Transfer-Encoding': b'chunked',
             b'Content-Type': b'application/json',
         },
         body=b'f\r\n{"key":"value"}\r\n0\r\n\r\n')
     self.assert_pipeline_response(response)
class HttpsConnectTunnelHandler(BaseTcpTunnelHandler):
    """A https CONNECT tunnel."""

    PROXY_TUNNEL_ESTABLISHED_RESPONSE_PKT = memoryview(
        build_http_response(httpStatusCodes.OK,
                            reason=b'Connection established'))

    PROXY_TUNNEL_UNSUPPORTED_SCHEME = memoryview(
        build_http_response(httpStatusCodes.BAD_REQUEST,
                            headers={b'Connection': b'close'},
                            reason=b'Unsupported protocol scheme'))

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        super().__init__(*args, **kwargs)

    def handle_data(self, data: memoryview) -> Optional[bool]:
        # Queue for upstream if connection has been established
        if self.upstream and self.upstream._conn is not None:
            self.upstream.queue(data)
            return None

        # Parse client request
        self.request.parse(data)

        # Drop the request if not a CONNECT request
        if self.request.method != httpMethods.CONNECT:
            self.client.queue(
                HttpsConnectTunnelHandler.PROXY_TUNNEL_UNSUPPORTED_SCHEME)
            return True

        # CONNECT requests are short and we need not worry about
        # receiving partial request bodies here.
        assert self.request.state == httpParserStates.COMPLETE

        # Establish connection with upstream
        self.connect_upstream()

        # Queue tunnel established response to client
        self.client.queue(
            HttpsConnectTunnelHandler.PROXY_TUNNEL_ESTABLISHED_RESPONSE_PKT)

        return None
 def test_build_response_adds_content_length_header(self) -> None:
     body = b'Hello world!!!'
     self.assertEqual(
         build_http_response(200,
                             reason=b'OK',
                             protocol_version=b'HTTP/1.1',
                             headers={b'key': b'value'},
                             body=body),
         CRLF.join([
             b'HTTP/1.1 200 OK', b'key: value',
             b'Content-Length: ' + bytes_(len(body)), CRLF
         ]) + body)
Example #16
0
 def test_pac_file_served_from_buffer(
         self, mock_fromfd: mock.Mock, mock_selector: mock.Mock) -> None:
     self._conn = mock_fromfd.return_value
     self.mock_selector_for_client_read(mock_selector)
     pac_file_content = b'function FindProxyForURL(url, host) { return "PROXY localhost:8899; DIRECT"; }'
     self.init_and_make_pac_file_request(text_(pac_file_content))
     self.protocol_handler.run_once()
     self.assertEqual(
         self.protocol_handler.request.state,
         httpParserStates.COMPLETE)
     self._conn.send.called_once_with(build_http_response(
         200, reason=b'OK', headers={
             b'Content-Type': b'application/x-ns-proxy-autoconfig',
             b'Connection': b'close'
         }, body=pac_file_content
     ))
Example #17
0
 async def test_pac_file_served_from_disk(self) -> None:
     await self.protocol_handler._run_once()
     self.assertEqual(
         self.protocol_handler.request.state,
         httpParserStates.COMPLETE,
     )
     self._conn.send.called_once_with(
         build_http_response(
             200,
             reason=b'OK',
             headers={
                 b'Content-Type': b'application/x-ns-proxy-autoconfig',
             },
             body=self.expected_response,
             conn_close=True,
         ),
     )
Example #18
0
 def test_with_proxy(self) -> None:
     """Makes a HTTP request to in-build web server via proxy server."""
     with socket_connection(('localhost', self.PROXY_PORT)) as conn:
         conn.send(
             build_http_request(httpMethods.GET,
                                b'http://localhost:%d/' % self.PROXY_PORT,
                                headers={
                                    b'Host':
                                    b'localhost:%d' % self.PROXY_PORT,
                                }))
         response = conn.recv(DEFAULT_CLIENT_RECVBUF_SIZE)
     self.assertEqual(
         response,
         build_http_response(httpStatusCodes.NOT_FOUND,
                             reason=b'NOT FOUND',
                             headers={
                                 b'Server': PROXY_AGENT_HEADER_VALUE,
                                 b'Connection': b'close'
                             }))
Example #19
0
 def test_pac_file_served_from_disk(self, mock_fromfd: mock.Mock,
                                    mock_selector: mock.Mock) -> None:
     pac_file = os.path.join(os.path.dirname(PROXY_PY_DIR), 'helper',
                             'proxy.pac')
     self._conn = mock_fromfd.return_value
     self.mock_selector_for_client_read(mock_selector)
     self.init_and_make_pac_file_request(pac_file)
     self.protocol_handler.run_once()
     self.assertEqual(self.protocol_handler.request.state,
                      httpParserStates.COMPLETE)
     with open(pac_file, 'rb') as f:
         self._conn.send.called_once_with(
             build_http_response(200,
                                 reason=b'OK',
                                 headers={
                                     b'Content-Type':
                                     b'application/x-ns-proxy-autoconfig',
                                     b'Connection': b'close'
                                 },
                                 body=f.read()))
Example #20
0
    def handle_mc_res(self, request: HttpParser):
        # Resources have been requested
        print('Resources requested: {}'.format(request.path))
        if request.path == b'/resources/':
            try:
                new_resources = get_mc_resources()

                resources_old_format = convert_mc_resources_to_old_format(
                    new_resources)
                self.client.queue(
                    memoryview(
                        build_http_response(status_code=200,
                                            body=resources_old_format.encode(),
                                            headers={b'Connection':
                                                     b'close'})))
            except RuntimeError as e:
                print('RuntimeError while getting resources: {}'.format(e))
            except Exception as e:
                print('Exception while getting resources: {}'.format(e))
        else:
            print(
                'Individual resource passthrough is not currently supported!')
    def test_filter_by_url_regex_plugin(self,
                                        mock_server_conn: mock.Mock) -> None:
        request = build_http_request(b'GET',
                                     b'http://www.facebook.com/tr/',
                                     headers={
                                         b'Host': b'www.facebook.com',
                                     })
        self._conn.recv.return_value = request
        self.mock_selector.return_value.select.side_effect = [
            [(selectors.SelectorKey(fileobj=self._conn,
                                    fd=self._conn.fileno,
                                    events=selectors.EVENT_READ,
                                    data=None), selectors.EVENT_READ)],
        ]
        self.protocol_handler.run_once()

        self.assertEqual(
            self.protocol_handler.client.buffer[0].tobytes(),
            build_http_response(
                status_code=httpStatusCodes.NOT_FOUND,
                reason=b'Blocked',
                headers={b'Connection': b'close'},
            ))
Example #22
0
    def handle_mc_skin(self, request: HttpParser, cloak: bool, query: bool):
        # A skin has been requested, get the username from the URL path
        if not cloak:
            print('Skin requested: {}'.format(request.path.decode()))
            subtype = 'SKIN'
        else:
            print('Cloak requested: {}'.format(request.path.decode()))
            subtype = 'CAPE'
        # 'not-query' form is a static file (or an imitation of one) named after the user, 'query' form ends in ?user=<username>
        if not query:
            username_start = request.path.rfind(b'/') + 1
            username_end = request.path.find(b'.')
            username = request.path[username_start:username_end]
        else:
            username_start = request.path.rfind(b'=') + 1
            username = request.path[username_start:]

        username = username.decode()

        print('Got player: {}'.format(username))

        try:
            # Try to get the player UUID from their username
            player_uuid = get_mc_uuid_from_username(username)
            print('Got player UUID: {}'.format(player_uuid))

            # Then, we can grab their skin data and send it as the response
            skin = get_mc_player_skin_from_uuid(player_uuid, subtype)
            self.client.queue(
                memoryview(
                    build_http_response(status_code=200,
                                        body=skin,
                                        headers={b'Connection': b'close'})))
        except RuntimeError as e:
            print('RuntimeError while getting player skin: {}'.format(e))
        except Exception as e:
            print('Exception while getting player skin: {}'.format(e))
Example #23
0
    def test_filter_by_upstream_host_plugin(
            self, mock_server_conn: mock.Mock) -> None:
        request = build_http_request(b'GET',
                                     b'http://google.com/',
                                     headers={
                                         b'Host': b'google.com',
                                     })
        self._conn.recv.return_value = request
        self.mock_selector.return_value.select.side_effect = [
            [(selectors.SelectorKey(fileobj=self._conn,
                                    fd=self._conn.fileno,
                                    events=selectors.EVENT_READ,
                                    data=None), selectors.EVENT_READ)],
        ]
        self.protocol_handler.run_once()

        mock_server_conn.assert_not_called()
        self.assertEqual(
            self.protocol_handler.client.buffer[0].tobytes(),
            build_http_response(
                status_code=httpStatusCodes.I_AM_A_TEAPOT,
                reason=b'I\'m a tea pot',
                headers={b'Connection': b'close'},
            ))
Example #24
0
    async def test_man_in_the_middle_plugin(self) -> None:
        request = build_http_request(
            b'GET',
            b'http://super.secure/',
            headers={
                b'Host': b'super.secure',
            },
            no_ua=True,
        )
        self._conn.recv.return_value = request

        server = self.mock_server_conn.return_value
        server.connect.return_value = True

        def has_buffer() -> bool:
            return cast(bool, server.queue.called)

        def closed() -> bool:
            return not server.connect.called

        server.has_buffer.side_effect = has_buffer
        type(server).closed = mock.PropertyMock(side_effect=closed)

        self.mock_selector.return_value.select.side_effect = [
            [(
                selectors.SelectorKey(
                    fileobj=self._conn.fileno(),
                    fd=self._conn.fileno(),
                    events=selectors.EVENT_READ,
                    data=None,
                ),
                selectors.EVENT_READ,
            )],
            [(
                selectors.SelectorKey(
                    fileobj=server.connection.fileno(),
                    fd=server.connection.fileno(),
                    events=selectors.EVENT_WRITE,
                    data=None,
                ),
                selectors.EVENT_WRITE,
            )],
            [(
                selectors.SelectorKey(
                    fileobj=server.connection.fileno(),
                    fd=server.connection.fileno(),
                    events=selectors.EVENT_READ,
                    data=None,
                ),
                selectors.EVENT_READ,
            )],
        ]

        # Client read
        await self.protocol_handler._run_once()
        self.mock_server_conn.assert_called_with(
            'super.secure',
            DEFAULT_HTTP_PORT,
        )
        server.connect.assert_called_once()
        queued_request = \
            build_http_request(
                b'GET', b'/',
                headers={
                    b'Host': b'super.secure',
                    b'Via': b'1.1 %s' % PROXY_AGENT_HEADER_VALUE,
                },
                no_ua=True,
            )
        server.queue.assert_called_once()
        print(server.queue.call_args_list[0][0][0].tobytes())
        print(queued_request)
        self.assertEqual(server.queue.call_args_list[0][0][0], queued_request)

        # Server write
        await self.protocol_handler._run_once()
        server.flush.assert_called_once()

        # Server read
        server.recv.return_value = \
            build_http_response(
                httpStatusCodes.OK,
                reason=b'OK',
                body=b'Original Response From Upstream',
            )
        await self.protocol_handler._run_once()
        response = HttpParser(httpParserTypes.RESPONSE_PARSER)
        response.parse(self.protocol_handler.work.buffer[0])
        assert response.body
        self.assertEqual(
            gzip.decompress(response.body),
            b'Hello from man in the middle',
        )
Example #25
0
    def test_man_in_the_middle_plugin(self,
                                      mock_server_conn: mock.Mock) -> None:
        request = build_http_request(b'GET',
                                     b'http://super.secure/',
                                     headers={
                                         b'Host': b'super.secure',
                                     })
        self._conn.recv.return_value = request

        server = mock_server_conn.return_value
        server.connect.return_value = True

        def has_buffer() -> bool:
            return cast(bool, server.queue.called)

        def closed() -> bool:
            return not server.connect.called

        server.has_buffer.side_effect = has_buffer
        type(server).closed = mock.PropertyMock(side_effect=closed)

        self.mock_selector.return_value.select.side_effect = [
            [(selectors.SelectorKey(fileobj=self._conn,
                                    fd=self._conn.fileno,
                                    events=selectors.EVENT_READ,
                                    data=None), selectors.EVENT_READ)],
            [(selectors.SelectorKey(fileobj=server.connection,
                                    fd=server.connection.fileno,
                                    events=selectors.EVENT_WRITE,
                                    data=None), selectors.EVENT_WRITE)],
            [(selectors.SelectorKey(fileobj=server.connection,
                                    fd=server.connection.fileno,
                                    events=selectors.EVENT_READ,
                                    data=None), selectors.EVENT_READ)],
        ]

        # Client read
        self.protocol_handler.run_once()
        mock_server_conn.assert_called_with('super.secure', DEFAULT_HTTP_PORT)
        server.connect.assert_called_once()
        queued_request = \
            build_http_request(
                b'GET', b'/',
                headers={
                    b'Host': b'super.secure',
                    b'Via': b'1.1 %s' % PROXY_AGENT_HEADER_VALUE
                }
            )
        server.queue.assert_called_once_with(queued_request)

        # Server write
        self.protocol_handler.run_once()
        server.flush.assert_called_once()

        # Server read
        server.recv.return_value = \
            build_http_response(
                httpStatusCodes.OK,
                reason=b'OK', body=b'Original Response From Upstream')
        self.protocol_handler.run_once()
        self.assertEqual(
            self.protocol_handler.client.buffer[0].tobytes(),
            build_http_response(httpStatusCodes.OK,
                                reason=b'OK',
                                body=b'Hello from man in the middle'))