示例#1
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
示例#2
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
示例#3
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")
        )
示例#4
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")
        )
示例#5
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)
    )
示例#6
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
示例#7
0
def test_ping(ws_testdata):
    tctx, playbook, flow = ws_testdata
    assert (
            playbook
            << websocket.WebsocketStartHook(flow)
            >> reply()
            >> DataReceived(tctx.client, masked_bytes(b"\x89\x11ping-with-payload"))
            << Log("Received WebSocket ping from client (payload: b'ping-with-payload')")
            << SendData(tctx.server, masked(b"\x89\x11ping-with-payload"))
            >> DataReceived(tctx.server, b"\x8a\x11pong-with-payload")
            << Log("Received WebSocket pong from server (payload: b'pong-with-payload')")
            << SendData(tctx.client, b"\x8a\x11pong-with-payload")
    )
    assert not flow.websocket.messages
示例#8
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
示例#9
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
示例#10
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)
    )
示例#11
0
def test_unknown_ext(ws_testdata):
    tctx, playbook, flow = ws_testdata
    flow.response.headers["Sec-WebSocket-Extensions"] = "funky-bits; param=42"
    assert (
            playbook
            << Log("Ignoring unknown WebSocket extension 'funky-bits'.")
            << websocket.WebsocketStartHook(flow)
            >> reply()
    )
示例#12
0
def test_unknown_ext(ws_testdata):
    tctx, playbook = ws_testdata
    flow = Placeholder(WebSocketFlow)
    # noinspection PyUnresolvedReferences
    http_flow: HTTPFlow = playbook.layer.flow.handshake_flow
    http_flow.response.headers[
        "Sec-WebSocket-Extensions"] = "funky-bits; param=42"
    assert (
        playbook << Log("Ignoring unknown WebSocket extension 'funky-bits'.")
        << websocket.WebsocketStartHook(flow) >> reply())
示例#13
0
def test_transparent_failure(tctx: Context, monkeypatch):
    """Test that we recover from a transparent mode resolve error."""
    def raise_err(sock):
        raise RuntimeError("platform-specific error")

    monkeypatch.setattr(platform, "original_addr", raise_err)
    assert (Playbook(modes.TransparentProxy(tctx), logs=True) << GetSocket(
        tctx.client
    ) >> reply(object()) << Log(
        "Transparent mode failure: RuntimeError('platform-specific error')",
        "info"))
示例#14
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(Placeholder(WebSocketFlow))
    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)
        )
示例#15
0
def test_no_normalization(tctx, normalize):
    """Test that we don't normalize headers when we just pass them through."""
    tctx.options.normalize_outbound_headers = normalize
    tctx.options.validate_inbound_headers = False

    server = Placeholder(Server)
    flow = Placeholder(HTTPFlow)
    playbook, cff = start_h2_client(tctx)

    request_headers = list(example_request_headers) + [(b"Should-Not-Be-Capitalized! ", b" :) ")]
    request_headers_lower = [(k.lower(), v) for (k, v) in request_headers]
    response_headers = list(example_response_headers) + [(b"Same", b"Here")]
    response_headers_lower = [(k.lower(), v) for (k, v) in response_headers]

    initial = Placeholder(bytes)
    assert (
            playbook
            >> DataReceived(tctx.client,
                            cff.build_headers_frame(request_headers, flags=["END_STREAM"]).serialize())
            << http.HttpRequestHeadersHook(flow)
            >> reply()
            << http.HttpRequestHook(flow)
            >> reply()
            << OpenConnection(server)
            >> reply(None, side_effect=make_h2)
            << SendData(server, initial)
    )
    frames = decode_frames(initial())
    assert [type(x) for x in frames] == [
        hyperframe.frame.SettingsFrame,
        hyperframe.frame.HeadersFrame,
    ]
    assert hpack.hpack.Decoder().decode(frames[1].data, True) == request_headers_lower if normalize else request_headers

    sff = FrameFactory()
    (
            playbook
            >> DataReceived(server, sff.build_headers_frame(response_headers, flags=["END_STREAM"]).serialize())
            << http.HttpResponseHeadersHook(flow)
            >> reply()
            << http.HttpResponseHook(flow)
            >> reply()
    )
    if normalize:
        playbook << Log("Lowercased 'Same' header as uppercase is not allowed with HTTP/2.")
    hdrs = response_headers_lower if normalize else response_headers
    assert playbook << SendData(tctx.client, cff.build_headers_frame(hdrs, flags=["END_STREAM"]).serialize())

    assert flow().request.headers.fields == ((b"Should-Not-Be-Capitalized! ", b" :) "),)
    assert flow().response.headers.fields == ((b"Same", b"Here"),)
示例#16
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