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") )
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()
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()
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)
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
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 = []
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)
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()
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()
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()
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))
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"))
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
def test_repr(self, tctx): nl = layer.NextLayer(tctx) nl.layer = tutils.EchoLayer(tctx) assert repr(nl)
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)
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}")