mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-17 11:25:48 +02:00
Fix VP bind server rejecting TLS connections on port 3002 (#559)
BambuStudio uses TLS on port 3002 for certain printer models (e.g. A1 Mini / N1). The bind server only spoke plain TCP on both ports, so the TLS ClientHello was rejected as "invalid frame" and the slicer could never discover or connect to the virtual printer. Port 3002 now uses TLS (reusing the VP's existing certificate), port 3000 remains plain TCP. Also updated proxy-mode to use TLSProxy for the port 3002 bind proxy instead of raw TCPProxy.
This commit is contained in:
@@ -2,9 +2,12 @@
|
||||
|
||||
Bambu slicers (BambuStudio, OrcaSlicer) connect to a printer on port 3000
|
||||
or 3002 to perform the "bind with access code" handshake before using
|
||||
MQTT/FTP. The port varies by slicer version, so we listen on both.
|
||||
MQTT/FTP.
|
||||
|
||||
Protocol:
|
||||
Port 3000: plain TCP (legacy / some printer models).
|
||||
Port 3002: TLS (newer firmware, e.g. A1 Mini 01.07.x).
|
||||
|
||||
Protocol (same on both ports, only transport differs):
|
||||
- Framing: 0xA5A5 + uint16_le(total_msg_size) + JSON payload + 0xA7A7
|
||||
- Slicer sends: {"login":{"command":"detect","sequence_id":"20000"}}
|
||||
- Printer replies: {"login":{"bind":"free","command":"detect","connect":"lan",
|
||||
@@ -16,11 +19,15 @@ Protocol:
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import ssl
|
||||
import struct
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
BIND_PORTS = [3000, 3002]
|
||||
BIND_PORT_PLAIN = 3000
|
||||
BIND_PORT_TLS = 3002
|
||||
BIND_PORTS = [BIND_PORT_PLAIN, BIND_PORT_TLS]
|
||||
FRAME_HEADER = b"\xa5\xa5"
|
||||
FRAME_TRAILER = b"\xa7\xa7"
|
||||
HEADER_SIZE = 4 # 2 bytes magic + 2 bytes length
|
||||
@@ -33,8 +40,8 @@ class BindServer:
|
||||
In server mode, Bambuddy IS the printer — it responds with its own
|
||||
identity so the slicer can discover and bind to it.
|
||||
|
||||
Different BambuStudio versions connect on different ports (3000 or 3002),
|
||||
so we listen on both to ensure compatibility.
|
||||
Port 3000 is plain TCP, port 3002 is TLS. BambuStudio chooses which
|
||||
port to use based on the printer model discovered via SSDP.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
@@ -44,39 +51,66 @@ class BindServer:
|
||||
name: str,
|
||||
version: str = "01.00.00.00",
|
||||
bind_address: str = "0.0.0.0", # nosec B104
|
||||
cert_path: Path | None = None,
|
||||
key_path: Path | None = None,
|
||||
):
|
||||
self.serial = serial
|
||||
self.model = model
|
||||
self.name = name
|
||||
self.version = version
|
||||
self.bind_address = bind_address
|
||||
self.cert_path = cert_path
|
||||
self.key_path = key_path
|
||||
|
||||
self._servers: list[asyncio.Server] = []
|
||||
self._running = False
|
||||
|
||||
def _create_tls_context(self) -> ssl.SSLContext | None:
|
||||
"""Create SSL context for the TLS bind port (3002)."""
|
||||
if not self.cert_path or not self.key_path:
|
||||
return None
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
ctx.load_cert_chain(str(self.cert_path), str(self.key_path))
|
||||
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
return ctx
|
||||
|
||||
async def start(self) -> None:
|
||||
"""Start the bind server on ports 3000 and 3002."""
|
||||
"""Start the bind server on ports 3000 (plain) and 3002 (TLS)."""
|
||||
if self._running:
|
||||
return
|
||||
|
||||
self._running = True
|
||||
|
||||
tls_ctx = self._create_tls_context()
|
||||
if not tls_ctx:
|
||||
logger.warning("Bind server: no TLS cert provided, port %s will be plain TCP", BIND_PORT_TLS)
|
||||
|
||||
logger.info(
|
||||
"Starting bind server on ports %s (serial=%s, model=%s)",
|
||||
"Starting bind server on ports %s (serial=%s, model=%s, tls=%s)",
|
||||
BIND_PORTS,
|
||||
self.serial,
|
||||
self.model,
|
||||
tls_ctx is not None,
|
||||
)
|
||||
|
||||
try:
|
||||
for port in BIND_PORTS:
|
||||
use_tls = port == BIND_PORT_TLS and tls_ctx is not None
|
||||
try:
|
||||
server = await asyncio.start_server(
|
||||
self._handle_client,
|
||||
self.bind_address,
|
||||
port,
|
||||
ssl=tls_ctx if use_tls else None,
|
||||
)
|
||||
self._servers.append(server)
|
||||
logger.info("Bind server listening on %s:%s", self.bind_address, port)
|
||||
logger.info(
|
||||
"Bind server listening on %s:%s (%s)",
|
||||
self.bind_address,
|
||||
port,
|
||||
"TLS" if use_tls else "plain",
|
||||
)
|
||||
except OSError as e:
|
||||
if e.errno == 98:
|
||||
logger.warning("Bind server port %s already in use, skipping", port)
|
||||
|
||||
Reference in New Issue
Block a user