Exemplo n.º 1
0
    def test_simple(self, tctx):
        nl = layer.NextLayer(tctx, ask_on_start=True)
        nl.debug = "  "
        playbook = tutils.Playbook(nl, hooks=True)

        assert (
                playbook
                << layer.NextLayerHook(nl)
                >> tutils.reply()
                >> events.DataReceived(tctx.client, b"foo")
                << layer.NextLayerHook(nl)
                >> tutils.reply()
                >> events.DataReceived(tctx.client, b"bar")
                << layer.NextLayerHook(nl)
        )
        assert nl.data_client() == b"foobar"
        assert nl.data_server() == b""

        nl.layer = tutils.EchoLayer(tctx)
        assert (
                playbook
                >> tutils.reply()
                << commands.SendData(tctx.client, b"foo")
                << commands.SendData(tctx.client, b"bar")
        )
Exemplo n.º 2
0
    def handle_connect_finish(self):
        if not self.flow.response:
            # Do not send any response headers as it breaks proxying non-80 ports on
            # Android emulators using the -http-proxy option.
            self.flow.response = http.Response(
                self.flow.request.data.http_version,
                200,
                b"Connection established",
                http.Headers(),
                b"",
                None,
                time.time(),
                time.time(),
            )

        if 200 <= self.flow.response.status_code < 300:
            self.child_layer = self.child_layer or layer.NextLayer(
                self.context)
            yield from self.child_layer.handle_event(events.Start())
            self._handle_event = self.passthrough
            yield SendHttp(
                ResponseHeaders(self.stream_id, self.flow.response, True),
                self.context.client)
            yield SendHttp(ResponseEndOfMessage(self.stream_id),
                           self.context.client)
        else:
            yield from self.send_response()
Exemplo n.º 3
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()
Exemplo n.º 4
0
    def __init__(self, context: Context) -> None:
        self.client = context.client
        self.transports = {}
        self.max_conns = collections.defaultdict(lambda: asyncio.Semaphore(5))

        # Ask for the first layer right away.
        # In a reverse proxy scenario, this is necessary as we would otherwise hang
        # on protocols that start with a server greeting.
        self.layer = layer.NextLayer(context, ask_on_start=True)
        self.timeout_watchdog = TimeoutWatchdog(self.on_timeout)
Exemplo n.º 5
0
    def state_connect(self):
        # Parse Connect Request
        if len(self.buf) < 5:
            return

        if self.buf[:3] != b"\x05\x01\x00":
            yield from self.socks_err(f"Unsupported SOCKS5 request: {self.buf!r}", SOCKS5_REP_COMMAND_NOT_SUPPORTED)
            return

        # Determine message length
        atyp = self.buf[3]
        message_len: int
        if atyp == SOCKS5_ATYP_IPV4_ADDRESS:
            message_len = 4 + 4 + 2
        elif atyp == SOCKS5_ATYP_IPV6_ADDRESS:
            message_len = 4 + 16 + 2
        elif atyp == SOCKS5_ATYP_DOMAINNAME:
            message_len = 4 + 1 + self.buf[4] + 2
        else:
            yield from self.socks_err(f"Unknown address type: {atyp}", SOCKS5_REP_ADDRESS_TYPE_NOT_SUPPORTED)
            return

        # Do we have enough bytes yet?
        if len(self.buf) < message_len:
            return

        # Parse host and port
        msg, self.buf = self.buf[:message_len], self.buf[message_len:]

        host: str
        if atyp == SOCKS5_ATYP_IPV4_ADDRESS:
            host = socket.inet_ntop(socket.AF_INET, msg[4:-2])
        elif atyp == SOCKS5_ATYP_IPV6_ADDRESS:
            host = socket.inet_ntop(socket.AF_INET6, msg[4:-2])
        else:
            host_bytes = msg[5:-2]
            host = host_bytes.decode("ascii", "replace")

        port, = struct.unpack("!H", msg[-2:])

        # We now have all we need, let's get going.
        self.context.server.address = (host, port)
        self.child_layer = layer.NextLayer(self.context)

        # this already triggers the child layer's Start event,
        # but that's not a problem in practice...
        err = yield from self.finish_start()
        if err:
            yield commands.SendData(self.context.client, b"\x05\x04\x00\x01\x00\x00\x00\x00\x00\x00")
            yield commands.CloseConnection(self.context.client)
        else:
            yield commands.SendData(self.context.client, b"\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00")
            if self.buf:
                yield from self.child_layer.handle_event(events.DataReceived(self.context.client, self.buf))
                del self.buf
Exemplo n.º 6
0
 def __init__(
     self,
     context: context.Context,
     tunnel_connection: connection.Connection,
     conn: connection.Connection,
 ):
     super().__init__(context)
     self.tunnel_connection = tunnel_connection
     self.conn = conn
     self.child_layer = layer.NextLayer(self.context)
     self._event_queue = []
Exemplo n.º 7
0
    def test_func_references(self, tctx: Context):
        nl = layer.NextLayer(tctx)
        playbook = tutils.Playbook(nl)

        assert (playbook >> events.DataReceived(tctx.client, b"foo") <<
                layer.NextLayerHook(nl))
        nl.layer = tutils.EchoLayer(tctx)
        handle = nl.handle_event
        assert (playbook >> tutils.reply() << commands.SendData(
            tctx.client, b"foo"))
        sd, = handle(events.DataReceived(tctx.client, b"bar"))
        assert isinstance(sd, commands.SendData)
Exemplo n.º 8
0
    def _handle_event(self,
                      event: events.Event) -> layer.CommandGenerator[None]:
        assert platform.original_addr is not None
        socket = yield commands.GetSocket(self.context.client)
        try:
            self.context.server.address = platform.original_addr(socket)
        except Exception as e:
            yield commands.Log(f"Transparent mode failure: {e!r}")

        self.child_layer = layer.NextLayer(self.context)

        yield from self.finish_start()
Exemplo n.º 9
0
    def handle_connect_finish(self):
        if not self.flow.response:
            self.flow.response = http.make_connect_response(self.flow.request.data.http_version)

        if 200 <= self.flow.response.status_code < 300:
            yield SendHttp(ResponseHeaders(self.stream_id, self.flow.response), self.context.client)
            yield SendHttp(ResponseEndOfMessage(self.stream_id), self.context.client)
            self.child_layer = self.child_layer or layer.NextLayer(self.context)
            yield from self.child_layer.handle_event(events.Start())
            self._handle_event = self.passthrough
        else:
            yield from self.send_response()
Exemplo n.º 10
0
    def _handle_event(self,
                      event: events.Event) -> layer.CommandGenerator[None]:
        spec = server_spec.parse_with_mode(self.context.options.mode)[1]
        self.context.server.address = spec.address

        if spec.scheme not in ("http", "tcp"):
            if not self.context.options.keep_host_header:
                self.context.server.sni = spec.address[0]
            self.child_layer = tls.ServerTLSLayer(self.context)
        else:
            self.child_layer = layer.NextLayer(self.context)

        yield from self.finish_start()
Exemplo n.º 11
0
 def test_receive_close(self, tctx: Context, layer_found: bool):
     """Test that we abort a client connection which has disconnected without any layer being found."""
     nl = layer.NextLayer(tctx)
     playbook = tutils.Playbook(nl)
     assert (
         playbook >> events.DataReceived(tctx.client, b"foo") <<
         layer.NextLayerHook(nl) >> events.ConnectionClosed(tctx.client))
     if layer_found:
         nl.layer = tutils.RecordLayer(tctx)
         assert (playbook >> tutils.reply(to=-2))
         assert isinstance(nl.layer.event_log[-1], events.ConnectionClosed)
     else:
         assert (playbook >> tutils.reply(to=-2) <<
                 commands.CloseConnection(tctx.client))
Exemplo n.º 12
0
    def test_late_hook_reply(self, tctx: Context):
        """
        Properly handle case where we receive an additional event while we are waiting for
        a reply from the proxy core.
        """
        nl = layer.NextLayer(tctx)
        playbook = tutils.Playbook(nl)

        assert (playbook >> events.DataReceived(tctx.client, b"foo") <<
                layer.NextLayerHook(nl) >> events.DataReceived(
                    tctx.client, b"bar"))
        assert nl.data_client() == b"foo"  # "bar" is paused.
        nl.layer = tutils.EchoLayer(tctx)

        assert (playbook >> tutils.reply(to=-2) << commands.SendData(
            tctx.client, b"foo") << commands.SendData(tctx.client, b"bar"))
Exemplo n.º 13
0
 def test_repr(self, tctx: Context):
     nl = layer.NextLayer(tctx)
     nl.layer = tutils.EchoLayer(tctx)
     assert repr(nl)
     assert nl.stack_pos
     assert nl.layer.stack_pos
Exemplo n.º 14
0
 def test_repr(self, tctx):
     nl = layer.NextLayer(tctx)
     nl.layer = tutils.EchoLayer(tctx)
     assert repr(nl)
Exemplo n.º 15
0
 def _handle_event(self,
                   event: events.Event) -> layer.CommandGenerator[None]:
     child_layer = layer.NextLayer(self.context)
     self._handle_event = child_layer.handle_event
     yield from child_layer.handle_event(event)
Exemplo n.º 16
0
    def _handle_event(self,
                      event: events.Event) -> layer.CommandGenerator[None]:
        if isinstance(event, events.Start):
            pass
        elif isinstance(event, events.DataReceived):
            self.buf += event.data

            if not self.greeted:
                # Parse Client Greeting
                if len(self.buf) < 2:
                    return

                if self.buf[0] != SOCKS5_VERSION:
                    if self.buf[:3].isupper():
                        guess = "Probably not a SOCKS request but a regular HTTP request. "
                    else:
                        guess = ""
                    yield from self.socks_err(
                        guess +
                        "Invalid SOCKS version. Expected 0x05, got 0x%x" %
                        self.buf[0])
                    return

                n_methods = self.buf[1]
                if len(self.buf) < 2 + n_methods:
                    return
                if SOCKS5_METHOD_NO_AUTHENTICATION_REQUIRED not in self.buf[
                        2:2 + n_methods]:
                    yield from self.socks_err(
                        "mitmproxy only supports SOCKS without authentication",
                        SOCKS5_METHOD_NO_ACCEPTABLE_METHODS)
                    return

                # Send Server Greeting
                # Ver = SOCKS5, Auth = NO_AUTH
                yield commands.SendData(self.context.client, b"\x05\x00")
                self.buf = self.buf[2 + n_methods:]
                self.greeted = True

            # Parse Connect Request
            if len(self.buf) < 4:
                return

            if self.buf[:3] != b"\x05\x01\x00":
                yield from self.socks_err(
                    f"Unsupported SOCKS5 request: {self.buf!r}",
                    SOCKS5_REP_COMMAND_NOT_SUPPORTED)
                return

            # Determine message length
            atyp = self.buf[3]
            message_len: int
            if atyp == SOCKS5_ATYP_IPV4_ADDRESS:
                message_len = 4 + 4 + 2
            elif atyp == SOCKS5_ATYP_IPV6_ADDRESS:
                message_len = 4 + 16 + 2
            elif atyp == SOCKS5_ATYP_DOMAINNAME:
                message_len = 4 + 1 + self.buf[4] + 2
            else:
                yield from self.socks_err(
                    f"Unknown address type: {atyp}",
                    SOCKS5_REP_ADDRESS_TYPE_NOT_SUPPORTED)
                return

            # Do we have enough bytes yet?
            if len(self.buf) < message_len:
                return

            # Parse host and port
            msg, self.buf = self.buf[:message_len], self.buf[message_len:]

            host: str
            if atyp == SOCKS5_ATYP_IPV4_ADDRESS:
                host = socket.inet_ntop(socket.AF_INET, msg[4:-2])
            elif atyp == SOCKS5_ATYP_IPV6_ADDRESS:
                host = socket.inet_ntop(socket.AF_INET6, msg[4:-2])
            else:
                host_bytes = msg[5:-2]
                host = host_bytes.decode("ascii", "replace")

            port, = struct.unpack("!H", msg[-2:])

            # We now have all we need, let's get going.
            self.context.server.address = (host, port)
            self.child_layer = layer.NextLayer(self.context)

            # this already triggers the child layer's Start event,
            # but that's not a problem in practice...
            err = yield from self.finish_start()
            if err:
                yield commands.SendData(
                    self.context.client,
                    b"\x05\x04\x00\x01\x00\x00\x00\x00\x00\x00")
                yield commands.CloseConnection(self.context.client)
            else:
                yield commands.SendData(
                    self.context.client,
                    b"\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00")
                if self.buf:
                    yield from self.child_layer.handle_event(
                        events.DataReceived(self.context.client, self.buf))
                    del self.buf

        elif isinstance(event, events.ConnectionClosed):
            if self.buf:
                yield commands.Log(
                    f"Client closed connection before completing SOCKS5 handshake: {self.buf!r}"
                )
            yield commands.CloseConnection(event.connection)
        else:
            raise AssertionError(f"Unknown event: {event}")