[Debug] Virtual Printer proxy: diagnostic port probing for A1 (#757)

A1/A1 Mini printers fail to connect through VP proxy mode while
    X1C/P2S/H2C work fine with identical transparent TCP proxy code.
    Root cause unknown — the proxy is model-agnostic, so the failure
    suggests BambuStudio uses a different connection flow for A1 models
    that hits ports we don't proxy.

    Add diagnostic probe listeners on ports 21, 80, and 443 on each
    proxy VP's dedicated bind IP. If the slicer connects to any of
    these un-proxied ports, a WARNING is logged so debug logs and
    tcpdump can reveal what's missing.
This commit is contained in:
maziggy
2026-03-20 10:52:42 +01:00
parent 332a7c6ac8
commit f6a85031e6
2 changed files with 38 additions and 0 deletions
@@ -1463,6 +1463,7 @@ class SlicerProxyManager:
self._rtsp_proxy: TCPProxy | None = None
self._bind_proxies: list[TCPProxy] = []
self._bind_server = None
self._probe_servers: list[asyncio.Server] = []
self._tasks: list[asyncio.Task] = []
# FTP passive data port range — Bambu printers typically use ports in
@@ -1650,6 +1651,23 @@ class SlicerProxyManager:
)
)
# Diagnostic probe: listen on common un-proxied ports to detect
# if the slicer tries to reach a service we don't handle.
if self.bind_address and self.bind_address != "0.0.0.0":
for probe_port in (21, 80, 443):
try:
srv = await asyncio.start_server(
lambda r, w, p=probe_port: self._probe_handler(r, w, p),
self.bind_address,
probe_port,
)
self._probe_servers.append(srv)
except OSError:
pass # Port in use or no permission — skip
if self._probe_servers:
probed = [s.sockets[0].getsockname()[1] for s in self._probe_servers if s.sockets]
logger.info("Proxy diagnostic: probing un-proxied ports %s on %s", probed, self.bind_address)
logger.info(
"Slicer proxy started for %s (transparent TCP + MQTT TLS, %d FTP data ports)",
self.target_host,
@@ -1696,6 +1714,10 @@ class SlicerProxyManager:
await dp.stop()
self._ftp_data_proxies = []
for srv in self._probe_servers:
srv.close()
self._probe_servers = []
# Cancel tasks
for task in self._tasks:
task.cancel()
@@ -1712,6 +1734,21 @@ class SlicerProxyManager:
self._tasks = []
logger.info("Slicer proxy stopped")
async def _probe_handler(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter, port: int) -> None:
"""Log unexpected connections on un-proxied ports for diagnostics."""
peername = writer.get_extra_info("peername")
client = f"{peername[0]}:{peername[1]}" if peername else "unknown"
logger.warning(
"PROBE: slicer connected to un-proxied port %d from %s — this port may need proxying",
port,
client,
)
writer.close()
try:
await writer.wait_closed()
except OSError:
pass
def _log_activity(self, name: str, message: str) -> None:
"""Log activity via callback if configured."""
if self.on_activity: