예제 #1
0
    def test_continue(self, tctx: Context):
        class TLayer(layer.Layer):
            def _handle_event(
                    self, event: events.Event) -> layer.CommandGenerator[None]:
                yield commands.OpenConnection(self.context.server)
                yield commands.OpenConnection(self.context.server)

        assert (tutils.Playbook(TLayer(tctx)) << commands.OpenConnection(
            tctx.server) >> tutils.reply(None) << commands.OpenConnection(
                tctx.server) >> tutils.reply(None))
예제 #2
0
    def test_untrusted_cert(self, tctx):
        """If the certificate is not trusted, we should fail."""
        playbook = tutils.Playbook(tls.ServerTLSLayer(tctx))
        tctx.server.address = ("wrong.host.mitmproxy.org", 443)
        tctx.server.sni = "wrong.host.mitmproxy.org"

        tssl = SSLTest(server_side=True)

        # send ClientHello
        data = tutils.Placeholder(bytes)
        assert (playbook >> events.DataReceived(
            tctx.client, b"open-connection") << layer.NextLayerHook(
                tutils.Placeholder()) >> tutils.reply_next_layer(TlsEchoLayer)
                << commands.OpenConnection(tctx.server) >> tutils.reply(None)
                << tls.TlsStartHook(tutils.Placeholder()) >> reply_tls_start()
                << commands.SendData(tctx.server, data))

        # receive ServerHello, finish client handshake
        tssl.bio_write(data())
        with pytest.raises(ssl.SSLWantReadError):
            tssl.do_handshake()

        assert (playbook >> events.DataReceived(tctx.server, tssl.bio_read(
        )) << commands.Log(
            "Server TLS handshake failed. Certificate verify failed: Hostname mismatch",
            "warn"
        ) << commands.CloseConnection(tctx.server) << commands.SendData(
            tctx.client,
            b"open-connection failed: Certificate verify failed: Hostname mismatch"
        ))
        assert not tctx.server.tls_established
예제 #3
0
 def event_to_child(self,
                    event: events.Event) -> layer.CommandGenerator[None]:
     if self.tunnel_state is TunnelState.ESTABLISHING and not self.command_to_reply_to:
         self._event_queue.append(event)
         return
     for command in self.child_layer.handle_event(event):
         if isinstance(command, commands.ConnectionCommand
                       ) and command.connection == self.conn:
             if isinstance(command, commands.SendData):
                 yield from self.send_data(command.data)
             elif isinstance(command, commands.CloseConnection):
                 if self.conn != self.tunnel_connection:
                     if command.half_close:
                         self.conn.state &= ~connection.ConnectionState.CAN_WRITE
                     else:
                         self.conn.state = connection.ConnectionState.CLOSED
                 yield from self.send_close(command.half_close)
             elif isinstance(command, commands.OpenConnection):
                 # create our own OpenConnection command object that blocks here.
                 self.command_to_reply_to = command
                 self.tunnel_state = TunnelState.ESTABLISHING
                 err = yield commands.OpenConnection(self.tunnel_connection)
                 if err:
                     yield from self.event_to_child(
                         events.OpenConnectionCompleted(command, err))
                     self.tunnel_state = TunnelState.CLOSED
                 else:
                     yield from self.start_handshake()
             else:  # pragma: no cover
                 raise AssertionError(f"Unexpected command: {command}")
         else:
             yield command
예제 #4
0
    def test_server_required(self, tctx):
        """
        Test the scenario where a server connection is required (for example, because of an unknown ALPN)
        to establish TLS with the client.
        """
        tssl_server = SSLTest(server_side=True, alpn=["quux"])
        playbook, client_layer, tssl_client = make_client_tls_layer(tctx, alpn=["quux"])

        # We should now get instructed to open a server connection.
        data = tutils.Placeholder(bytes)

        def require_server_conn(client_hello: tls.ClientHelloData) -> None:
            client_hello.establish_server_tls_first = True

        assert (
                playbook
                >> events.DataReceived(tctx.client, tssl_client.bio_read())
                << tls.TlsClienthelloHook(tutils.Placeholder())
                >> tutils.reply(side_effect=require_server_conn)
                << commands.OpenConnection(tctx.server)
                >> tutils.reply(None)
                << tls.TlsStartHook(tutils.Placeholder())
                >> reply_tls_start(alpn=b"quux")
                << commands.SendData(tctx.server, data)
        )

        # Establish TLS with the server...
        tssl_server.bio_write(data())
        with pytest.raises(ssl.SSLWantReadError):
            tssl_server.do_handshake()

        data = tutils.Placeholder(bytes)
        assert (
                playbook
                >> events.DataReceived(tctx.server, tssl_server.bio_read())
                << commands.SendData(tctx.server, data)
                << tls.TlsStartHook(tutils.Placeholder())
        )
        tssl_server.bio_write(data())
        assert tctx.server.tls_established
        # Server TLS is established, we can now reply to the client handshake...

        data = tutils.Placeholder(bytes)
        assert (
                playbook
                >> reply_tls_start(alpn=b"quux")
                << commands.SendData(tctx.client, data)
        )
        tssl_client.bio_write(data())
        tssl_client.do_handshake()
        interact(playbook, tctx.client, tssl_client)

        # Both handshakes completed!
        assert tctx.client.tls_established
        assert tctx.server.tls_established
        assert tctx.server.sni == tctx.client.sni
        assert tctx.client.alpn == b"quux"
        assert tctx.server.alpn == b"quux"
        _test_echo(playbook, tssl_server, tctx.server)
        _test_echo(playbook, tssl_client, tctx.client)
예제 #5
0
 def _handle_event(self, event: events.Event) -> layer.CommandGenerator[None]:
     if isinstance(event, events.DataReceived) and event.data == b"open-connection":
         err = yield commands.OpenConnection(self.context.server)
         if err:
             yield commands.SendData(event.connection, f"open-connection failed: {err}".encode())
     else:
         yield from super()._handle_event(event)
예제 #6
0
    def test_passthrough_from_clienthello(self, tctx, server_state):
        """
        Test the scenario where the connection is moved to passthrough mode in the tls_clienthello hook.
        """
        if server_state == "open":
            tctx.server.timestamp_start = time.time()
            tctx.server.state = ConnectionState.OPEN

        playbook, client_layer, tssl_client = make_client_tls_layer(
            tctx, alpn=["quux"])

        def make_passthrough(client_hello: ClientHelloData) -> None:
            client_hello.ignore_connection = True

        client_hello = tssl_client.bio_read()
        (playbook >> events.DataReceived(tctx.client, client_hello) <<
         tls.TlsClienthelloHook(tutils.Placeholder()) >>
         tutils.reply(side_effect=make_passthrough))
        if server_state == "closed":
            (playbook << commands.OpenConnection(tctx.server) >>
             tutils.reply(None))
        assert (playbook << commands.SendData(
            tctx.server, client_hello)  # passed through unmodified
                >> events.DataReceived(
                    tctx.server,
                    b"ServerHello")  # and the same for the serverhello.
                << commands.SendData(tctx.client, b"ServerHello"))
예제 #7
0
 def handle_connect_regular(self):
     if not self.flow.response and self.context.options.connection_strategy == "eager":
         err = yield commands.OpenConnection(self.context.server)
         if err:
             self.flow.response = http.Response.make(
                 502, f"Cannot connect to {human.format_address(self.context.server.address)}: {err}"
             )
     self.child_layer = layer.NextLayer(self.context)
     yield from self.handle_connect_finish()
예제 #8
0
파일: tls.py 프로젝트: zhangbei59/mitmproxy
 def start_server_tls(self) -> layer.CommandGenerator[Optional[str]]:
     """
     We often need information from the upstream connection to establish TLS with the client.
     For example, we need to check if the client does ALPN or not.
     """
     if not self.server_tls_available:
         return "No server TLS available."
     err = yield commands.OpenConnection(self.context.server)
     return err
예제 #9
0
    def finish_start(self) -> layer.CommandGenerator[Optional[str]]:
        if self.context.options.connection_strategy == "eager":
            err = yield commands.OpenConnection(self.context.server)
            if err:
                self._handle_event = self.done  # type: ignore
                return err

        self._handle_event = self.child_layer.handle_event  # type: ignore
        yield from self.child_layer.handle_event(events.Start())
        return None
예제 #10
0
    def finish_start(self):
        if self.context.options.connection_strategy == "eager":
            err = yield commands.OpenConnection(self.context.server)
            if err:
                yield commands.CloseConnection(self.context.client)
                self._handle_event = self.done
                return

        self._handle_event = self.child_layer.handle_event
        yield from self.child_layer.handle_event(events.Start())
예제 #11
0
    def start(self, _) -> layer.CommandGenerator[None]:
        if self.flow:
            yield TcpStartHook(self.flow)

        if not self.context.server.connected:
            err = yield commands.OpenConnection(self.context.server)
            if err:
                if self.flow:
                    self.flow.error = flow.Error(str(err))
                    yield TcpErrorHook(self.flow)
                yield commands.CloseConnection(self.context.client)
                self._handle_event = self.done
                return
        self._handle_event = self.relay_messages
예제 #12
0
 def _handle_event(self, event: events.Event) -> layer.CommandGenerator[None]:
     err: Optional[str]
     if self.context.server.connected:
         err = None
     else:
         err = yield commands.OpenConnection(self.context.server)
     if not err:
         if self.context.server.alpn == b"h2":
             self.child_layer = Http2Client(self.context)
         else:
             self.child_layer = Http1Client(self.context)
         self._handle_event = self.child_layer.handle_event
         yield from self._handle_event(event)
     yield RegisterHttpConnection(self.context.server, err)
예제 #13
0
    def test_debug_messages(self, tctx: Context):
        tctx.server.id = "serverid"

        class TLayer(layer.Layer):
            debug = " "

            def _handle_event(
                    self, event: events.Event) -> layer.CommandGenerator[None]:
                yield from self.state(event)

            def state_foo(self,
                          event: events.Event) -> layer.CommandGenerator[None]:
                assert isinstance(event, events.Start)
                yield commands.OpenConnection(self.context.server)
                self.state = self.state_bar

            state = state_foo

            def state_bar(self,
                          event: events.Event) -> layer.CommandGenerator[None]:
                assert isinstance(event, events.DataReceived)
                yield commands.Log("baz", "info")

        tlayer = TLayer(tctx)
        assert (tutils.Playbook(tlayer, hooks=True, logs=True) << commands.Log(
            " >> Start({})", "debug"
        ) << commands.Log(
            " << OpenConnection({'connection': Server({'id': '…rverid', 'address': None, "
            "'state': <ConnectionState.CLOSED: 0>})})", "debug"
        ) << commands.OpenConnection(tctx.server) >> events.DataReceived(
            tctx.client, b"foo"
        ) << commands.Log(
            " >! DataReceived(client, b'foo')", "debug"
        ) >> tutils.reply(
            None, to=-3
        ) << commands.Log(
            " >> Reply(OpenConnection({'connection': Server("
            "{'id': '…rverid', 'address': None, 'state': <ConnectionState.OPEN: 3>, "
            "'timestamp_start': 1624544785})}),None)", "debug") <<
                commands.Log(" !> DataReceived(client, b'foo')",
                             "debug") << commands.Log("baz", "info"))
        assert repr(tlayer) == "TLayer(state: bar)"
예제 #14
0
 def state_foo(self,
               event: events.Event) -> layer.CommandGenerator[None]:
     assert isinstance(event, events.Start)
     yield commands.OpenConnection(self.context.server)
     self.state = self.state_bar
예제 #15
0
 def _handle_event(
         self, event: events.Event) -> layer.CommandGenerator[None]:
     yield commands.OpenConnection(self.context.server)
     yield commands.OpenConnection(self.context.server)
예제 #16
0
def test_dataclasses(tconn):
    assert repr(commands.SendData(tconn, b"foo"))
    assert repr(commands.OpenConnection(tconn))
    assert repr(commands.CloseConnection(tconn))
    assert repr(commands.GetSocket(tconn))
    assert repr(commands.Log("hello", "info"))