• mailsrvr: false 'DKIM body hash changed' error when a send fails mid-b

    From Rob Swindell@1:103/705 to GitLab issue in main/sbbs on Sun Oct 4 20:40:42 2026
    open https://gitlab.synchro.net/main/sbbs/-/work_items/1279

    When an outbound SMTP delivery's connection drops partway through sending the message body, the mail server logs a misleading DKIM error:

    ```
    12:11:34 2332 SEND connecting to port 25 on mx1.emailn.de [178.63.251.174] 12:11:42 2332 SEND TLS Session started successfully
    12:12:22 2332 SEND/TLS dbg 'WSAECONNABORTED: Connection was terminated due to a time-out or other failure' (-42) flushing data
    12:12:22 2332 SEND/TLS !DKIM body hash changed between sign and send - message delivery aborted
    12:12:22 2332 SEND !Delivery attempt #1 FAILED (mx1.emailn.de replied with: ```

    A second occurrence on 2026-08-17 (plain SMTP, no TLS) shows the same sequence: `Connection aborted by peer on send`, then the DKIM error. Because it is logged at `LOG_ERR`, the error also lands in `data/error.log`.

    Nothing changed between signing and sending. The connection failed, and the DKIM verify pass then hashed a truncated body.

    ## Cause

    In `mailsrvr.cpp`:

    1. During pass 2, `sockprintf()` feeds each line to the DKIM verify capture (`dkim_capture_line()`) *before* trying to transmit it.
    2. In `sockmimetext()`'s body loop, the first failed `sockprintf()` exits the loop with `break`. The rest of the body and any attachments are skipped.
    3. Execution still reaches `sockprintf(..., ".")` at the end of `sockmimetext()`. The verify capture sees the end-of-data terminator, finalizes a hash over the partial body, and sets `mismatch`. The caller then logs "DKIM body hash changed between sign and send".

    The "message delivery aborted" part is also misleading: the dropped connection had already ended the delivery.

    ## Suggested fix

    In the `sockmimetext()` body loop, replace `break` with `return lines;`. This follows the existing early `return 0` when the header-terminator send fails. After a failed send the session is unusable, so the terminator and attachment encoding can't reach the peer anyway. With this change:

    - The DKIM verify capture never sees `.`, so there's no false mismatch.
    - Attachments are no longer MIME-encoded into a dead socket.
    - `MSG_KILLFILE` attachments are left in place, which is correct because they weren't delivered.

    The POP3 RETR path, which shares `sockmimetext()`, gets the same early exit.

    ## Release notes

    DKIM signing (#215) was added after v3.21e, so this fix doesn't need an entry in `docs/v322_new.md`.

    _Authored by Claude (Claude Code), on behalf of @rswindell_
    --- SBBSecho 3.38-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)