V2.3.19 — Fix FTPS+TLS 1.3 "426 Connection reset" (close_notify flush)
Bug fix release. Resolves the V2.3.15-deferred FTPS + TLS 1.3 "426 Connection reset by peer" failure on the data channel.
Root cause
io_close was calling mbedtls_ssl_close_notify() and immediately closing the TCP socket without checking the return value. close_notify is non-blocking and frequently returns MBEDTLS_ERR_SSL_WANT_WRITE right after a large body upload while the lwIP TCP send buffer drains, so the close_notify alert never made it onto the wire.
RFC 8446 §6.1: TLS 1.3 servers MUST treat TCP close without close_notify as a possible truncation attack. The project's LAN FTPS server reacted with "426 Connection reset by peer" on the data channel after the body otherwise transferred cleanly. TLS 1.2 servers tolerate fire-and-forget close for backwards compatibility with old broken clients, so the bug only manifested once V2.3.5 re-enabled TLS 1.3.
Diagnosis
Comparing FileZilla (which loops close_notify until the alert is actually flushed — standard mature-TLS-client behaviour) against our fire-and-forget call. The cipher (AES-128-GCM) and key exchange (ECDHE-secp384r1-RSA-PSS-RSAE-SHA256) FileZilla negotiated against the same server were both well within mbedTLS's defaults, ruling out crypto-suite mismatch — leaving the close path as the only material difference.
Fix
log_ftp.c::io_close: deadline-bounded retry loop using the same WANT_READ / WANT_WRITE continuation pattern already used in io_send_all and io_recv1. 2 s deadline keeps us from blocking forever on a stalled socket; in normal operation the loop fires once or twice and exits in <50 ms. ~15 LOC.
Scope
- HTTPS targets (Madavi / SC / Radmon / OSM / aqi.eco) own their own close path inside
esp_http_client; not touched. They have not exhibited analogous issues — cloud terminators are more permissive than the LAN FTPS server about truncation. ftp_tls12_onlycheckbox default stays ON for one release as a safety net; if V2.3.19 soaks cleanly we can flip to OFF in V2.3.20.
Validation
- All three boards built clean (heltec_v2 43% partition free, heltec_v2_4mb 39%, feathers3_d 43%).
V2.3.19embedded version verified in all three binaries.
OTA-safe from V2.3.18 (no partition layout changes, no sdkconfig changes).
15 release artefacts (5 × 3 boards): heltec_v2 (8 MB Heltec WiFi Kit 32 V2), heltec_v2_4mb (4 MB knock-off variants), feathers3_d (UM FeatherS3 + PSRAM).