Esempio n. 1
0
def test_open_connection(tctx):
    """
    If there is no server connection yet, establish one,
    because the server may send data first.
    """
    assert (Playbook(tcp.TCPLayer(tctx, True)) << OpenConnection(tctx.server))

    tctx.server.state = ConnectionState.OPEN
    assert (Playbook(tcp.TCPLayer(tctx, True)) << None)
Esempio n. 2
0
def test_open_connection(tctx):
    """
    If there is no server connection yet, establish one,
    because the server may send data first.
    """
    assert (
            Playbook(tcp.TCPLayer(tctx, True))
            << OpenConnection(tctx.server)
    )

    tctx.server.timestamp_start = 1624544785
    assert (
            Playbook(tcp.TCPLayer(tctx, True))
            << None
    )
Esempio n. 3
0
    def send_response(self, already_streamed: bool = False):
        """We have either consumed the entire response from the server or the response was set by an addon."""
        assert self.flow.response
        self.flow.response.timestamp_end = time.time()
        yield HttpResponseHook(self.flow)
        self.server_state = self.state_done
        if (yield from self.check_killed(False)):
            return

        if not already_streamed:
            content = self.flow.response.raw_content
            yield SendHttp(ResponseHeaders(self.stream_id, self.flow.response, not content), self.context.client)
            if content:
                yield SendHttp(ResponseData(self.stream_id, content), self.context.client)

        yield SendHttp(ResponseEndOfMessage(self.stream_id), self.context.client)

        if self.flow.response.status_code == 101:
            is_websocket = (
                    self.flow.response.headers.get("upgrade", "").lower() == "websocket"
                    and
                    self.flow.request.headers.get("Sec-WebSocket-Version", "") == "13"
            )
            if is_websocket:
                self.child_layer = websocket.WebsocketLayer(self.context, self.flow)
            else:
                self.child_layer = tcp.TCPLayer(self.context)
            if self.debug:
                yield commands.Log(f"{self.debug}[http] upgrading to {self.child_layer}", "debug")
            yield from self.child_layer.handle_event(events.Start())
            self._handle_event = self.passthrough
            return
Esempio n. 4
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
Esempio n. 5
0
    def send_response(self, already_streamed: bool = False):
        """We have either consumed the entire response from the server or the response was set by an addon."""
        assert self.flow.response
        self.flow.response.timestamp_end = time.time()

        is_websocket = (self.flow.response.status_code == 101
                        and self.flow.response.headers.get(
                            "upgrade", "").lower() == "websocket"
                        and self.flow.request.headers.get(
                            "Sec-WebSocket-Version",
                            "").encode() == wsproto.handshake.WEBSOCKET_VERSION
                        and self.context.options.websocket)
        if is_websocket:
            # We need to set this before calling the response hook
            # so that addons can determine if a WebSocket connection is following up.
            self.flow.websocket = WebSocketData()

        yield HttpResponseHook(self.flow)
        self.server_state = self.state_done
        if (yield from self.check_killed(False)):
            return

        if not already_streamed:
            content = self.flow.response.raw_content
            done_after_headers = not (content or self.flow.response.trailers)
            yield SendHttp(
                ResponseHeaders(self.stream_id, self.flow.response,
                                done_after_headers), self.context.client)
            if content:
                yield SendHttp(ResponseData(self.stream_id, content),
                               self.context.client)

        if self.flow.response.trailers:
            yield SendHttp(
                ResponseTrailers(self.stream_id, self.flow.response.trailers),
                self.context.client)
        yield SendHttp(ResponseEndOfMessage(self.stream_id),
                       self.context.client)

        if self.flow.response.status_code == 101:
            if is_websocket:
                self.child_layer = websocket.WebsocketLayer(
                    self.context, self.flow)
            elif self.context.options.rawtcp:
                self.child_layer = tcp.TCPLayer(self.context)
            else:
                yield commands.Log(
                    f"Sent HTTP 101 response, but no protocol is enabled to upgrade to.",
                    "warn")
                yield commands.CloseConnection(self.context.client)
                self.client_state = self.server_state = self.state_errored
                return
            if self.debug:
                yield commands.Log(
                    f"{self.debug}[http] upgrading to {self.child_layer}",
                    "debug")
            yield from self.child_layer.handle_event(events.Start())
            self._handle_event = self.passthrough
            return
Esempio n. 6
0
def test_receive_data_before_server_connected(tctx):
    """
    assert that data received before a server connection is established
    will still be forwarded.
    """
    assert (Playbook(tcp.TCPLayer(tctx), hooks=False) << OpenConnection(
        tctx.server) >> DataReceived(tctx.client, b"hello!") >> reply(
            None, to=-2) << SendData(tctx.server, b"hello!"))
Esempio n. 7
0
 def no_flow_hooks():
     assert (
             Playbook(tcp.TCPLayer(tctx, ignore=ignore), hooks=True)
             << OpenConnection(tctx.server)
             >> reply(None)
             >> DataReceived(tctx.client, b"hello!")
             << SendData(tctx.server, b"hello!")
     )
Esempio n. 8
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))
Esempio n. 9
0
def test_open_connection_err(tctx):
    f = Placeholder(TCPFlow)
    assert (
            Playbook(tcp.TCPLayer(tctx))
            << tcp.TcpStartHook(f)
            >> reply()
            << OpenConnection(tctx.server)
            >> reply("Connect call failed")
            << tcp.TcpErrorHook(f)
            >> reply()
            << CloseConnection(tctx.client)
    )
Esempio n. 10
0
    def receive_handshake_data(
            self,
            data: bytes) -> layer.CommandGenerator[Tuple[bool, Optional[str]]]:
        if self.client_hello_parsed:
            return (yield from super().receive_handshake_data(data))
        self.recv_buffer.extend(data)
        try:
            client_hello = parse_client_hello(self.recv_buffer)
        except ValueError:
            return False, f"Cannot parse ClientHello: {self.recv_buffer.hex()}"

        if client_hello:
            self.client_hello_parsed = True
        else:
            return False, None

        self.conn.sni = client_hello.sni
        self.conn.alpn_offers = client_hello.alpn_protocols
        tls_clienthello = ClientHelloData(self.context, client_hello)
        yield TlsClienthelloHook(tls_clienthello)

        if tls_clienthello.ignore_connection:
            # we've figured out that we don't want to intercept this connection, so we assign fake connection objects
            # to all TLS layers. This makes the real connection contents just go through.
            self.conn = self.tunnel_connection = connection.Client(
                ("ignore-conn", 0), ("ignore-conn", 0), time.time())
            parent_layer = self.context.layers[self.context.layers.index(self)
                                               - 1]
            if isinstance(parent_layer, ServerTLSLayer):
                parent_layer.conn = parent_layer.tunnel_connection = connection.Server(
                    None)
            self.child_layer = tcp.TCPLayer(self.context, ignore=True)
            yield from self.event_to_child(
                events.DataReceived(self.context.client,
                                    bytes(self.recv_buffer)))
            self.recv_buffer.clear()
            return True, None
        if tls_clienthello.establish_server_tls_first and not self.context.server.tls_established:
            err = yield from self.start_server_tls()
            if err:
                yield commands.Log(
                    f"Unable to establish TLS connection with server ({err}). "
                    f"Trying to establish TLS with client anyway.")

        yield from self.start_tls()
        if not self.conn.connected:
            return False, "connection closed early"

        ret = yield from super().receive_handshake_data(bytes(
            self.recv_buffer))
        self.recv_buffer.clear()
        return ret
Esempio n. 11
0
def test_inject(tctx):
    """inject data into an open connection."""
    f = Placeholder(TCPFlow)

    assert (
        Playbook(tcp.TCPLayer(tctx)) << tcp.TcpStartHook(f) >>
        TcpMessageInjected(f, TCPMessage(True, b"hello!")) >> reply(to=-2) <<
        OpenConnection(tctx.server) >> reply(None) << tcp.TcpMessageHook(f) >>
        reply() << SendData(tctx.server, b"hello!")
        # and the other way...
        >> TcpMessageInjected(
            f, TCPMessage(False, b"I have already done the greeting for you."))
        << tcp.TcpMessageHook(f) >> reply() << SendData(
            tctx.client, b"I have already done the greeting for you.") << None)
    assert len(f().messages) == 2