From 8a14a9fbdf155e22d8db23fcd9860681cd593e77 Mon Sep 17 00:00:00 2001 From: Mark Mennell Date: Tue, 11 Aug 2026 09:46:31 +1000 Subject: [PATCH 1/2] Terminate on unsupported challenge version instead of sending code 2 A challenger's next read after sending CHALLENGE is exactly the 32-byte CHALLENGE-RESPONSE hash, so a response code written into that stream is indistinguishable from the start of a hash. Close without responding (SPEC SS10.5); code 2 remains for unsupported MESSAGE versions, where the peer's first read is always a response code. Co-Authored-By: Claude Fable 5 --- cmd/fmsgd/host.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/fmsgd/host.go b/cmd/fmsgd/host.go index 63825fc..f582886 100644 --- a/cmd/fmsgd/host.go +++ b/cmd/fmsgd/host.go @@ -693,9 +693,9 @@ func readVersionOrChallenge(c net.Conn, r *bufio.Reader, h *FMsgHeader) (bool, e if challengeVersion == 1 { return true, handleChallenge(c, r) } - if err := sendCode(c, RejectCodeUnsupportedVersion); err != nil { - log.Printf("WARN: failed to send unsupported version response: %s", err) - } + // TERMINATE without responding (SPEC §10.3/§10.5): the challenger's + // next read is exactly the 32-byte CHALLENGE-RESPONSE hash, so a + // response code here would be indistinguishable from hash bytes. return false, fmt.Errorf("unsupported challenge version: %d", challengeVersion) } if v != 1 { From 1187918df2cdd4003aabfc2e8bf392d76c65e105 Mon Sep 17 00:00:00 2001 From: Mark Mennell Date: Tue, 25 Aug 2026 09:37:47 +0800 Subject: [PATCH 2/2] Terminate on any unsupported version, retire code 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows the revised ruling in markmnl/fmsg#29: an unsupported version is one we do not know how to respond in, so both branches of readVersionOrChallenge now TERMINATE without writing anything — the message-version branch (first byte <= 128) as well as the challenge branch. Code 2 is therefore never sent on the wire. RejectCodeUnsupportedVersion and its responseCodeName case are removed, with 2 left unassigned so the numbering is unchanged; responseCodeName falls through to unknown(2) if one is ever read from a peer. Its host_test row goes with it. --- cmd/fmsgd/host.go | 15 ++++++--------- cmd/fmsgd/host_test.go | 1 - 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/cmd/fmsgd/host.go b/cmd/fmsgd/host.go index f582886..f42d1a6 100644 --- a/cmd/fmsgd/host.go +++ b/cmd/fmsgd/host.go @@ -33,8 +33,10 @@ const ( InboxDirName = "in" OutboxDirName = "out" + // Response codes (SPEC §9). 2 was "unsupported version"; unsupported + // versions now TERMINATE without responding (SPEC §10.3/§10.5), so it is + // never sent and is left unassigned to keep the numbering stable. RejectCodeInvalid uint8 = 1 - RejectCodeUnsupportedVersion uint8 = 2 RejectCodeUndisclosed uint8 = 3 RejectCodeTooBig uint8 = 4 RejectCodeInsufficentResources uint8 = 5 @@ -64,8 +66,6 @@ func responseCodeName(code uint8) string { switch code { case RejectCodeInvalid: return "invalid" - case RejectCodeUnsupportedVersion: - return "unsupported version" case RejectCodeUndisclosed: return "undisclosed" case RejectCodeTooBig: @@ -693,15 +693,12 @@ func readVersionOrChallenge(c net.Conn, r *bufio.Reader, h *FMsgHeader) (bool, e if challengeVersion == 1 { return true, handleChallenge(c, r) } - // TERMINATE without responding (SPEC §10.3/§10.5): the challenger's - // next read is exactly the 32-byte CHALLENGE-RESPONSE hash, so a - // response code here would be indistinguishable from hash bytes. + // TERMINATE without responding (SPEC §10.3/§10.5): an unsupported + // version is one we do not know how to respond in. return false, fmt.Errorf("unsupported challenge version: %d", challengeVersion) } if v != 1 { - if err := sendCode(c, RejectCodeUnsupportedVersion); err != nil { - log.Printf("WARN: failed to send unsupported version response: %s", err) - } + // TERMINATE without responding (SPEC §10.3/§10.5). return false, fmt.Errorf("unsupported message version: %d", v) } h.Version = v diff --git a/cmd/fmsgd/host_test.go b/cmd/fmsgd/host_test.go index c38db03..a6fe6fe 100644 --- a/cmd/fmsgd/host_test.go +++ b/cmd/fmsgd/host_test.go @@ -162,7 +162,6 @@ func TestResponseCodeName(t *testing.T) { want string }{ {RejectCodeInvalid, "invalid"}, - {RejectCodeUnsupportedVersion, "unsupported version"}, {RejectCodeUndisclosed, "undisclosed"}, {RejectCodeTooBig, "too big"}, {RejectCodeInsufficentResources, "insufficient resources"},