コード例 #1
0
def open_h2_server_conn():
    # this is a bit fake here (port 80, with alpn, but no tls - c'mon),
    # but we don't want to pollute our tests with TLS handshakes.
    s = Server(("example.com", 80))
    s.state = ConnectionState.OPEN
    s.alpn = b"h2"
    return s
コード例 #2
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
コード例 #3
0
    def get_connection(self, event: GetHttpConnection, *, reuse: bool = True) -> layer.CommandGenerator[None]:
        # Do we already have a connection we can re-use?
        if reuse:
            for connection in self.connections:
                # see "tricky multiplexing edge case" in make_http_connection for an explanation
                conn_is_pending_or_h2 = (
                    connection.alpn == b"h2"
                    or connection in self.waiting_for_establishment
                )
                h2_to_h1 = self.context.client.alpn == b"h2" and not conn_is_pending_or_h2
                connection_suitable = (
                    event.connection_spec_matches(connection)
                    and not h2_to_h1
                )
                if connection_suitable:
                    if connection in self.waiting_for_establishment:
                        self.waiting_for_establishment[connection].append(event)
                        return
                    elif connection.connected:
                        stream = self.command_sources.pop(event)
                        yield from self.event_to_child(stream, GetHttpConnectionCompleted(event, (connection, None)))
                        return
                    else:
                        pass  # the connection is at least half-closed already, we want a new one.

        can_use_context_connection = (
            self.context.server not in self.connections and
            self.context.server.connected and
            event.connection_spec_matches(self.context.server)
        )
        context = self.context.fork()

        stack = tunnel.LayerStack()

        if not can_use_context_connection:

            context.server = Server(event.address)
            if event.tls:
                context.server.sni = event.address[0]

            if event.via:
                assert event.via.scheme in ("http", "https")
                http_proxy = Server(event.via.address)

                if event.via.scheme == "https":
                    http_proxy.alpn_offers = tls.HTTP_ALPNS
                    http_proxy.sni = event.via.address[0]
                    stack /= tls.ServerTLSLayer(context, http_proxy)

                send_connect = not (self.mode == HTTPMode.upstream and not event.tls)
                stack /= _upstream_proxy.HttpUpstreamProxy(context, http_proxy, send_connect)
            if event.tls:
                stack /= tls.ServerTLSLayer(context)

        stack /= HttpClient(context)

        self.connections[context.server] = stack[0]
        self.waiting_for_establishment[context.server].append(event)

        yield from self.event_to_child(stack[0], events.Start())
コード例 #4
0
ファイル: test_tunnel.py プロジェクト: zhoulingmin/mitmproxy
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
ファイル: test_tunnel.py プロジェクト: zhoulingmin/mitmproxy
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_basic(self):
     s = Server(("address", 22))
     assert repr(s)
     assert str(s)
     s.timestamp_tls_setup = 1607780791
     assert str(s)
     s.alpn = b"foo"
     s.sockname = ("127.0.0.1", 54321)
     assert str(
         s) == "Server(address:22, state=closed, alpn=foo, src_port=54321)"
コード例 #7
0
ファイル: __init__.py プロジェクト: yangboyd/mitmproxy
    def handle_connect_upstream(self):
        assert self.context.server.via.scheme in ("http", "https")

        http_proxy = Server(self.context.server.via.address)

        stack = tunnel.LayerStack()
        if self.context.server.via.scheme == "https":
            http_proxy.sni = self.context.server.via.address[0]
            stack /= tls.ServerTLSLayer(self.context, http_proxy)
        stack /= _upstream_proxy.HttpUpstreamProxy(self.context, http_proxy, True)

        self.child_layer = stack[0]
        yield from self.handle_connect_finish()
コード例 #8
0
    def test_cannot_parse_clienthello(self, tctx: context.Context):
        """Test the scenario where we cannot parse the ClientHello"""
        playbook, client_layer, tssl_client = make_client_tls_layer(tctx)
        tls_hook_data = tutils.Placeholder(TlsData)

        invalid = b"\x16\x03\x01\x00\x00"

        assert (playbook >> events.DataReceived(
            tctx.client, invalid
        ) << commands.Log(
            f"Client TLS handshake failed. Cannot parse ClientHello: {invalid.hex()}",
            level="warn") << tls.TlsFailedClientHook(tls_hook_data) >>
                tutils.reply() << commands.CloseConnection(tctx.client))
        assert tls_hook_data().conn.error
        assert not tctx.client.tls_established

        # Make sure that an active server connection does not cause child layers to spawn.
        client_layer.debug = ""
        assert (playbook >> events.DataReceived(
            Server(None), b"data on other stream"
        ) << commands.Log(
            ">> DataReceived(server, b'data on other stream')", 'debug'
        ) << commands.Log(
            "Swallowing DataReceived(server, b'data on other stream') as handshake failed.",
            "debug"))
コード例 #9
0
ファイル: test_tunnel.py プロジェクト: zhoulingmin/mitmproxy
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")
        )
コード例 #10
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
コード例 #11
0
ファイル: test_tls.py プロジェクト: u2ubf989u2/mitmproxy
    def test_client_only(self, tctx: context.Context):
        """Test TLS with client only"""
        playbook, client_layer, tssl_client = make_client_tls_layer(tctx)
        client_layer.debug = "  "
        assert not tctx.client.tls_established

        # Send ClientHello, receive ServerHello
        data = tutils.Placeholder(bytes)
        assert (playbook >> events.DataReceived(
            tctx.client, tssl_client.bio_read()) << tls.TlsClienthelloHook(
                tutils.Placeholder()) >> tutils.reply() << tls.TlsStartHook(
                    tutils.Placeholder()) >> reply_tls_start() <<
                commands.SendData(tctx.client, data))
        tssl_client.bio_write(data())
        tssl_client.do_handshake()
        # Finish Handshake
        interact(playbook, tctx.client, tssl_client)

        assert tssl_client.obj.getpeercert(True)
        assert tctx.client.tls_established

        # Echo
        _test_echo(playbook, tssl_client, tctx.client)
        other_server = Server(None)
        assert (playbook >> events.DataReceived(other_server, b"Plaintext") <<
                commands.SendData(other_server, b"plaintext"))
コード例 #12
0
def request(flow: http.HTTPFlow) -> None:
    address = proxy_address(flow)

    is_proxy_change = address != flow.server_conn.via.address
    server_connection_already_open = flow.server_conn.timestamp_start is not None
    if is_proxy_change and server_connection_already_open:
        # server_conn already refers to an existing connection (which cannot be modified),
        # so we need to replace it with a new server connection object.
        flow.server_conn = Server(flow.server_conn.address)
    flow.server_conn.via = ServerSpec("http", address)
コード例 #13
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
コード例 #14
0
 def test_address(self):
     s = Server(("address", 22))
     s.address = ("example.com", 443)
     s.state = ConnectionState.OPEN
     with pytest.raises(RuntimeError):
         s.address = ("example.com", 80)
     # No-op assignment, allowed because it might be triggered by a Server.set_state() call.
     s.address = ("example.com", 443)
コード例 #15
0
    def __init__(self, flow: http.HTTPFlow, options: Options) -> None:
        client = flow.client_conn.copy()
        client.state = ConnectionState.OPEN

        context = Context(client, options)
        context.server = Server((flow.request.host, flow.request.port))
        context.server.tls = flow.request.scheme == "https"
        if options.mode.startswith("upstream:"):
            context.server.via = server_spec.parse_with_mode(options.mode)[1]

        super().__init__(context)

        self.layer = layers.HttpLayer(context, HTTPMode.transparent)
        self.layer.connections[client] = MockServer(flow, context.fork())
        self.flow = flow
        self.done = asyncio.Event()
コード例 #16
0
    def get_connection(self,
                       event: GetHttpConnection,
                       *,
                       reuse: bool = True) -> layer.CommandGenerator[None]:
        # Do we already have a connection we can re-use?
        if reuse:
            for connection in self.connections:
                connection_suitable = (
                    event.connection_spec_matches(connection))
                if connection_suitable:
                    if connection in self.waiting_for_establishment:
                        self.waiting_for_establishment[connection].append(
                            event)
                        return
                    elif connection.error:
                        stream = self.command_sources.pop(event)
                        yield from self.event_to_child(
                            stream,
                            GetHttpConnectionCompleted(
                                event, (None, connection.error)))
                        return
                    elif connection.connected:
                        # see "tricky multiplexing edge case" in make_http_connection for an explanation
                        h2_to_h1 = self.context.client.alpn == b"h2" and connection.alpn != b"h2"
                        if not h2_to_h1:
                            stream = self.command_sources.pop(event)
                            yield from self.event_to_child(
                                stream,
                                GetHttpConnectionCompleted(
                                    event, (connection, None)))
                            return
                    else:
                        pass  # the connection is at least half-closed already, we want a new one.

        context_connection_matches = (
            self.context.server not in self.connections
            and event.connection_spec_matches(self.context.server))
        can_use_context_connection = (context_connection_matches
                                      and self.context.server.connected)
        if context_connection_matches and self.context.server.error:
            stream = self.command_sources.pop(event)
            yield from self.event_to_child(
                stream,
                GetHttpConnectionCompleted(event,
                                           (None, self.context.server.error)))
            return

        context = self.context.fork()

        stack = tunnel.LayerStack()

        if not can_use_context_connection:

            context.server = Server(event.address)

            if event.via:
                context.server.via = event.via
                assert event.via.scheme in ("http", "https")
                # We always send a CONNECT request, *except* for plaintext absolute-form HTTP requests in upstream mode.
                send_connect = event.tls or self.mode != HTTPMode.upstream
                stack /= _upstream_proxy.HttpUpstreamProxy.make(
                    context, send_connect)
            if event.tls:
                # Assume that we are in transparent mode and lazily did not open a connection yet.
                # We don't want the IP (which is the address) as the upstream SNI, but the client's SNI instead.
                if self.mode == HTTPMode.transparent and event.address == self.context.server.address:
                    context.server.sni = self.context.client.sni or event.address[
                        0]
                else:
                    context.server.sni = event.address[0]
                stack /= tls.ServerTLSLayer(context)

        stack /= HttpClient(context)

        self.connections[context.server] = stack[0]
        self.waiting_for_establishment[context.server].append(event)

        yield from self.event_to_child(stack[0], events.Start())
コード例 #17
0
ファイル: test_connection.py プロジェクト: adibdz/mitmproxy
 def test_address(self):
     s = Server(("address", 22))
     s.address = ("example.com", 443)
     s.state = ConnectionState.OPEN
     with pytest.raises(RuntimeError):
         s.address = ("example.com", 80)