예제 #1
0
def test_disconnect_during_handshake_start(tctx: Context, disconnect):
    server = Server(("proxy", 1234))
    server.state = ConnectionState.OPEN

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

    playbook = Playbook(tl, logs=True)

    assert (
            playbook
            << 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("Got start. Server state: CLOSED")
                << Log("Got client close.")
                << CloseConnection(tctx.client)
        )
    else:
        assert (
                playbook
                >> ConnectionClosed(server)
                << CloseConnection(server)
                << Log("Got start. Server state: CLOSED")
        )
예제 #2
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 without notice'. Server state: CLOSED")
                << Log("Got client close.")
                << CloseConnection(tctx.client)
        )
    else:
        assert (
                playbook
                >> ConnectionClosed(server)
                << CloseConnection(server)
                << Log("Opened: err='connection closed without notice'. Server state: CLOSED")
        )
예제 #3
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)
    )
예제 #4
0
def test_http_client_aborts(tctx, stream):
    """Test handling of the case where a client aborts during request transmission."""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=True)

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

    assert (playbook >> DataReceived(
        tctx.client, b"POST http://example.com/ HTTP/1.1\r\n"
        b"Host: example.com\r\n"
        b"Content-Length: 6\r\n"
        b"\r\n"
        b"abc") << http.HttpRequestHeadersHook(flow))
    if stream:
        assert (playbook >> reply(side_effect=enable_streaming) <<
                OpenConnection(server) >> reply(None) << SendData(
                    server, b"POST / HTTP/1.1\r\n"
                    b"Host: example.com\r\n"
                    b"Content-Length: 6\r\n"
                    b"\r\n"
                    b"abc"))
    else:
        assert playbook >> reply()
    (playbook >> ConnectionClosed(tctx.client) << CloseConnection(tctx.client))
    if stream:
        playbook << CloseConnection(server)
    assert (playbook << http.HttpErrorHook(flow) >> reply() << None)

    assert "peer closed connection" in flow().error.msg
예제 #5
0
def test_tunnel_handshake_command(tctx: Context, success):
    server = Server(("proxy", 1234))

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

    playbook = Playbook(tl, logs=True)
    (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") >> DataReceived(
         server, b"handshake-" + success.encode()) << SendData(
             server, b"handshake-" + success.encode()))
    if success == "success":
        assert (playbook << Log(f"Opened: err=None. Server state: OPEN") >>
                DataReceived(server, b"tunneled-server-hello") << SendData(
                    server, b"tunneled-server-hello-reply") >>
                ConnectionClosed(tctx.client) << Log("Got client close.") <<
                CloseConnection(tctx.client))
        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
    else:
        assert (playbook << CloseConnection(server) <<
                Log("Opened: err='handshake error'. Server state: CLOSED"))
        assert tl.tunnel_state is tunnel.TunnelState.CLOSED
예제 #6
0
def test_simple(tctx):
    """open connection, receive data, send it to peer"""
    f = Placeholder(TCPFlow)

    assert (
            Playbook(tcp.TCPLayer(tctx))
            << tcp.TcpStartHook(f)
            >> reply()
            << OpenConnection(tctx.server)
            >> reply(None)
            >> DataReceived(tctx.client, b"hello!")
            << tcp.TcpMessageHook(f)
            >> reply()
            << SendData(tctx.server, b"hello!")
            >> DataReceived(tctx.server, b"hi")
            << tcp.TcpMessageHook(f)
            >> reply()
            << SendData(tctx.client, b"hi")
            >> ConnectionClosed(tctx.server)
            << CloseConnection(tctx.client, half_close=True)
            >> ConnectionClosed(tctx.client)
            << CloseConnection(tctx.server)
            << tcp.TcpEndHook(f)
            >> reply()
            >> ConnectionClosed(tctx.client)
            << None
    )
    assert len(f().messages) == 2
예제 #7
0
def test_server_aborts(tctx, data):
    """Test the scenario where the server doesn't serve a response"""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    err = Placeholder(bytes)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)
    assert (
            playbook
            >> 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")
    )
    if data:
        playbook >> DataReceived(server, data)
    assert (
            playbook
            >> ConnectionClosed(server)
            << CloseConnection(server)
            << http.HttpErrorHook(flow)
            >> reply()
            << SendData(tctx.client, err)
            << CloseConnection(tctx.client)
    )
    assert flow().error
    assert b"502 Bad Gateway" in err()
예제 #8
0
def test_close_disconnect(ws_testdata):
    tctx, playbook, flow = ws_testdata
    assert (playbook << websocket.WebsocketStartHook(flow) >> reply() >>
            ConnectionClosed(tctx.server) << CloseConnection(tctx.server) <<
            SendData(tctx.client, b"\x88\x02\x03\xe8") << CloseConnection(
                tctx.client) << websocket.WebsocketErrorHook(flow) >> reply()
            >> ConnectionClosed(tctx.client))
    assert "ABNORMAL_CLOSURE" in flow.error.msg
예제 #9
0
def test_close_error(ws_testdata):
    tctx, playbook, flow = ws_testdata
    assert (playbook << websocket.WebsocketStartHook(flow) >> reply() >>
            DataReceived(tctx.server, b"\x88\x02\x0f\xa0") << SendData(
                tctx.server, masked(b"\x88\x02\x0f\xa0")) << CloseConnection(
                    tctx.server) << SendData(tctx.client, b"\x88\x02\x0f\xa0")
            << CloseConnection(
                tctx.client) << websocket.WebsocketErrorHook(flow) >> reply())
    assert "UNKNOWN_ERROR=4000" in flow.error.msg
예제 #10
0
def test_close_code(ws_testdata):
    tctx, playbook, flow = ws_testdata
    assert (playbook << websocket.WebsocketStartHook(flow) >> reply() >>
            DataReceived(tctx.server, b"\x88\x02\x0f\xa0") << SendData(
                tctx.server, masked(b"\x88\x02\x0f\xa0")) << CloseConnection(
                    tctx.server) << SendData(tctx.client, b"\x88\x02\x0f\xa0")
            << CloseConnection(
                tctx.client) << websocket.WebsocketEndHook(flow) >> reply())
    assert flow.websocket.close_code == 4000
예제 #11
0
def test_close_disconnect(ws_testdata):
    tctx, playbook, flow = ws_testdata
    assert (playbook << websocket.WebsocketStartHook(flow) >> reply() >>
            ConnectionClosed(tctx.server) << CloseConnection(tctx.server) <<
            SendData(tctx.client, b"\x88\x02\x03\xe8") << CloseConnection(
                tctx.client) << websocket.WebsocketEndHook(flow) >> reply() >>
            ConnectionClosed(tctx.client))
    # The \x03\xe8 above is code 1000 (normal closure).
    # But 1006 (ABNORMAL_CLOSURE) is expected, because the connection was already closed.
    assert flow.websocket.close_code == 1006
예제 #12
0
def test_receive_data_after_half_close(tctx):
    """
    data received after the other connection has been half-closed should still be forwarded.
    """
    assert (Playbook(tcp.TCPLayer(tctx), hooks=False) <<
            OpenConnection(tctx.server) >> reply(None) >> DataReceived(
                tctx.client, b"eof-delimited-request") << SendData(
                    tctx.server, b"eof-delimited-request") >> ConnectionClosed(
                        tctx.client) <<
            CloseConnection(tctx.server, half_close=True) >> DataReceived(
                tctx.server, b"i'm late") << SendData(tctx.client, b"i'm late")
            >> ConnectionClosed(tctx.server) << CloseConnection(tctx.client))
예제 #13
0
def test_response_until_eof(tctx):
    """Test scenario where the server response body is terminated by EOF."""
    server = 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\n\r\nfoo") >>
            ConnectionClosed(server) << CloseConnection(server) << SendData(
                tctx.client, b"HTTP/1.1 200 OK\r\n\r\nfoo") << CloseConnection(
                    tctx.client))
예제 #14
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)
    )
예제 #15
0
def test_protocol_error(ws_testdata):
    tctx, playbook, flow = ws_testdata
    assert (
        playbook << websocket.WebsocketStartHook(flow) >> reply() >>
        DataReceived(tctx.server, b"\x01\x03foo") >>
        DataReceived(tctx.server, b"\x02\x03bar") << SendData(
            tctx.server,
            masked(
                b"\x88/\x03\xeaexpected CONTINUATION, got <Opcode.BINARY: 2>"))
        << CloseConnection(tctx.server) << SendData(
            tctx.client,
            b"\x88/\x03\xeaexpected CONTINUATION, got <Opcode.BINARY: 2>") <<
        CloseConnection(
            tctx.client) << websocket.WebsocketErrorHook(flow) >> reply())
    assert not flow.websocket.messages
예제 #16
0
def test_http_proxy_tcp(tctx, mode, close_first):
    """Test TCP over HTTP CONNECT."""
    server = Placeholder(Server)
    f = Placeholder(TCPFlow)
    tctx.options.connection_strategy = "lazy"

    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=False)) <<
            TcpStartHook(f) >> reply() << 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)

    assert (playbook >> TcpMessageInjected(
        f, TCPMessage(False,
                      b"fake news from your friendly man-in-the-middle")) <<
            SendData(tctx.client,
                     b"fake news from your friendly man-in-the-middle"))

    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))
예제 #17
0
def test_close_normal(ws_testdata):
    tctx, playbook, flow = ws_testdata
    masked_close = Placeholder(bytes)
    close = Placeholder(bytes)
    assert (playbook << websocket.WebsocketStartHook(flow) >> reply() >>
            DataReceived(tctx.client, masked_bytes(b"\x88\x00")) << SendData(
                tctx.server, masked_close) << CloseConnection(tctx.server) <<
            SendData(tctx.client, close) << CloseConnection(
                tctx.client) << websocket.WebsocketEndHook(flow) >> reply())
    # wsproto currently handles this inconsistently, see
    # https://github.com/python-hyper/wsproto/pull/153/files
    assert masked_close() == masked(
        b"\x88\x02\x03\xe8") or masked_close() == masked(b"\x88\x00")
    assert close() == b"\x88\x02\x03\xe8" or close() == b"\x88\x00"

    assert flow.websocket.close_code == 1005
예제 #18
0
def test_h2_to_h1(tctx):
    """Test HTTP/2 -> HTTP/1 request translation"""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)

    conn, playbook = h2_client(tctx)

    conn.send_headers(1, example_request_headers, end_stream=True)
    response = Placeholder(bytes)
    assert (
        playbook >> DataReceived(tctx.client, conn.data_to_send()) <<
        http.HttpRequestHeadersHook(flow) >> reply() <<
        http.HttpRequestHook(flow) >> reply() << 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: 12\r\n\r\n")
        << http.HttpResponseHeadersHook(flow) >> reply() >> DataReceived(
            server, b"Hello World!") << http.HttpResponseHook(flow) <<
        CloseConnection(server) >> reply(to=-2) << SendData(
            tctx.client, response))
    events = conn.receive_data(response())
    assert event_types(events) == [
        h2.events.ResponseReceived, h2.events.DataReceived,
        h2.events.DataReceived, h2.events.StreamEnded
    ]
    resp: h2.events.ResponseReceived = events[0]
    body: h2.events.DataReceived = events[1]
    assert resp.headers == [(b':status', b'200'), (b'content-length', b'12')]
    assert body.data == b"Hello World!"
예제 #19
0
def test_h1_to_h2(tctx):
    """Test HTTP/1 -> HTTP/2 request translation"""
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)

    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular))

    conf = h2.config.H2Configuration(client_side=False)
    conn = h2.connection.H2Connection(conf)
    conn.initiate_connection()

    request = Placeholder(bytes)
    assert (playbook >> DataReceived(
        tctx.client,
        b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n") <<
            http.HttpRequestHeadersHook(flow) >> reply() <<
            http.HttpRequestHook(flow) >> reply() << OpenConnection(server) >>
            reply(None, side_effect=make_h2) << SendData(server, request))
    events = conn.receive_data(request())
    assert event_types(events) == [
        h2.events.RemoteSettingsChanged, h2.events.RequestReceived,
        h2.events.StreamEnded
    ]

    conn.send_headers(1, example_response_headers)
    conn.send_data(1, b"Hello World!", end_stream=True)
    settings_ack = Placeholder(bytes)
    assert (
        playbook >> DataReceived(server, conn.data_to_send()) <<
        http.HttpResponseHeadersHook(flow) << SendData(server, settings_ack) >>
        reply(to=-2) << http.HttpResponseHook(flow) >> reply() << SendData(
            tctx.client, b"HTTP/1.1 200 OK\r\n\r\nHello World!") <<
        CloseConnection(tctx.client))
    assert settings_ack() == b'\x00\x00\x00\x04\x01\x00\x00\x00\x00'
예제 #20
0
def test_cancel_during_response_hook(tctx):
    """
    Test that we properly handle the case of the following event sequence:
        - we receive a server response
        - we trigger the response hook
        - the client cancels the stream
        - the response hook completes

    Given that we have already triggered the response hook, we don't want to trigger the error hook.
    """
    playbook, cff = start_h2_client(tctx)
    flow = Placeholder(HTTPFlow)
    server = Placeholder(Server)

    assert (
            playbook
            >> DataReceived(tctx.client,
                            cff.build_headers_frame(example_request_headers, flags=["END_STREAM"]).serialize())
            << http.HttpRequestHeadersHook(flow)
            >> reply()
            << http.HttpRequestHook(flow)
            >> reply()
            << 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 204 No Content\r\n\r\n")
            << http.HttpResponseHeadersHook(flow)
            << CloseConnection(server)
            >> reply(to=-2)
            << http.HttpResponseHook(flow)
            >> DataReceived(tctx.client, cff.build_rst_stream_frame(1, ErrorCodes.CANCEL).serialize())
            >> reply(to=-2)
    )
예제 #21
0
def test_tunnel_handshake_start(tctx: Context, success):
    server = Server(("proxy", 1234))
    server.state = ConnectionState.OPEN

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

    playbook = Playbook(tl, logs=True)
    (playbook << SendData(server, b"handshake-hello") >> DataReceived(
        tctx.client, b"client-hello") >> DataReceived(
            server, b"handshake-" + success.encode()) << SendData(
                server, b"handshake-" + success.encode()))
    if success == "success":
        playbook << Log("Got start. Server state: OPEN")
    else:
        playbook << CloseConnection(server)
        playbook << Log("Got start. Server state: CLOSED")

    playbook << SendData(tctx.client, b"client-hello-reply")
    if success == "success":
        playbook >> DataReceived(server, b"tunneled-server-hello")
        playbook << SendData(server, b"tunneled-server-hello-reply")

    assert playbook
예제 #22
0
def test_rst_then_close(tctx):
    """
    Test that we properly handle the case of a client that first causes protocol errors and then disconnects.

    Adapted from h2spec http2/5.1/5.
    """
    playbook, cff = start_h2_client(tctx)
    flow = Placeholder(HTTPFlow)
    server = Placeholder(Server)

    assert (
            playbook
            >> DataReceived(tctx.client,
                            cff.build_headers_frame(example_request_headers, flags=["END_STREAM"]).serialize())
            << http.HttpRequestHeadersHook(flow)
            >> reply()
            << http.HttpRequestHook(flow)
            >> reply()
            << OpenConnection(server)
            >> DataReceived(tctx.client, cff.build_data_frame(b"unexpected data frame").serialize())
            << SendData(tctx.client, cff.build_rst_stream_frame(1, ErrorCodes.STREAM_CLOSED).serialize())
            >> ConnectionClosed(tctx.client)
            << CloseConnection(tctx.client)
            >> reply("connection cancelled", to=-5)
            << http.HttpErrorHook(flow)
            >> reply()
    )
    assert flow().error.msg == "connection cancelled"
예제 #23
0
def test_cancel_then_server_disconnect(tctx):
    """
    Test that we properly handle the case of the following event sequence:
        - client cancels a stream
        - we start an error hook
        - server disconnects
        - error hook completes.
    """
    playbook, cff = start_h2_client(tctx)
    flow = Placeholder(HTTPFlow)
    server = Placeholder(Server)

    assert (
            playbook
            >> DataReceived(tctx.client,
                            cff.build_headers_frame(example_request_headers, flags=["END_STREAM"]).serialize())
            << http.HttpRequestHeadersHook(flow)
            >> reply()
            << http.HttpRequestHook(flow)
            >> reply()
            << OpenConnection(server)
            >> reply(None)
            << SendData(server, b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
            >> DataReceived(tctx.client, cff.build_rst_stream_frame(1, ErrorCodes.CANCEL).serialize())
            << CloseConnection(server)
            << http.HttpErrorHook(flow)
            >> reply()
            >> ConnectionClosed(server)
            << None
    )
예제 #24
0
 def assert_kill(err_hook: bool = True):
     playbook >> reply(side_effect=kill)
     if err_hook:
         playbook << http.HttpErrorHook(flow)
         playbook >> reply()
     playbook << CloseConnection(tctx.client)
     assert playbook
예제 #25
0
def test_server_unreachable(tctx, connect):
    """Test the scenario where the target server is unreachable."""
    tctx.options.connection_strategy = "eager"
    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    err = Placeholder(bytes)
    playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)
    if connect:
        playbook >> DataReceived(tctx.client, b"CONNECT example.com:443 HTTP/1.1\r\n\r\n")
    else:
        playbook >> DataReceived(tctx.client, b"GET http://example.com/ HTTP/1.1\r\n\r\n")

    playbook << OpenConnection(server)
    playbook >> reply("Connection failed")
    if not connect:
        # Our API isn't ideal here, there is no error hook for CONNECT requests currently.
        # We could fix this either by having CONNECT request go through all our regular hooks,
        # or by adding dedicated ok/error hooks.
        playbook << http.HttpErrorHook(flow)
        playbook >> reply()
    playbook << SendData(tctx.client, err)
    if not connect:
        playbook << CloseConnection(tctx.client)

    assert playbook
    if not connect:
        assert flow().error
    assert b"502 Bad Gateway" in err()
    assert b"Connection failed" in err()
예제 #26
0
def test_disconnect_while_intercept(tctx):
    """Test a server disconnect while a request is intercepted."""
    tctx.options.connection_strategy = "eager"

    server1 = Placeholder(Server)
    server2 = Placeholder(Server)
    flow = Placeholder(HTTPFlow)

    assert (
            Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False)
            >> DataReceived(tctx.client, b"CONNECT example.com:80 HTTP/1.1\r\n\r\n")
            << http.HttpConnectHook(Placeholder(HTTPFlow))
            >> reply()
            << OpenConnection(server1)
            >> reply(None)
            << SendData(tctx.client, b'HTTP/1.1 200 Connection established\r\n\r\n')
            >> DataReceived(tctx.client, b"GET / 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.HttpRequestHook(flow)
            >> ConnectionClosed(server1)
            << CloseConnection(server1)
            >> reply(to=-3)
            << OpenConnection(server2)
            >> reply(None)
            << SendData(server2, b"GET / 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")
    )
    assert server1() != server2()
    assert flow().server_conn == server2()
예제 #27
0
def test_socks5_err(data: bytes, err: bytes, msg: str, tctx: Context):
    playbook = (Playbook(modes.Socks5Proxy(tctx), logs=True) >> DataReceived(
        tctx.client, data))
    if err:
        playbook << SendData(tctx.client, err)
    playbook << CloseConnection(tctx.client)
    playbook << Log(msg)
    assert playbook
예제 #28
0
def test_http_proxy_relative_request_no_host_header(tctx):
    """Test handling of a relative-form "GET /" in regular proxy mode, but without a host header."""
    err = Placeholder(bytes)
    assert (Playbook(http.HttpLayer(tctx, HTTPMode.regular), hooks=False) >>
            DataReceived(tctx.client, b"GET / HTTP/1.1\r\n\r\n") << SendData(
                tctx.client, err) << CloseConnection(tctx.client))
    assert b"400 Bad Request" in err()
    assert b"HTTP request has no host header, destination unknown." in err()
예제 #29
0
def test_socks5_premature_close(tctx: Context):
    assert (
        Playbook(modes.Socks5Proxy(tctx), logs=True)
        >> DataReceived(tctx.client, b"\x05")
        >> ConnectionClosed(tctx.client)
        << Log(r"Client closed connection before completing SOCKS5 handshake: b'\x05'")
        << CloseConnection(tctx.client)
    )
예제 #30
0
 def _handle_event(self, event: Event) -> layer.CommandGenerator[None]:
     if isinstance(event, Start):
         yield Log(f"Got start. Server state: {self.context.server.state.name}")
     elif isinstance(event, DataReceived) and event.data == b"client-hello":
         yield SendData(self.context.client, b"client-hello-reply")
     elif isinstance(event, DataReceived) and event.data == b"server-hello":
         yield SendData(self.context.server, b"server-hello-reply")
     elif isinstance(event, DataReceived) and event.data == b"open":
         err = yield OpenConnection(self.context.server)
         yield Log(f"Opened: {err=}. Server state: {self.context.server.state.name}")
     elif isinstance(event, DataReceived) and event.data == b"half-close":
         err = yield CloseConnection(event.connection, half_close=True)
     elif isinstance(event, ConnectionClosed):
         yield Log(f"Got {event.connection.__class__.__name__.lower()} close.")
         yield CloseConnection(event.connection)
     else:
         raise AssertionError