Skip to content

Fix tech-debt issues #7, #10; investigate/document #8, #14 - #21

Merged
sidick merged 1 commit into
mainfrom
tech-debt-cleanup
Aug 30, 2026
Merged

Fix tech-debt issues #7, #10; investigate/document #8, #14#21
sidick merged 1 commit into
mainfrom
tech-debt-cleanup

Conversation

@sidick

@sidick sidick commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Summary

#7 - mqtt.library connect-phase not abortable (fixed)

transport_bsdsocket_connect() now makes the socket non-blocking for the connect() attempt only, with a 30s elapsed-time budget (tool_now_ms()), matching the pattern the AmiSSL/OpenSSL transports already use for their handshake phase. This NDK's bsdsocket.library has no SO_ERROR/SOL_SOCKET (checked - genuinely absent), so getpeername() is the portable substitute for "did that connect actually succeed." gethostbyname() itself remains unabortable - noted in a comment as the harder remaining gap (no async-resolve API on this socket surface).

#10 - PUBLISH decode accepts DUP=1 with QoS 0 (fixed)

Decided per the issue's own framing: matched the existing precedent (empty-topic-name rejection for MQTT-4.7.3-1) rather than the tolerant-receiver alternative. mqtt_decode() now rejects DUP=1 on a QoS 0 PUBLISH as -MQTT_ERR_MALFORMED (MQTT-3.3.1-2), with a test and a new "Decode strictness" section in docs/PROTOCOL.md.

#14 - TLS cipher-suite measurement (investigated, recommend closing)

Measured real handshake time against a broker under Copperline at stock 14MHz, forcing each TLS 1.3 ciphersuite: AES-256-GCM ~12.93s, AES-128-GCM 12.22s, ChaCha20-Poly1305 12.18s - a ~6% spread, noise-level against the total. Confirms the original issue #3 reasoning: handshake cost is dominated by asymmetric crypto, not the symmetric cipher. Not building the feature; recommending closure as answered.

#8 - non-monotonic Amiga clock (investigated, documented, deferred)

A real fix needs timer.device's TR_GETSYSTIME, which needs per-task IORequest state threaded through every caller of tool_now_ms() - not a drop-in replacement, and statics are off the table for the same reason SocketBase/AmiSSLBase are. Documented the exact failure mode instead of an invasive redesign, and fixed a stale comment claiming the host clock still uses time() (it moved to CLOCK_MONOTONIC during the TLS work).

Test plan

  • make test - 250/250 (new DUP/QoS0 rejection test included).
  • make lint, make m68k - clean.
  • make test-target (on-target codec test) - clean, no regression from the mqtt_packet.c change.
  • sh tests/net/net-smoke.sh - real m68k, both mqtt_pub-static and library-linked mqtt_pub via the rewritten connect path.
  • sh tests/library/net-run.sh - full mqtt.library API on-target, no regression.
  • sh tests/library/reconn-run.sh - mco_AutoReconnect across a real broker outage (repeated connect() calls through the new code path) - full pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QWq1CRLCQy9AaD9UtLRSM9

#7 - mqtt.library: connect-phase not abortable (fixed)

transport_bsdsocket_connect() now makes the socket non-blocking for the
connect() attempt only (issue #7): a host that never responds (down,
firewalled, wrong port) used to block for the TCP stack's own connect
timeout (often 75s+) with no way to abort it, unlike the rest of the
transport (bsdsocket_recv() already gates on WaitSelect+SIGBREAKF_CTRL_C).
Added a 30s elapsed-time budget, matching the same pattern (and constant)
the AmiSSL/OpenSSL transports already use for their handshake phase.
This NDK's bsdsocket.library has no SO_ERROR/SOL_SOCKET to ask "did that
connect actually succeed" (checked - genuinely absent, not just unused),
so getpeername() is the portable substitute: it only succeeds on an
established connection. gethostbyname() itself is still not abortable -
noted in a comment as the remaining, harder gap (no async-resolve API on
this socket surface).

Verified end-to-end on real m68k: net-smoke (plain bsdsocket, both
mqtt_pub-static and library-linked mqtt_pub), library-net-smoke (full
mqtt.library API), and library-reconnect-smoke (mco_AutoReconnect across
a real broker outage - repeated connect() calls through the new code
path) all pass with no regression.

#10 - PUBLISH decode accepts DUP=1 with QoS 0 (fixed)

Decided per the issue's own framing: match the existing precedent (the
empty-topic-name rejection for MQTT-4.7.3-1) rather than keep the
tolerant-receiver posture. mqtt_decode() now rejects DUP=1 on a QoS 0
PUBLISH as -MQTT_ERR_MALFORMED (MQTT-3.3.1-2), with a test vector and a
new "Decode strictness" section in docs/PROTOCOL.md documenting both
rejections together.

#14 - TLS cipher-suite measurement (investigated, closing with data)

Built a throwaway on-target benchmark (not committed - one-off,
matching the amissl-spike precedent for this kind of investigation)
forcing each TLS 1.3 ciphersuite via SSL_CTX_set_ciphersuites() and
timing the handshake against a real broker under Copperline at stock
14MHz. Result: AES-256-GCM 12.92-12.94s, AES-128-GCM 12.22s, ChaCha20-
Poly1305 12.18s - a ~6% spread, noise-level against the total, not the
dramatic difference a lack-of-AES-acceleration theory might predict.
Confirms the original issue #3 reasoning: handshake cost is dominated by
asymmetric crypto (the cert/key exchange), which cipher-suite selection
doesn't touch - exposing SSL_CTX_set_cipher_list() as a speed lever
wouldn't meaningfully help. Recommending closure as answered rather than
building the feature.

#8 - non-monotonic Amiga clock (investigated, documented, deferred)

A real fix needs timer.device's TR_GETSYSTIME, which needs a per-task
IORequest - i.e. state threaded through every caller of tool_now_ms(),
not a drop-in replacement (statics are off the table for the same
multi-connection reason SocketBase/AmiSSLBase are - IORequests aren't
safe to share across the concurrent tasks mqtt.library's connection
subprocesses are). Given a clock actively changing mid-session is rare
in practice, documented the failure mode precisely instead of an
invasive API redesign: tool_clock.h's own comment now explains why
DateStamp() can't distinguish a clock step from genuine uint32_t
wraparound, and docs/PROTOCOL.md's Keepalive section cross-references
it. Also fixed a stale comment there claiming the host clock uses
time() - it was switched to CLOCK_MONOTONIC during the TLS work and the
comment never caught up.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QWq1CRLCQy9AaD9UtLRSM9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant