def test_upgrade(tctx):
    """Test a HTTP -> WebSocket upgrade"""
    tctx.server.address = ("example.com", 80)
    tctx.server.state = ConnectionState.OPEN
    flow = Placeholder(HTTPFlow)
    assert (
        Playbook(http.HttpLayer(tctx, HTTPMode.transparent)) >> DataReceived(
            tctx.client, b"GET / HTTP/1.1\r\n"
            b"Connection: upgrade\r\n"
            b"Upgrade: websocket\r\n"
            b"Sec-WebSocket-Version: 13\r\n"
            b"\r\n") << http.HttpRequestHeadersHook(flow) >> reply() <<
        http.HttpRequestHook(flow) >> reply() << SendData(
            tctx.server, b"GET / HTTP/1.1\r\n"
            b"Connection: upgrade\r\n"
            b"Upgrade: websocket\r\n"
            b"Sec-WebSocket-Version: 13\r\n"
            b"\r\n") >> DataReceived(
                tctx.server, b"HTTP/1.1 101 Switching Protocols\r\n"
                b"Upgrade: websocket\r\n"
                b"Connection: Upgrade\r\n"
                b"\r\n") << http.HttpResponseHeadersHook(flow) >> reply() <<
        http.HttpResponseHook(flow) >> reply() << SendData(
            tctx.client, b"HTTP/1.1 101 Switching Protocols\r\n"
            b"Upgrade: websocket\r\n"
            b"Connection: Upgrade\r\n"
            b"\r\n") << websocket.WebsocketStartHook(flow) >> reply() >>
        DataReceived(tctx.client, masked_bytes(b"\x81\x0bhello world")) <<
        websocket.WebsocketMessageHook(flow) >> reply() << SendData(
            tctx.server, masked(b"\x81\x0bhello world")) >> DataReceived(
                tctx.server, b"\x82\nhello back") <<
        websocket.WebsocketMessageHook(flow) >> reply() << SendData(
            tctx.client, b"\x82\nhello back") >> DataReceived(
                tctx.client, masked_bytes(b"\x81\x0bhello again")) <<
        websocket.WebsocketMessageHook(flow) >> reply() << SendData(
            tctx.server, masked(b"\x81\x0bhello again")))
    assert len(flow().websocket.messages) == 3
    assert flow().websocket.messages[0].content == b"hello world"
    assert flow().websocket.messages[0].from_client
    assert flow().websocket.messages[0].type == Opcode.TEXT
    assert flow().websocket.messages[1].content == b"hello back"
    assert flow().websocket.messages[1].from_client is False
    assert flow().websocket.messages[1].type == Opcode.BINARY
Beispiel #2
0
def test_http_proxy(tctx):
    """Test a simple HTTP GET / request"""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    assert (Playbook(http.HttpLayer(tctx, HTTPMode.regular)) >> DataReceived(
        tctx.client,
        b"GET http://example.com/foo?hello=1 HTTP/1.1\r\nHost: example.com\r\n\r\n"
    ) << http.HttpRequestHeadersHook(flow) >> reply() << http.HttpRequestHook(
        flow) >> reply() << OpenConnection(server) >> reply(None) << SendData(
            server, b"GET /foo?hello=1 HTTP/1.1\r\nHost: example.com\r\n\r\n")
            >> DataReceived(
                server,
                b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World") <<
            http.HttpResponseHeadersHook(flow) >> reply() >> DataReceived(
                server, b"!") << http.HttpResponseHook(flow) >> reply() <<
            SendData(
                tctx.client,
                b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!"))
    assert server().address == ("example.com", 80)
Beispiel #3
0
def test_socks5_auth_fail(client_greeting: bytes, server_choice: bytes,
                          client_auth: bytes, err: bytes, msg: str,
                          tctx: Context):
    ProxyAuth().load(tctx.options)
    tctx.options.proxyauth = "user:password"
    playbook = (Playbook(modes.Socks5Proxy(tctx), logs=True) >> DataReceived(
        tctx.client, client_greeting))
    if server_choice is None:
        playbook << SendData(tctx.client, err)
    else:
        playbook << SendData(tctx.client, server_choice)
        playbook >> DataReceived(tctx.client, client_auth)
        playbook << modes.Socks5AuthHook(Placeholder(modes.Socks5AuthData))
        playbook >> reply()
        playbook << SendData(tctx.client, err)

    playbook << CloseConnection(tctx.client)
    playbook << Log(msg)
    assert playbook
Beispiel #4
0
def h2_client(tctx: Context) -> Tuple[h2.connection.H2Connection, Playbook]:
    tctx.client.alpn = b"h2"

    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular))
    conn = h2.connection.H2Connection()
    conn.initiate_connection()

    server_preamble = Placeholder(bytes)
    assert (playbook << SendData(tctx.client, server_preamble))
    assert event_types(conn.receive_data(
        server_preamble())) == [h2.events.RemoteSettingsChanged]

    settings_ack = Placeholder(bytes)
    assert (playbook >> DataReceived(tctx.client, conn.data_to_send()) <<
            SendData(tctx.client, settings_ack))
    assert event_types(conn.receive_data(
        settings_ack())) == [h2.events.SettingsAcknowledged]

    return conn, playbook
Beispiel #5
0
def test_tunnel_openconnection_error(tctx: Context):
    server = Server(("proxy", 1234))

    tl = TTunnelLayer(tctx, server, tctx.server)
    tl.child_layer = TChildLayer(tctx)

    playbook = Playbook(tl, logs=True)
    assert (
            playbook
            << Log("Got start. Server state: CLOSED")
            >> DataReceived(tctx.client, b"open")
            << OpenConnection(server)
    )
    assert tl.tunnel_state is tunnel.TunnelState.ESTABLISHING
    assert (
            playbook
            >> reply("IPoAC packet dropped.")
            << Log("Opened: err='IPoAC packet dropped.'. Server state: CLOSED")
    )
    assert tl.tunnel_state is tunnel.TunnelState.CLOSED
Beispiel #6
0
 def test_no_data_on_closed_stream(self, tctx):
     frame_factory = FrameFactory()
     req = Request.make("GET", "http://example.com/")
     resp = {
         ":status": 200
     }
     assert (
             Playbook(Http2Client(tctx))
             << SendData(tctx.server, Placeholder(bytes))  # preamble + initial settings frame
             >> DataReceived(tctx.server, frame_factory.build_settings_frame({}, ack=True).serialize())
             >> http.RequestHeaders(1, req, end_stream=True)
             << SendData(tctx.server, b"\x00\x00\x06\x01\x05\x00\x00\x00\x01\x82\x86\x84\\\x81\x07")
             >> http.RequestEndOfMessage(1)
             >> DataReceived(tctx.server, frame_factory.build_headers_frame(resp).serialize())
             << http.ReceiveHttp(Placeholder(http.ResponseHeaders))
             >> http.RequestProtocolError(1, "cancelled", code=status_codes.CLIENT_CLOSED_REQUEST)
             << SendData(tctx.server, frame_factory.build_rst_stream_frame(1, ErrorCodes.CANCEL).serialize())
             >> DataReceived(tctx.server, frame_factory.build_data_frame(b"foo").serialize())
             << SendData(tctx.server, frame_factory.build_rst_stream_frame(1, ErrorCodes.STREAM_CLOSED).serialize())
     )  # important: no ResponseData event here!
Beispiel #7
0
 def test_connect(self, tctx, pipeline):
     playbook = Playbook(Http1Server(tctx))
     (
             playbook
             >> DataReceived(tctx.client,
                             b"CONNECT example.com:443 HTTP/1.1\r\n"
                             b"content-length: 0\r\n"
                             b"\r\n"
                             + (b"some plain tcp" if pipeline else b""))
             << ReceiveHttp(Placeholder(RequestHeaders))
             # << ReceiveHttp(RequestEndOfMessage(1))
             >> ResponseHeaders(1, http.Response.make(200))
             << SendData(tctx.client, b'HTTP/1.1 200 OK\r\ncontent-length: 0\r\n\r\n')
             >> ResponseEndOfMessage(1)
     )
     if not pipeline:
         playbook >> DataReceived(tctx.client, b"some plain tcp")
     assert (playbook
             << ReceiveHttp(RequestData(1, b"some plain tcp"))
             )
Beispiel #8
0
    def test_upgrade_denied(self, tctx):
        req = http.Request.make("GET", "http://example.com/ws", headers={
            "Connection": "Upgrade",
            "Upgrade": "websocket",
        })
        resp = Placeholder(ResponseHeaders)

        playbook = Playbook(Http1Client(tctx))
        assert (
                playbook
                >> RequestHeaders(1, req, True)
                << SendData(tctx.server,
                            b"GET /ws HTTP/1.1\r\nConnection: Upgrade\r\nUpgrade: websocket\r\ncontent-length: 0\r\n\r\n")
                >> RequestEndOfMessage(1)
                >> DataReceived(tctx.server, b"HTTP/1.1 200 Ok\r\ncontent-length: 0\r\n\r\n")
                << ReceiveHttp(resp)
                << ReceiveHttp(ResponseEndOfMessage(1))
                >> RequestHeaders(3, req, True)
                << SendData(tctx.server, Placeholder(bytes))
        )
def test_upgrade_streamed(tctx):
    """If the HTTP response is streamed, we may get early data from the client."""
    tctx.server.address = ("example.com", 80)
    tctx.server.state = ConnectionState.OPEN
    flow = Placeholder(HTTPFlow)

    def enable_streaming(flow: HTTPFlow):
        flow.response.stream = True

    assert (
        Playbook(http.HttpLayer(tctx, HTTPMode.transparent)) >> DataReceived(
            tctx.client, b"GET / HTTP/1.1\r\n"
            b"Connection: upgrade\r\n"
            b"Upgrade: websocket\r\n"
            b"Sec-WebSocket-Version: 13\r\n"
            b"\r\n") << http.HttpRequestHeadersHook(flow) >> reply() <<
        http.HttpRequestHook(flow) >> reply() << SendData(
            tctx.server, b"GET / HTTP/1.1\r\n"
            b"Connection: upgrade\r\n"
            b"Upgrade: websocket\r\n"
            b"Sec-WebSocket-Version: 13\r\n"
            b"\r\n") >> DataReceived(
                tctx.server, b"HTTP/1.1 101 Switching Protocols\r\n"
                b"Upgrade: websocket\r\n"
                b"Connection: Upgrade\r\n"
                b"\r\n") << http.HttpResponseHeadersHook(flow) >>
        reply(side_effect=enable_streaming) << SendData(
            tctx.client, b"HTTP/1.1 101 Switching Protocols\r\n"
            b"Upgrade: websocket\r\n"
            b"Connection: Upgrade\r\n"
            b"\r\n") << http.HttpResponseHook(flow) >> DataReceived(
                tctx.client, masked_bytes(b"\x81\x0bhello world"))  # early !!
        >> reply(to=-2) << websocket.WebsocketStartHook(flow) >> reply() <<
        websocket.WebsocketMessageHook(flow) >> reply() << SendData(
            tctx.server, masked(b"\x81\x0bhello world")) >> DataReceived(
                tctx.server, b"\x82\nhello back") <<
        websocket.WebsocketMessageHook(flow) >> reply() << SendData(
            tctx.client, b"\x82\nhello back") >> DataReceived(
                tctx.client, masked_bytes(b"\x81\x0bhello again")) <<
        websocket.WebsocketMessageHook(flow) >> reply() << SendData(
            tctx.server, masked(b"\x81\x0bhello again")))
Beispiel #10
0
def test_socks5_auth_success(client_greeting: bytes, server_choice: bytes,
                             client_auth: bytes, server_resp: bytes,
                             address: bytes, packed: bytes, tctx: Context):
    ProxyAuth().load(tctx.options)
    tctx.options.proxyauth = "user:password"
    server = Placeholder(Server)
    nextlayer = Placeholder(NextLayer)
    playbook = (Playbook(modes.Socks5Proxy(tctx), logs=True) >> DataReceived(
        tctx.client, client_greeting) << SendData(tctx.client, server_choice)
                >> DataReceived(tctx.client, client_auth) <<
                modes.Socks5AuthHook(Placeholder(modes.Socks5AuthData)) >>
                reply(side_effect=_valid_socks_auth) << SendData(
                    tctx.client, server_resp) >> DataReceived(
                        tctx.client,
                        b"\x05\x01\x00" + packed + b"\x12\x34applicationdata")
                << OpenConnection(server) >> reply(None) << SendData(
                    tctx.client, b"\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00")
                << NextLayerHook(nextlayer))
    assert playbook
    assert server().address == (address, 0x1234)
    assert nextlayer().data_client() == b"applicationdata"
Beispiel #11
0
    def test_upgrade(self, tctx):
        req = http.Request.make("GET",
                                "http://example.com/ws",
                                headers={
                                    "Connection": "Upgrade",
                                    "Upgrade": "websocket",
                                })
        resp = Placeholder(ResponseHeaders)

        playbook = Playbook(Http1Client(tctx))
        assert (playbook >> RequestHeaders(1, req, True) << SendData(
            tctx.server,
            b"GET /ws HTTP/1.1\r\nConnection: Upgrade\r\nUpgrade: websocket\r\ncontent-length: 0\r\n\r\n"
        ) >> RequestEndOfMessage(1) >> DataReceived(
            tctx.server,
            b"HTTP/1.1 101 Switching Protocols\r\ncontent-length: 0\r\n\r\nhello"
        ) << ReceiveHttp(resp) << ReceiveHttp(ResponseEndOfMessage(1)) <<
                ReceiveHttp(ResponseData(1, b"hello"))
                # no we can send plain data
                >> RequestData(1, b"some more websockets") << SendData(
                    tctx.server, b"some more websockets"))
Beispiel #12
0
 def test_simple(self, tctx, pipeline):
     hdrs1 = Placeholder(RequestHeaders)
     hdrs2 = Placeholder(RequestHeaders)
     req2 = (b"GET http://example.com/two HTTP/1.1\r\n"
             b"Host: example.com\r\n"
             b"\r\n")
     playbook = Playbook(Http1Server(tctx))
     (playbook >> DataReceived(
         tctx.client, b"POST http://example.com/one HTTP/1.1\r\n"
         b"Content-Length: 3\r\n"
         b"\r\n"
         b"abc" +
         (req2 if pipeline else b"")) << ReceiveHttp(hdrs1) << ReceiveHttp(
             RequestData(1, b"abc")) << ReceiveHttp(RequestEndOfMessage(1))
      >> ResponseHeaders(1, http.Response.make(200)) << SendData(
          tctx.client, b'HTTP/1.1 200 OK\r\ncontent-length: 0\r\n\r\n') >>
      ResponseEndOfMessage(1))
     if not pipeline:
         playbook >> DataReceived(tctx.client, req2)
     assert (playbook << ReceiveHttp(hdrs2) << ReceiveHttp(
         RequestEndOfMessage(3)))
Beispiel #13
0
def test_http_expect(tctx):
    """Test handling of a 'Expect: 100-continue' header."""
    server = Placeholder(Server)
    assert (
            Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)
            >> DataReceived(tctx.client, b"PUT http://example.com/large-file HTTP/1.1\r\n"
                                         b"Host: example.com\r\n"
                                         b"Content-Length: 15\r\n"
                                         b"Expect: 100-continue\r\n\r\n")
            << SendData(tctx.client, b"HTTP/1.1 100 Continue\r\n\r\n")
            >> DataReceived(tctx.client, b"lots of content")
            << OpenConnection(server)
            >> reply(None)
            << SendData(server, b"PUT /large-file HTTP/1.1\r\n"
                                b"Host: example.com\r\n"
                                b"Content-Length: 15\r\n\r\n"
                                b"lots of content")
            >> DataReceived(server, b"HTTP/1.1 201 Created\r\nContent-Length: 0\r\n\r\n")
            << SendData(tctx.client, b"HTTP/1.1 201 Created\r\nContent-Length: 0\r\n\r\n")
    )
    assert server().address == ("example.com", 80)
Beispiel #14
0
 def test_upgrade(self, tctx, pipeline):
     playbook = Playbook(Http1Server(tctx))
     (
             playbook
             >> DataReceived(tctx.client,
                             b"POST http://example.com/one HTTP/1.1\r\n"
                             b"Connection: Upgrade\r\n"
                             b"Upgrade: websocket\r\n"
                             b"\r\n"
                             + (b"some websockets" if pipeline else b""))
             << ReceiveHttp(Placeholder(RequestHeaders))
             << ReceiveHttp(RequestEndOfMessage(1))
             >> ResponseHeaders(1, http.Response.make(101))
             << SendData(tctx.client, b'HTTP/1.1 101 Switching Protocols\r\ncontent-length: 0\r\n\r\n')
             >> ResponseEndOfMessage(1)
     )
     if not pipeline:
         playbook >> DataReceived(tctx.client, b"some websockets")
     assert (playbook
             << ReceiveHttp(RequestData(1, b"some websockets"))
             )
Beispiel #15
0
def test_redirect(strategy, https_server, https_client, tctx, monkeypatch):
    """Test redirects between http:// and https:// in regular proxy mode."""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    tctx.options.connection_strategy = strategy
    p = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)

    if https_server:
        monkeypatch.setattr(tls, "ServerTLSLayer", tls.MockTLSLayer)

    def redirect(flow: HTTPFlow):
        if https_server:
            flow.request.url = "https://redirected.site/"
        else:
            flow.request.url = "http://redirected.site/"

    if https_client:
        p >> DataReceived(tctx.client, b"CONNECT example.com:80 HTTP/1.1\r\n\r\n")
        if strategy == "eager":
            p << OpenConnection(Placeholder())
            p >> reply(None)
        p << SendData(tctx.client, b'HTTP/1.1 200 Connection established\r\n\r\n')
        p >> DataReceived(tctx.client, b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
        p << layer.NextLayerHook(Placeholder())
        p >> reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.transparent))
    else:
        p >> DataReceived(tctx.client, b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n")
    p << http.HttpRequestHook(flow)
    p >> reply(side_effect=redirect)
    p << OpenConnection(server)
    p >> reply(None)
    p << SendData(server, b"GET / HTTP/1.1\r\nHost: redirected.site\r\n\r\n")
    p >> DataReceived(server, b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!")
    p << SendData(tctx.client, b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!")

    assert p
    if https_server:
        assert server().address == ("redirected.site", 443)
    else:
        assert server().address == ("redirected.site", 80)
Beispiel #16
0
def test_http_proxy_tcp(tctx, mode, close_first):
    """Test TCP over HTTP CONNECT."""
    server = Placeholder(Server)

    if mode == "upstream":
        tctx.options.mode = "upstream:http://proxy:8080"
        toplayer = http.HttpLayer(tctx, HTTPMode.upstream)
    else:
        tctx.options.mode = "regular"
        toplayer = http.HttpLayer(tctx, HTTPMode.regular)

    playbook = Playbook(toplayer, hooks=False)
    assert (playbook >> DataReceived(
        tctx.client, b"CONNECT example:443 HTTP/1.1\r\n\r\n") << SendData(
            tctx.client, b"HTTP/1.1 200 Connection established\r\n\r\n") >>
            DataReceived(tctx.client, b"this is not http") <<
            layer.NextLayerHook(Placeholder()) >>
            reply_next_layer(lambda ctx: TCPLayer(ctx, ignore=True)) <<
            OpenConnection(server))

    playbook >> reply(None)
    if mode == "upstream":
        playbook << SendData(server, b"CONNECT example:443 HTTP/1.1\r\n\r\n")
        playbook >> DataReceived(
            server, b"HTTP/1.1 200 Connection established\r\n\r\n")

    assert (playbook << SendData(server, b"this is not http") >> DataReceived(
        server, b"true that") << SendData(tctx.client, b"true that"))

    if mode == "regular":
        assert server().address == ("example", 443)
    else:
        assert server().address == ("proxy", 8080)

    if close_first == "client":
        a, b = tctx.client, server
    else:
        a, b = server, tctx.client
    assert (playbook >> ConnectionClosed(a) << CloseConnection(b) >>
            ConnectionClosed(b) << CloseConnection(a))
Beispiel #17
0
def test_upgrade(tctx, proto):
    """Test a HTTP -> WebSocket upgrade with different protocols enabled"""
    if proto != "websocket":
        tctx.options.websocket = False
    if proto != "tcp":
        tctx.options.rawtcp = False

    tctx.server.address = ("example.com", 80)
    tctx.server.state = ConnectionState.OPEN
    http_flow = Placeholder(HTTPFlow)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.transparent))
    (playbook >> DataReceived(
        tctx.client, b"GET / HTTP/1.1\r\n"
        b"Connection: upgrade\r\n"
        b"Upgrade: websocket\r\n"
        b"Sec-WebSocket-Version: 13\r\n"
        b"\r\n") << http.HttpRequestHeadersHook(http_flow) >> reply() <<
     http.HttpRequestHook(http_flow) >> reply() << SendData(
         tctx.server, b"GET / HTTP/1.1\r\n"
         b"Connection: upgrade\r\n"
         b"Upgrade: websocket\r\n"
         b"Sec-WebSocket-Version: 13\r\n"
         b"\r\n") >> DataReceived(
             tctx.server, b"HTTP/1.1 101 Switching Protocols\r\n"
             b"Upgrade: websocket\r\n"
             b"Connection: Upgrade\r\n"
             b"\r\n") << http.HttpResponseHeadersHook(http_flow) >> reply() <<
     http.HttpResponseHook(http_flow) >> reply() << SendData(
         tctx.client, b"HTTP/1.1 101 Switching Protocols\r\n"
         b"Upgrade: websocket\r\n"
         b"Connection: Upgrade\r\n"
         b"\r\n"))
    if proto == "websocket":
        assert playbook << WebsocketStartHook(http_flow)
    elif proto == "tcp":
        assert playbook << TcpStartHook(Placeholder(TCPFlow))
    else:
        assert (playbook << Log(
            "Sent HTTP 101 response, but no protocol is enabled to upgrade to.",
            "warn") << CloseConnection(tctx.client))
Beispiel #18
0
def test_http_server_aborts(tctx, stream):
    """Test handling of the case where a server aborts during response transmission."""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular))

    def enable_streaming(flow: HTTPFlow):
        flow.response.stream = True

    assert (playbook >> DataReceived(
        tctx.client, b"GET http://example.com/ HTTP/1.1\r\n"
        b"Host: example.com\r\n\r\n") << http.HttpRequestHeadersHook(flow) >>
            reply() << http.HttpRequestHook(flow) >> reply() <<
            OpenConnection(server) >> reply(None) << SendData(
                server, b"GET / HTTP/1.1\r\n"
                b"Host: example.com\r\n\r\n") >> DataReceived(
                    server, b"HTTP/1.1 200 OK\r\n"
                    b"Content-Length: 6\r\n"
                    b"\r\n"
                    b"abc") << http.HttpResponseHeadersHook(flow))
    if stream:
        assert (playbook >> reply(side_effect=enable_streaming) << SendData(
            tctx.client, b"HTTP/1.1 200 OK\r\n"
            b"Content-Length: 6\r\n"
            b"\r\n"
            b"abc"))
    else:
        assert playbook >> reply()
    assert (playbook >> ConnectionClosed(server) << CloseConnection(server) <<
            http.HttpErrorHook(flow))
    if stream:
        assert (playbook >> reply() << CloseConnection(tctx.client))
    else:
        error_html = Placeholder(bytes)
        assert (playbook >> reply() << SendData(tctx.client, error_html) <<
                CloseConnection(tctx.client))
        assert b"502 Bad Gateway" in error_html()
        assert b"peer closed connection" in error_html()

    assert "peer closed connection" in flow().error.msg
Beispiel #19
0
def test_proxy_chain(tctx, strategy):
    server = Placeholder(Server)
    tctx.options.connection_strategy = strategy
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)

    playbook >> DataReceived(tctx.client, b"CONNECT proxy:8080 HTTP/1.1\r\n\r\n")
    if strategy == "eager":
        playbook << OpenConnection(server)
        playbook >> reply(None)
    playbook << SendData(tctx.client, b"HTTP/1.1 200 Connection established\r\n\r\n")

    playbook >> DataReceived(tctx.client, b"CONNECT second-proxy:8080 HTTP/1.1\r\n\r\n")
    playbook << layer.NextLayerHook(Placeholder())
    playbook >> reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.transparent))
    playbook << SendData(tctx.client,
                         b"HTTP/1.1 502 Bad Gateway\r\n"
                         b"content-length: 198\r\n"
                         b"\r\n"
                         b"mitmproxy received an HTTP CONNECT request even though it is not running in regular/upstream mode. "
                         b"This usually indicates a misconfiguration, please see the mitmproxy mode documentation for details.")

    assert playbook
Beispiel #20
0
def test_disconnect_during_handshake_command(tctx: Context, disconnect):
    server = Server(("proxy", 1234))

    tl = TTunnelLayer(tctx, server, tctx.server)
    tl.child_layer = TChildLayer(tctx)

    playbook = Playbook(tl, logs=True)
    assert (playbook << Log("Got start. Server state: CLOSED") >> DataReceived(
        tctx.client, b"client-hello") << SendData(
            tctx.client, b"client-hello-reply") >> DataReceived(
                tctx.client, b"open") << OpenConnection(server) >> reply(None)
            << SendData(server, b"handshake-hello"))
    if disconnect == "client":
        assert (playbook >> ConnectionClosed(tctx.client) >> ConnectionClosed(
            server)  # proxyserver will cancel all other connections as well.
                << CloseConnection(server) <<
                Log("Opened: err='connection closed'. Server state: CLOSED") <<
                Log("Got client close.") << CloseConnection(tctx.client))
    else:
        assert (
            playbook >> ConnectionClosed(server) << CloseConnection(server) <<
            Log("Opened: err='connection closed'. Server state: CLOSED"))
Beispiel #21
0
def test_response_streaming(tctx):
    """Test HTTP response streaming"""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)

    def enable_streaming(flow: HTTPFlow):
        flow.response.stream = lambda x: x.upper()

    assert (Playbook(http.HttpLayer(tctx, HTTPMode.regular)) >> DataReceived(
        tctx.client,
        b"GET http://example.com/largefile HTTP/1.1\r\nHost: example.com\r\n\r\n"
    ) << http.HttpRequestHeadersHook(flow) >> reply() << http.HttpRequestHook(
        flow) >> reply() << OpenConnection(server) >> reply(None) << SendData(
            server, b"GET /largefile HTTP/1.1\r\nHost: example.com\r\n\r\n")
            >> DataReceived(
                server, b"HTTP/1.1 200 OK\r\nContent-Length: 6\r\n\r\nabc") <<
            http.HttpResponseHeadersHook(flow) >>
            reply(side_effect=enable_streaming) << SendData(
                tctx.client,
                b"HTTP/1.1 200 OK\r\nContent-Length: 6\r\n\r\nABC") >>
            DataReceived(server, b"def") << SendData(
                tctx.client, b"DEF") << http.HttpResponseHook(flow) >> reply())
Beispiel #22
0
    def test_simple(self, tctx, pipeline):
        req = http.Request.make("GET", "http://example.com/")
        resp = Placeholder(ResponseHeaders)

        playbook = Playbook(Http1Client(tctx))
        (playbook >> RequestHeaders(1, req, True) << SendData(
            tctx.server, b"GET / HTTP/1.1\r\ncontent-length: 0\r\n\r\n") >>
         RequestEndOfMessage(1))
        if pipeline:
            with pytest.raises(
                    AssertionError,
                    match="assert self.stream_id == event.stream_id"):
                assert (playbook >> RequestHeaders(3, req, True))
            return
        assert (
            playbook >> DataReceived(
                tctx.server, b"HTTP/1.1 200 OK\r\ncontent-length: 0\r\n\r\n")
            << ReceiveHttp(resp) << ReceiveHttp(ResponseEndOfMessage(1))
            # no we can send the next request
            >> RequestHeaders(3, req, True) << SendData(
                tctx.server, b"GET / HTTP/1.1\r\ncontent-length: 0\r\n\r\n"))
        assert resp().response.status_code == 200
Beispiel #23
0
def test_dont_reuse_closed(tctx):
    """Test that a closed connection is not reused."""
    server = Placeholder(Server)
    server2 = Placeholder(Server)
    assert (
        Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) >>
        DataReceived(
            tctx.client,
            b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n")
        << OpenConnection(server) >> reply(None) << SendData(
            server,
            b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") >> DataReceived(
                server, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") <<
        SendData(tctx.client, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
        >> ConnectionClosed(server) << CloseConnection(server) >> DataReceived(
            tctx.client,
            b"GET http://example.com/two HTTP/1.1\r\nHost: example.com\r\n\r\n"
        ) << OpenConnection(server2) >> reply(None) << SendData(
            server2,
            b"GET /two HTTP/1.1\r\nHost: example.com\r\n\r\n") >> DataReceived(
                server2, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") <<
        SendData(tctx.client, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"))
Beispiel #24
0
def test_transparent_tcp(tctx: Context, monkeypatch, connection_strategy):
    monkeypatch.setattr(platform, "original_addr", lambda sock:
                        ("address", 22))

    flow = Placeholder(TCPFlow)
    tctx.options.connection_strategy = connection_strategy

    sock = object()
    playbook = Playbook(modes.TransparentProxy(tctx))
    (playbook << GetSocket(tctx.client) >> reply(sock))
    if connection_strategy == "lazy":
        assert playbook
    else:
        assert (playbook << OpenConnection(tctx.server) >> reply(None) >>
                DataReceived(tctx.server, b"hello") << NextLayerHook(
                    Placeholder(NextLayer)) >> reply_next_layer(tcp.TCPLayer)
                << TcpStartHook(flow) >> reply() << TcpMessageHook(flow) >>
                reply() << SendData(tctx.client, b"hello"))
        assert flow().messages[0].content == b"hello"
        assert not flow().messages[0].from_client

    assert tctx.server.address == ("address", 22)
Beispiel #25
0
def test_tunnel_default_impls(tctx: Context):
    """
    Some tunnels don't need certain features, so the default behaviour
    should be to be transparent.
    """
    server = Server(None)
    server.state = ConnectionState.OPEN
    tl = tunnel.TunnelLayer(tctx, server, tctx.server)
    tl.child_layer = TChildLayer(tctx)
    playbook = Playbook(tl, logs=True)
    assert (playbook << Log("Got start. Server state: OPEN") >> DataReceived(
        server, b"server-hello") << SendData(server, b"server-hello-reply"))
    assert tl.tunnel_state is tunnel.TunnelState.OPEN
    assert (playbook >> ConnectionClosed(server) << Log("Got server close.") <<
            CloseConnection(server))
    assert tl.tunnel_state is tunnel.TunnelState.CLOSED

    assert (
        playbook >> DataReceived(
            tctx.client, b"open") << OpenConnection(server) >> reply(None) <<
        Log("Opened: err=None. Server state: OPEN") >> DataReceived(
            server, b"half-close") << CloseConnection(server, half_close=True))
Beispiel #26
0
def test_reverse_proxy_tcp_over_tls(tctx: Context, monkeypatch, patch,
                                    connection_strategy):
    """
    Test
        client --TCP-- mitmproxy --TCP over TLS-- server
    reverse proxying.
    """

    if patch:
        monkeypatch.setattr(tls, "ServerTLSLayer", tls.MockTLSLayer)

    flow = Placeholder(TCPFlow)
    data = Placeholder(bytes)
    tctx.options.mode = "reverse:https://localhost:8000"
    tctx.options.connection_strategy = connection_strategy
    playbook = Playbook(modes.ReverseProxy(tctx))
    if connection_strategy == "eager":
        (playbook << OpenConnection(tctx.server) >> DataReceived(
            tctx.client, b"\x01\x02\x03") >> reply(
                None, to=OpenConnection(tctx.server)))
    else:
        (playbook >> DataReceived(tctx.client, b"\x01\x02\x03"))
    if patch:
        (playbook << NextLayerHook(Placeholder(NextLayer)) >> reply_next_layer(
            tcp.TCPLayer) << TcpStartHook(flow) >> reply())
        if connection_strategy == "lazy":
            (playbook << OpenConnection(tctx.server) >> reply(None))
        assert (playbook << TcpMessageHook(flow) >> reply() << SendData(
            tctx.server, data))
        assert data() == b"\x01\x02\x03"
    else:
        if connection_strategy == "lazy":
            (playbook << NextLayerHook(Placeholder(NextLayer)) >>
             reply_next_layer(tcp.TCPLayer) << TcpStartHook(flow) >> reply() <<
             OpenConnection(tctx.server) >> reply(None))
        assert (playbook << TlsStartHook(Placeholder()) >> reply_tls_start() <<
                SendData(tctx.server, data))
        assert tls.parse_client_hello(data()).sni == "localhost"
Beispiel #27
0
def test_https_proxy(strategy, tctx):
    """Test a CONNECT request, followed by a HTTP GET /"""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular))
    tctx.options.connection_strategy = strategy

    (playbook
     >> DataReceived(tctx.client, b"CONNECT example.proxy:80 HTTP/1.1\r\n\r\n")
     << http.HttpConnectHook(Placeholder())
     >> reply())
    if strategy == "eager":
        (playbook
         << OpenConnection(server)
         >> reply(None))
    (playbook
     << SendData(tctx.client, b'HTTP/1.1 200 Connection established\r\n\r\n')
     >> DataReceived(tctx.client, b"GET /foo?hello=1 HTTP/1.1\r\nHost: example.com\r\n\r\n")
     << layer.NextLayerHook(Placeholder())
     >> reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.transparent))
     << http.HttpRequestHeadersHook(flow)
     >> reply()
     << http.HttpRequestHook(flow)
     >> reply())
    if strategy == "lazy":
        (playbook
         << OpenConnection(server)
         >> reply(None))
    (playbook
     << SendData(server, b"GET /foo?hello=1 HTTP/1.1\r\nHost: example.com\r\n\r\n")
     >> DataReceived(server, b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!")
     << http.HttpResponseHeadersHook(flow)
     >> reply()
     << http.HttpResponseHook(flow)
     >> reply()
     << SendData(tctx.client, b"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World!"))
    assert playbook
Beispiel #28
0
def test_reverse_proxy(tctx, keep_host_header):
    """Test mitmproxy in reverse proxy mode.

     - make sure that we connect to the right host
     - make sure that we respect keep_host_header
     - make sure that we include non-standard ports in the host header (#4280)
    """
    server = Placeholder(Server)
    tctx.options.mode = "reverse:http://localhost:8000"
    tctx.options.keep_host_header = keep_host_header
    assert (
        Playbook(modes.ReverseProxy(tctx), hooks=False) >> DataReceived(
            tctx.client, b"GET /foo HTTP/1.1\r\n"
            b"Host: example.com\r\n\r\n") << NextLayerHook(
                Placeholder(NextLayer)) >>
        reply_next_layer(lambda ctx: http.HttpLayer(ctx, HTTPMode.transparent))
        << OpenConnection(server) >> reply(None) << SendData(
            server, b"GET /foo HTTP/1.1\r\n"
            b"Host: " +
            (b"example.com" if keep_host_header else b"localhost:8000") +
            b"\r\n\r\n") >> DataReceived(
                server, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") <<
        SendData(tctx.client, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"))
    assert server().address == ("localhost", 8000)
Beispiel #29
0
def test_multiple_server_connections(tctx):
    """Test multiple requests being rewritten to different targets."""
    server1 = Placeholder(Server)
    server2 = Placeholder(Server)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)

    def redirect(to: str):
        def side_effect(flow: HTTPFlow):
            flow.request.url = to

        return side_effect

    assert (
            playbook
            >> DataReceived(tctx.client, b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n")
            << http.HttpRequestHook(Placeholder())
            >> reply(side_effect=redirect("http://one.redirect/"))
            << OpenConnection(server1)
            >> reply(None)
            << SendData(server1, b"GET / HTTP/1.1\r\nHost: one.redirect\r\n\r\n")
            >> DataReceived(server1, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
            << SendData(tctx.client, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
    )
    assert (
            playbook
            >> DataReceived(tctx.client, b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n")
            << http.HttpRequestHook(Placeholder())
            >> reply(side_effect=redirect("http://two.redirect/"))
            << OpenConnection(server2)
            >> reply(None)
            << SendData(server2, b"GET / HTTP/1.1\r\nHost: two.redirect\r\n\r\n")
            >> DataReceived(server2, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
            << SendData(tctx.client, b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
    )
    assert server1().address == ("one.redirect", 80)
    assert server2().address == ("two.redirect", 80)
Beispiel #30
0
def test_connection_close_header(tctx, client_close, server_close):
    """Test that we correctly close connections if we have a `Connection: close` header."""
    if not client_close and not server_close:
        return
    server = Placeholder(Server)
    assert (
            Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)
            >> DataReceived(tctx.client, b"GET http://example/ HTTP/1.1\r\n"
                                         b"Host: example\r\n" + client_close +
                            b"\r\n")
            << OpenConnection(server)
            >> reply(None)
            << SendData(server, b"GET / HTTP/1.1\r\n"
                                b"Host: example\r\n" + client_close +
                        b"\r\n")
            >> DataReceived(server, b"HTTP/1.1 200 OK\r\n"
                                    b"Content-Length: 0\r\n" + server_close +
                            b"\r\n")
            << CloseConnection(server)
            << SendData(tctx.client, b"HTTP/1.1 200 OK\r\n"
                                     b"Content-Length: 0\r\n" + server_close +
                        b"\r\n")
            << CloseConnection(tctx.client)
    )