def state_auth(self): if len(self.buf) < 3: return # Parsing username and password, which is somewhat atrocious user_len = self.buf[1] if len(self.buf) < 3 + user_len: return pass_len = self.buf[2 + user_len] if len(self.buf) < 3 + user_len + pass_len: return user = self.buf[2:(2 + user_len)].decode("utf-8", "backslashreplace") password = self.buf[(3 + user_len):(3 + user_len + pass_len)].decode("utf-8", "backslashreplace") data = Socks5AuthData(self.context.client, user, password) yield Socks5AuthHook(data) if not data.valid: # The VER field contains the current **version of the subnegotiation**, which is X'01'. yield commands.SendData(self.context.client, b"\x01\x01") yield from self.socks_err("authentication failed") return yield commands.SendData(self.context.client, b"\x01\x00") self.buf = self.buf[3 + user_len + pass_len:] self.state = self.state_connect yield from self.state()
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)
def relay_messages( self, event: events.ConnectionEvent) -> layer.CommandGenerator[None]: from_client = event.connection == self.context.client send_to: Connection if from_client: send_to = self.context.server else: send_to = self.context.client if isinstance(event, events.DataReceived): if self.flow: tcp_message = tcp.TCPMessage(from_client, event.data) self.flow.messages.append(tcp_message) yield TcpMessageHook(self.flow) yield commands.SendData(send_to, tcp_message.content) else: yield commands.SendData(send_to, event.data) elif isinstance(event, events.ConnectionClosed): all_done = not ( (self.context.client.state & ConnectionState.CAN_READ) or (self.context.server.state & ConnectionState.CAN_READ)) if all_done: if self.context.server.state is not ConnectionState.CLOSED: yield commands.CloseConnection(self.context.server) if self.context.client.state is not ConnectionState.CLOSED: yield commands.CloseConnection(self.context.client) self._handle_event = self.done if self.flow: yield TcpEndHook(self.flow) else: yield commands.CloseConnection(send_to, half_close=True)
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"))
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
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"))
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 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 test_simple(self, tctx): playbook = tutils.Playbook(tls.ServerTLSLayer(tctx)) tctx.server.state = ConnectionState.OPEN tctx.server.address = ("example.mitmproxy.org", 443) tctx.server.sni = b"example.mitmproxy.org" tssl = SSLTest(server_side=True) # send ClientHello data = tutils.Placeholder(bytes) assert ( playbook << 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() interact(playbook, tctx.server, tssl) # finish server handshake tssl.do_handshake() assert ( playbook >> events.DataReceived(tctx.server, tssl.bio_read()) << None ) assert tctx.server.tls_established # Echo assert ( playbook >> events.DataReceived(tctx.client, b"foo") << layer.NextLayerHook(tutils.Placeholder()) >> tutils.reply_next_layer(TlsEchoLayer) << commands.SendData(tctx.client, b"foo") ) _test_echo(playbook, tssl, tctx.server) with pytest.raises(ssl.SSLWantReadError): tssl.obj.unwrap() assert ( playbook >> events.DataReceived(tctx.server, tssl.bio_read()) << commands.CloseConnection(tctx.server) >> events.ConnectionClosed(tctx.server) << None )
def state_greet(self): 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 "proxyauth" in self.context.options and self.context.options.proxyauth: method = SOCKS5_METHOD_USER_PASSWORD_AUTHENTICATION self.state = self.state_auth else: method = SOCKS5_METHOD_NO_AUTHENTICATION_REQUIRED self.state = self.state_connect if method not in self.buf[2:2 + n_methods]: method_str = "user/password" if method == SOCKS5_METHOD_USER_PASSWORD_AUTHENTICATION else "no" yield from self.socks_err( f"Client does not support SOCKS5 with {method_str} authentication.", SOCKS5_METHOD_NO_ACCEPTABLE_METHODS ) return yield commands.SendData(self.context.client, bytes([SOCKS5_VERSION, method])) self.buf = self.buf[2 + n_methods:] yield from self.state()
def patched_receive_handshake_data( self, data ) -> layer.CommandGenerator[typing.Tuple[bool, typing.Optional[str]]]: self.buf += data response_head = self.buf.maybe_extract_lines() if response_head: response_head = [bytes(x) for x in response_head] try: response = http1.read_response_head(response_head) except ValueError: return True, None challenge_message = extract_proxy_authenticate_msg( response_head) if 200 <= response.status_code < 300: if self.buf: yield from self.receive_data(data) del self.buf return True, None else: if not challenge_message: return True, None proxy_authorization = self.ntlm_context.get_ntlm_challenge_response_message( challenge_message) self.flow = build_connect_flow( self.context, ("Proxy-Authorization", proxy_authorization)) raw = http1.assemble_request(self.flow.request) yield commands.SendData(self.tunnel_connection, raw) return False, None else: return False, None
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 _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)
def start_handshake(self) -> layer.CommandGenerator[None]: if self.tunnel_connection.tls: # "Secure Web Proxy": We may have negotiated an ALPN when connecting to the upstream proxy. # The semantics are not really clear here, but we make sure that if we negotiated h2, # we act as an h2 client. self.conn.alpn = self.tunnel_connection.alpn if not self.send_connect: return (yield from super().start_handshake()) assert self.conn.address req = http.Request( host=self.conn.address[0], port=self.conn.address[1], method=b"CONNECT", scheme=b"", authority=f"{self.conn.address[0]}:{self.conn.address[1]}".encode( ), path=b"", http_version=b"HTTP/1.1", headers=http.Headers(), content=b"", trailers=None, timestamp_start=time.time(), timestamp_end=time.time(), ) raw = http1.assemble_request(req) yield commands.SendData(self.tunnel_connection, raw)
def test_immediate_disconnect(self, tctx: context.Context, close_at): """Test the scenario where the client is disconnecting during the handshake. This may happen because they are not interested in the connection anymore, or because they do not like the proxy certificate.""" playbook, client_layer, tssl_client = make_client_tls_layer( tctx, sni=b"wrong.host.mitmproxy.org") playbook.logs = True playbook >> events.DataReceived(tctx.client, tssl_client.bio_read()) playbook << tls.TlsClienthelloHook(tutils.Placeholder()) if close_at == "tls_clienthello": assert (playbook >> events.ConnectionClosed( tctx.client) >> tutils.reply(to=-2) << tls.TlsStartClientHook( tutils.Placeholder()) >> reply_tls_start_client() << commands.CloseConnection(tctx.client)) return playbook >> tutils.reply() playbook << tls.TlsStartClientHook(tutils.Placeholder()) if close_at == "tls_start_client": assert (playbook >> events.ConnectionClosed(tctx.client) >> reply_tls_start_client(to=-2) << commands.CloseConnection( tctx.client)) return assert (playbook >> reply_tls_start_client() << commands.SendData( tctx.client, tutils.Placeholder() ) >> events.ConnectionClosed(tctx.client) << commands.Log( "Client TLS handshake failed. The client disconnected during the handshake. " "If this happens consistently for wrong.host.mitmproxy.org, this may indicate that the " "client does not trust the proxy's certificate.", "info") << commands.CloseConnection(tctx.client))
def test_unsupported_protocol(self, tctx: context.Context): """Test the scenario where the server only supports an outdated TLS version by default.""" playbook = tutils.Playbook(tls.ServerTLSLayer(tctx)) tctx.server.address = ("example.mitmproxy.org", 443) tctx.server.state = ConnectionState.OPEN tctx.server.sni = "example.mitmproxy.org" # noinspection PyTypeChecker tssl = SSLTest(server_side=True, max_ver=ssl.TLSVersion.TLSv1_2) # send ClientHello data = tutils.Placeholder(bytes) assert ( playbook << tls.TlsStartServerHook(tutils.Placeholder()) >> reply_tls_start_server() << commands.SendData(tctx.server, data)) # receive ServerHello tssl.bio_write(data()) with pytest.raises(ssl.SSLError): tssl.do_handshake() # send back error tls_hook_data = tutils.Placeholder(TlsData) assert (playbook >> events.DataReceived(tctx.server, tssl.bio_read( )) << commands.Log( "Server TLS handshake failed. The remote server and mitmproxy cannot agree on a TLS version" " to use. You may need to adjust mitmproxy's tls_version_server_min option.", "warn") << tls.TlsFailedServerHook(tls_hook_data) >> tutils.reply() << commands.CloseConnection(tctx.server)) assert tls_hook_data().conn.error
def test_mitmproxy_ca_is_untrusted(self, tctx: context.Context): """Test the scenario where the client doesn't trust the mitmproxy CA.""" playbook, client_layer, tssl_client = make_client_tls_layer(tctx, sni=b"wrong.host.mitmproxy.org") playbook.logs = True 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()) with pytest.raises(ssl.SSLCertVerificationError): tssl_client.do_handshake() # Finish Handshake assert ( playbook >> events.DataReceived(tctx.client, tssl_client.bio_read()) << commands.Log("Client TLS handshake failed. The client does not trust the proxy's certificate " "for wrong.host.mitmproxy.org (sslv3 alert bad certificate)", "warn") << commands.CloseConnection(tctx.client) >> events.ConnectionClosed(tctx.client) ) assert not tctx.client.tls_established
def send(self, event: HttpEvent) -> layer.CommandGenerator[None]: assert event.stream_id == self.stream_id if isinstance(event, ResponseHeaders): self.response = response = event.response if response.is_http2: response = response.copy() # Convert to an HTTP/1 response. response.http_version = "HTTP/1.1" # not everyone supports empty reason phrases, so we better make up one. response.reason = status_codes.RESPONSES.get( response.status_code, "") # Shall we set a Content-Length header here if there is none? # For now, let's try to modify as little as possible. raw = http1.assemble_response_head(response) yield commands.SendData(self.conn, raw) elif isinstance(event, ResponseData): assert self.response if "chunked" in self.response.headers.get("transfer-encoding", "").lower(): raw = b"%x\r\n%s\r\n" % (len(event.data), event.data) else: raw = event.data if raw: yield commands.SendData(self.conn, raw) elif isinstance(event, ResponseEndOfMessage): assert self.response if "chunked" in self.response.headers.get("transfer-encoding", "").lower(): yield commands.SendData(self.conn, b"0\r\n\r\n") yield from self.mark_done(response=True) elif isinstance(event, ResponseProtocolError): if not self.response and event.code != status_codes.NO_RESPONSE: resp = http.Response.make( event.code, format_error(event.code, event.message), http.Headers( Server=version.MITMPROXY, Connection="close", Content_Type="text/html", )) raw = http1.assemble_response(resp) yield commands.SendData(self.conn, raw) if self.conn.state & ConnectionState.CAN_WRITE: yield commands.CloseConnection(self.conn) else: raise AssertionError(f"Unexpected event: {event}")
def relay_messages(self, event: events.Event) -> layer.CommandGenerator[None]: if isinstance(event, TcpMessageInjected): # we just spoof that we received data here and then process that regularly. event = events.DataReceived( self.context.client if event.message.from_client else self.context.server, event.message.content, ) assert isinstance(event, events.ConnectionEvent) from_client = event.connection == self.context.client send_to: Connection if from_client: send_to = self.context.server else: send_to = self.context.client if isinstance(event, events.DataReceived): if self.flow: tcp_message = tcp.TCPMessage(from_client, event.data) self.flow.messages.append(tcp_message) yield TcpMessageHook(self.flow) yield commands.SendData(send_to, tcp_message.content) else: yield commands.SendData(send_to, event.data) elif isinstance(event, events.ConnectionClosed): all_done = not ( (self.context.client.state & ConnectionState.CAN_READ) or (self.context.server.state & ConnectionState.CAN_READ)) if all_done: if self.context.server.state is not ConnectionState.CLOSED: yield commands.CloseConnection(self.context.server) if self.context.client.state is not ConnectionState.CLOSED: yield commands.CloseConnection(self.context.client) self._handle_event = self.done if self.flow: yield TcpEndHook(self.flow) self.flow.live = False else: yield commands.CloseConnection(send_to, half_close=True) else: raise AssertionError(f"Unexpected event: {event}")
def interact(playbook: tutils.Playbook, conn: context.Connection, tssl: SSLTest): data = tutils.Placeholder(bytes) assert ( playbook >> events.DataReceived(conn, tssl.bio_read()) << commands.SendData(conn, data) ) tssl.bio_write(data())
def test_not_connected(self, tctx: context.Context): """Test that we don't do anything if no server connection exists.""" layer = tls.ServerTLSLayer(tctx) layer.child_layer = TlsEchoLayer(tctx) assert (tutils.Playbook(layer) >> events.DataReceived( tctx.client, b"Hello World") << commands.SendData( tctx.client, b"hello world"))
def _test_echo(playbook: tutils.Playbook, tssl: SSLTest, conn: connection.Connection) -> None: tssl.obj.write(b"Hello World") data = tutils.Placeholder(bytes) assert (playbook >> events.DataReceived(conn, tssl.bio_read()) << commands.SendData(conn, data)) tssl.bio_write(data()) assert tssl.obj.read() == b"hello world"
def tls_interact(self) -> layer.CommandGenerator[None]: while True: try: data = self.tls.bio_read(65535) except SSL.WantReadError: return # Okay, nothing more waiting to be sent. else: yield commands.SendData(self.conn, data)
def patched_start_handshake(self) -> layer.CommandGenerator[None]: assert self.conn.address self.ntlm_context = CustomNTLMContext(ctx) proxy_authorization = self.ntlm_context.get_ntlm_start_negotiate_message( ) self.flow = build_connect_flow( self.context, ("Proxy-Authorization", proxy_authorization)) yield HttpConnectUpstreamHook(self.flow) raw = http1.assemble_request(self.flow.request) yield commands.SendData(self.tunnel_connection, raw)
def send(self, event: HttpEvent) -> layer.CommandGenerator[None]: if isinstance(event, RequestProtocolError): yield commands.CloseConnection(self.conn) return if not self.stream_id: assert isinstance(event, RequestHeaders) self.stream_id = event.stream_id self.request = event.request assert self.stream_id == event.stream_id if isinstance(event, RequestHeaders): request = event.request if request.is_http2: # Convert to an HTTP/1 request. request = request.copy( ) # (we could probably be a bit more efficient here.) request.http_version = "HTTP/1.1" if "Host" not in request.headers and request.authority: request.headers.insert(0, "Host", request.authority) request.authority = "" raw = http1.assemble_request_head(request) yield commands.SendData(self.conn, raw) elif isinstance(event, RequestData): assert self.request if "chunked" in self.request.headers.get("transfer-encoding", "").lower(): raw = b"%x\r\n%s\r\n" % (len(event.data), event.data) else: raw = event.data if raw: yield commands.SendData(self.conn, raw) elif isinstance(event, RequestEndOfMessage): assert self.request if "chunked" in self.request.headers.get("transfer-encoding", "").lower(): yield commands.SendData(self.conn, b"0\r\n\r\n") elif http1.expected_http_body_size(self.request, self.response) == -1: yield commands.CloseConnection(self.conn, half_close=True) yield from self.mark_done(request=True) else: raise AssertionError(f"Unexpected event: {event}")
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 start_handshake(self) -> layer.CommandGenerator[None]: if self.tunnel_connection.tls: # "Secure Web Proxy": We may have negotiated an ALPN when connecting to the upstream proxy. # The semantics are not really clear here, but we make sure that if we negotiated h2, # we act as an h2 client. self.conn.alpn = self.tunnel_connection.alpn if not self.send_connect: return (yield from super().start_handshake()) assert self.conn.address req = http.make_connect_request(self.conn.address) raw = http1.assemble_request(req) yield commands.SendData(self.tunnel_connection, raw)
def finish_handshake(playbook: tutils.Playbook, conn: connection.Connection, tssl: SSLTest): data = tutils.Placeholder(bytes) tls_hook_data = tutils.Placeholder(TlsData) if isinstance(conn, connection.Client): established_hook = tls.TlsEstablishedClientHook(tls_hook_data) else: established_hook = tls.TlsEstablishedServerHook(tls_hook_data) assert ( playbook >> events.DataReceived(conn, tssl.bio_read()) << established_hook >> tutils.reply() << commands.SendData(conn, data)) assert tls_hook_data().conn.error is None tssl.bio_write(data())
def socks_err( self, message: str, reply_code: Optional[int] = None, ) -> layer.CommandGenerator[None]: if reply_code is not None: yield commands.SendData( self.context.client, bytes([SOCKS5_VERSION, reply_code]) + b"\x00\x01\x00\x00\x00\x00\x00\x00") yield commands.CloseConnection(self.context.client) yield commands.Log(message) self._handle_event = self.done
def test_remote_speaks_no_tls(self, tctx): playbook = tutils.Playbook(tls.ServerTLSLayer(tctx)) tctx.server.state = ConnectionState.OPEN tctx.server.sni = "example.mitmproxy.org" # send ClientHello, receive random garbage back data = tutils.Placeholder(bytes) assert (playbook << tls.TlsStartHook(tutils.Placeholder( )) >> reply_tls_start( ) << commands.SendData(tctx.server, data) >> events.DataReceived( tctx.server, b"HTTP/1.1 404 Not Found\r\n" ) << commands.Log( "Server TLS handshake failed. The remote server does not speak TLS.", "warn") << commands.CloseConnection(tctx.server))