hMailServer 6.2.23 Alpha 2
Pre-releasehMailServer 6.2.23 Alpha 2
Pre-release. Not for production. 30 commits since v6.2.23-alpha1; 1,873 files touched, of which 1,825 are the licence-header pass from 22 August and 72 are the formatting commit, leaving roughly 80 files of actual change (+1,600 / −200 lines). The database schema is unchanged at 6025, the COM interfaces are unchanged, and no checked-in binary changed. Everything in Alpha 1's notes - the schema move from 6.2.21, the one-way upgrade, the known limitations - still applies to an installation coming from 6.2.21 or earlier.
What this release is
A small, deliberate release: one reported defect fixed, seven fixes taken from the original project - which has become active again after years - and the hardening that was written down as missing. It exists because a Thunderbird user could not save a single Sent copy, and because the upstream comparison turned up two ways a sender could steer the anti-spam tests. Alpha rather than beta because the STARTTLS change below alters what a non-conforming client sees, and that deserves a cycle in the open before it is called stable.
Fixed: Thunderbird's Sent copies were refused (#53)
Once a client enables UTF8=ACCEPT, RFC 6855 lets it send an APPEND's message as UTF8 (~{n} with the closing ) following the octets. Thunderbird 128 and later does this for every Sent copy. The server's command parser counts parentheses across the whole line before it tokenizes anything, saw one ( too many, and refused the line with BAD APPEND Command requires at least 2 parameter before reading a byte - so mail went out over SMTP and no Sent copy was ever stored, on every message, with only a per-machine client setting as a workaround.
The wrapper is now recognised and stripped ahead of the parser, and the ) is taken from the text that follows the octets. It works for the non-synchronizing ~{n+} form and for later messages of a MULTIAPPEND. A wrapper that is never closed is refused and nothing is stored - the ) is protocol, not message, and a server that guessed where the message ended would be worse than one that refused. Five fixtures, including the exact line from the report; six of their assertions fail against the previous binary.
Two anti-spam bypasses, ported from upstream
The original project has resumed development (seventy-one commits since this fork last compared itself against it). Every one was read against the function that does the same job here; the full record, verdict by verdict, is in .github/upstream-sync. Two of the ports matter for security:
The sender could choose which address the IP-based spam tests ran against
For mail arriving through an incoming relay or fetched from an external account, the server parses the topmost Received header to find the originating address. It took the first bracketed value after from - which is the HELO literal the client chose to present - rather than the address the receiving server actually observed, which comes last. And a header whose host name was not a valid domain name (my_pc, say) was skipped entirely, so parsing fell through into headers the sender wrote, or found no address at all and quietly skipped DNSBL, SPF and the HELO-host test.
The parser now takes the last observed address before by, ignores values marked as HELO-supplied, and keeps a header without a usable host name. Two assert(0) on sender-reachable input are gone with it. Upstream's own test vectors were taken along.
This changes which address is tested for relayed and fetched mail. If you run behind an incoming relay and have tuned scores around the old behaviour, expect verdicts to move - towards the address that actually connected.
RSET before EHLO, or STARTTLS without a fresh EHLO, opened a transaction with no greeting
RSET is valid before EHLO and used to move the session into the transaction state on its own; and the TLS handshake discarded the greeting as RFC 3207 requires but left the state machine where it was. Either way MAIL FROM was accepted with the HELO host empty, which skipped the HELO-host spam test and the OnHELO/OnEHLO script events for that session.
The greeting state is now only left by an actual EHLO or HELO. A client that sends MAIL FROM straight after the TLS handshake without saying EHLO again now receives 503 Bad sequence of commands - which is what RFC 3207 section 4.2 has required of it all along, and what every mainstream client does. If you have a home-grown submission script that skips the second EHLO, this is the release that tells you.
Hardening
- Control Flow Guard is on (#45).
hMailServer.exeis compiled and linked with/guard:cfin both configurations, so an indirect call through a corrupted function pointer or vtable - the natural target of a memory-safety defect in the SMTP, IMAP, POP3 or MIME parsers - terminates the process instead of transferring control.dumpbin /headersshowsGuardin the DLL characteristics. The full regression gate on the CFG build ran in 31 minutes against about 55 for the previous release's gates on the same machine - a faster run, not a slower one, so the cost is below the suite's run-to-run variance. - The release tag is signed (#46).
v6.2.23-alpha2is the first annotated, SSH-signed tag; every earlier one was a lightweight ref with nothing to verify. The signing workflow now refuses to sign a single asset for a tag that is lightweight or does not verify against the allow list in the repository. Verification is in the last section. - An unattended install that fails its database step now fails. The installer discarded the result of the post-install tasks, so a failed database create or upgrade showed a suppressible message box and setup still exited 0. It raises now; scripted deployments can tell.
- COM
IMAPFolder.Deletereports a refused deletion instead of answering S_OK while the row stayed behind.
Other fixes
- A reverse-DNS answer whose owner name carries the trailing dot (
1.0.0.127.in-addr.arpa.) no longer fails the resolver's equality filter and empties the PTR result, which had been failing open in the HELO and reverse-DNS spam tests on affected hosts. - The script host dereferenced a null engine when engine creation had failed (a missing or policy-blocked VBScript engine) and a script reload asked whether a procedure existed.
- The IMAP notification client dereferenced the current folder on the notifying session's thread while the owning session could be closing it.
- Two CodeQL alerts on the mbox importer's buffer handling, and the warning-level findings the managed quality scan raised in the test suite and the header script.
Dependencies
Ten NuGet updates (Azure.Core 1.62, MSAL 4.88, NLog 6.2, OpenTelemetry 1.18, NUnit3TestAdapter 6.3, xunit.runner.visualstudio 4.0) and three CodeQL action updates, both from Dependabot. GitHub's automatic dependency submission - red on every push since it was enabled, because it restores Windows-targeting projects on a Linux runner - now completes, so the dependency graph carries the transitive packages. Getting it there also meant bumping the headers of ten Visual Studio 2005/2008-era solutions (legacy tools and test harnesses) that the .NET 10 solution parser refuses outright. The native dependencies are unchanged: OpenSSL 4.0.1, Boost 1.91, PostgreSQL 18.3, MariaDB Connector/C 3.4.9.
Repository and process
hmailserver/docs/RegressionEnvironment.md- how to set a machine up so the 1,800-test suite runs, which CONTRIBUTING.md had admitted was unpublished (#48).- A Developer Certificate of Origin check on pull requests (#47), and the C# tree brought to
dotnet formatclean with the verify job restored (#44). - Two CI-only changes landed after the regression gate had run and are the only things in this range that did: the editorconfig-checker version pinned beside its action (the checker's 4.0.0 release renamed its download and every run since 3 September had failed before checking a file), and the ten solution headers above. Neither is compiled into anything shipped, which is why the gate stands.
- Issue #49 (SCRAM-SHA-1) is closed as not planned, with the reason: the stored PBKDF2-SHA256 output is the SCRAM-SHA-256 salted password, and SHA-1 would need a second stored verifier per account - a change to what is stored, not to the transport.
- Ten further upstream changes are recorded as roadmap rows with their size and what they wait for, the largest being upstream's way of building the C++ server on GitHub-hosted runners, which this fork lacks.
Known limitations
Everything listed under Alpha 1 still stands. New or newly written down:
- The weekly C++ CodeQL analysis has not run since late August. It targets a self-hosted runner that is no longer registered, and is cancelled after 24 hours in the queue each Monday. The C# analysis runs on every push. Restoring the runner, or porting the hosted build, is the roadmap's top build item.
- SCRAM-SHA-1 is not offered, for the reason above; clients without SCRAM-SHA-256 use PLAIN or LOGIN over TLS.
- Received-header parsing is stricter about host names but not about brackets. An address given without brackets (
(HELO host) (203.0.113.99)) is still not recognised, as before.
Downloads and verification
| Asset | What it is |
|---|---|
hMailServer-6.2.23-alpha2-x64.exe |
The installer (Inno Setup). Not Authenticode-signed. |
*.spdx.json, *.cdx.json |
SBOMs, SPDX and CycloneDX, covering the .NET and native dependencies. |
*.cosign.bundle |
A Sigstore bundle per asset, keyless, bound to this repository's workflow identity. |
Verify the tag, from any clone:
git -c gpg.ssh.allowedSignersFile=.github/allowed_signers verify-tag v6.2.23-alpha2
Verify an asset with cosign:
cosign verify-blob --bundle hMailServer-6.2.23-alpha2-x64.exe.cosign.bundle \
--certificate-identity-regexp '^https://github\.com/Progressiverobot/hmailserver/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
hMailServer-6.2.23-alpha2-x64.exe
The server binary is reproducible: two clean Release builds of this tag produce a byte-identical hMailServer.exe, SHA-256 cff066d39af42dac3532cbc091b6294a69a73b7d323666c16bfa0fc045c8fe23, with the v145 toolset (MSVC 14.51), Windows SDK 10.0.26100 and the library layout in README.md. The regression suite on that binary: 1838/1838, nothing skipped, 31 minutes. Control Panel unit suite: 645/645.