diff --git a/phoneuse/mitm_proxy.py b/phoneuse/mitm_proxy.py index 8c99083..42f94cd 100644 --- a/phoneuse/mitm_proxy.py +++ b/phoneuse/mitm_proxy.py @@ -21,6 +21,7 @@ CA_DIR = Path(os.environ.get("MITM_CA_DIR", EVENT_SERVER = os.environ.get("EVENT_SERVER_URL", "http://127.0.0.1:8765/events") PROXY_PORT = int(os.environ.get("MITM_PORT", "8080")) INTERCEPT_HOSTS = os.environ.get("MITM_INTERCEPT", "").split(",") +_cert_lock = asyncio.Lock() def ensure_ca(): @@ -158,8 +159,9 @@ async def handle_connect(client_r: asyncio.StreamReader, return try: - cert_path, key_path = await asyncio.get_event_loop().run_in_executor( - None, make_host_cert, host) + async with _cert_lock: + cert_path, key_path = await asyncio.get_event_loop().run_in_executor( + None, make_host_cert, host) except Exception as e: print(f"[mitm] cert failed for {host}: {e}") try: @@ -178,20 +180,16 @@ async def handle_connect(client_r: asyncio.StreamReader, ssl_cli_ctx.check_hostname = False ssl_cli_ctx.verify_mode = ssl.CERT_NONE - transport = client_w.transport - sock = transport.get_extra_info("socket") - if sock is None: - return - - import socket as _socket - raw_sock = sock.dup() - transport.close() - try: loop = asyncio.get_event_loop() - tls_sock = await loop.run_in_executor( - None, lambda: ssl_srv_ctx.wrap_socket(raw_sock, server_side=True)) - tls_r, tls_w = await asyncio.open_connection(sock=tls_sock) + old_transport = client_w.transport + protocol = old_transport.get_protocol() + # Upgrade the existing client connection to TLS in place (asyncio's + # native STARTTLS support) instead of detaching the raw fd — keeps + # the handshake inside the event loop with no socket hand-off race. + tls_transport = await loop.start_tls( + old_transport, protocol, ssl_srv_ctx, server_side=True) + tls_w = asyncio.StreamWriter(tls_transport, protocol, client_r, loop) srv_r, srv_w = await asyncio.wait_for( asyncio.open_connection(host, port, ssl=ssl_cli_ctx), timeout=10) @@ -199,7 +197,7 @@ async def handle_connect(client_r: asyncio.StreamReader, req_acc: bytearray = bytearray() resp_acc: bytearray = bytearray() await asyncio.gather( - pipe(tls_r, srv_w, req_acc), + pipe(client_r, srv_w, req_acc), pipe(srv_r, tls_w, resp_acc), return_exceptions=True) @@ -212,7 +210,7 @@ async def handle_connect(client_r: asyncio.StreamReader, "req_preview": req_acc[:512].decode("utf-8", errors="replace"), "ts": int(time.time()), }) - except Exception as e: + except Exception: pass