Kaynağa Gözat

fix(ftp): cap TLS to v1.2 for X2D FTPS to dodge WRONG_VERSION_NUMBER on firmware 01.01.00.00 (#1638)

  Reporter @vasmarfas saw X2D archive cards land almost empty - only print
  time, no filament weight / layers / MakerWorld link / thumbnail - and
  Spoolman filament-usage tracking went silent on the same printer.

  Support bundle traces the end-to-end: at print start
  backend/app/main.py::on_print_start tries the usual FTP-download dance
  for the 3MF, every implicit-FTPS connect attempt to the X2D fails with
  `[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1032)`, ~2
  minutes later "Could not find 3MF file for print" -> "Created fallback
  archive". Fallback path writes file_path="", file_size=0,
  content_hash=NULL, no layers / filament / model-link fields. Spoolman
  tracking degrades from the same root cause - both depend on the 3MF
  metadata parser.

  Proximate cause: Python 3.13's default ssl.create_default_context()
  negotiates TLS 1.3, the X2D's implicit-FTPS server on port 990 rejects
  the ClientHello. Same family as the P2S 01.02.00.00 bug from #1401
  (post-Python-3.13 TLS-1.3 breakage), different wire-level failure mode
  (P2S completes the handshake and truncates with 426; X2D fails the
  handshake outright).

  Same fix shape: add X2D to backend/app/services/ftp_profiles.py with
  cap_tls_v1_2=True, plus N6 -> X2D SSDP alias. Every other model stays
  on negotiated TLS 1.3.

  Honest caveat: hypothesis-driven trial, not a confirmed root-cause fix.
  WRONG_VERSION_NUMBER could equally describe the X2D switching to
  explicit FTPS (AUTH TLS on plaintext greeting) or moving FTPS to a
  different port - either would need a different code path. Reporter has
  been asked to test this build; if the cap doesn't clear it the registry
  slot stays useful and the next diagnostic round goes to openssl
  s_client from a network-adjacent host.
maziggy 3 ay önce
ebeveyn
işleme
e802bfc806

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
CHANGELOG.md


+ 13 - 0
backend/app/services/ftp_profiles.py

@@ -75,12 +75,25 @@ _PROFILES: dict[str, FTPProfile] = {
     "P2S": FTPProfile(
         cap_tls_v1_2=True,
     ),
+    # X2D firmware 01.01.00.00 fails the implicit-FTPS handshake on
+    # port 990 with ``[SSL: WRONG_VERSION_NUMBER]`` against Python
+    # 3.13's default TLS-1.3 ClientHello (#1638, reporter @vasmarfas).
+    # Without the 3MF download the print falls through to the no-3MF
+    # fallback archive path and the card lands almost empty (no
+    # filament total, no layers, no MakerWorld link). Cap to TLS 1.2
+    # by analogy with P2S; if the symptom turns out to be a different
+    # FTPS variant on the X2D (explicit AUTH TLS, different port) the
+    # entry stays useful as a per-model tuning slot for the follow-up.
+    "X2D": FTPProfile(
+        cap_tls_v1_2=True,
+    ),
 }
 
 # SSDP internal codes that should resolve to a display-name profile.
 # Mirrors the same map in :mod:`camera_profiles`.
 _MODEL_ALIASES: dict[str, str] = {
     "N7": "P2S",  # P2S internal SSDP code
+    "N6": "X2D",  # X2D internal SSDP code
 }
 
 

+ 16 - 0
backend/tests/unit/services/test_ftp_profiles.py

@@ -43,10 +43,26 @@ def test_p2s_internal_ssdp_code_resolves_to_p2s():
     assert profile.cap_tls_v1_2 is True
 
 
+def test_x2d_caps_tls_v1_2():
+    """X2D firmware 01.01.00.00 fails implicit-FTPS handshake on port
+    990 with WRONG_VERSION_NUMBER against Python 3.13's TLS-1.3 default
+    (#1638, reporter @vasmarfas). The profile caps to TLS 1.2 by
+    analogy with P2S."""
+    profile = get_ftp_profile("X2D")
+    assert profile.cap_tls_v1_2 is True
+
+
+def test_x2d_internal_ssdp_code_resolves_to_x2d():
+    """SSDP internal code N6 → X2D profile."""
+    profile = get_ftp_profile("N6")
+    assert profile.cap_tls_v1_2 is True
+
+
 def test_lookup_is_case_insensitive():
     """Printer.model may carry mixed case; the lookup normalises."""
     assert get_ftp_profile("p2s").cap_tls_v1_2 is True
     assert get_ftp_profile("P2s").cap_tls_v1_2 is True
+    assert get_ftp_profile("x2d").cap_tls_v1_2 is True
 
 
 def test_non_capped_models_still_default():

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor