61 Commits

Author SHA1 Message Date
Alberto Bertogli
3776186288 protobuf: Regenerate with protoc-gen-go v1.36.10
Regenerate the protobuf auto-generated files using an updated version of
protoc-gen-go.
2025-10-24 12:26:16 +01:00
Alberto Bertogli
08273ea901 queue: Sync the files written on Put
When we put something in the queue and respond "250 ok" to the client,
that is taken as accepting the email.

As part of putting something in the queue, we write it to disk, but
today we don't do an fsync on that file.

That leaves a gap where a badly timed crash on some systems could lead
to the file being empty, causing us to lose an email that we accepted.

To elliminate (or drastically reduce on some filesystems) the chances of
that situation, we call fsync on the file that gets written when we put
something in the queue.

Thanks to nolanl@github for reporting this in
https://github.com/albertito/chasquid/issues/78.
2025-10-18 12:10:24 +01:00
Alberto Bertogli
24c2c4f5fd Make the max queue size and give up time configurable
Today, the maximum number of items in the queue, as well as how long we
keep attempting to send each item, is hard-coded and not changed by end
users.

While they are totally adequate for chasquid's main use cases, it can
still be useful for some users to change them.

So this patch adds two new configuration options for those settings.
They're marked experimental for now, so we can adjust them if needed
after they get more exposure.

Thanks to Lewis Ross-Jones <lewis_r_j@hotmail.com> for suggesting this
improvement, and help with testing it.
2025-06-07 11:00:00 +01:00
Alberto Bertogli
9999a69086 aliases: Implement "via" aliases
This patch implements "via" aliases, which let us explicitly select a
server to use for delivery.

This feature is useful in different scenarios, such as a secondary MX
server that forwards all incoming email to a primary.

For now, it is experimental and the syntax and semantics are subject to
change.
2025-04-12 23:23:21 +01:00
Alberto Bertogli
45580dae46 Update to math/rand/v2
This commit updates the uses of math/rand to math/rand/v2, which was
released in Go 1.22 (2024-02).

The new package is generally safer, see https://go.dev/blog/randv2 for
the details.

There are no user-visible changes, it is only adjusting the name of
functions, simplify some code thanks to v2 having a better API, etc.
2025-02-16 20:18:16 +00:00
Alberto Bertogli
19ce435e92 Regenerate auto-generated files
This patch regenerates the auto-generated files. There are no
significant changes.

- Protobuf files updated the comment formatting to match recent changes
  in Go libraries.
- IANA assignment for a AEGIS (currently an IETF draft) has been
  updated.
- The link to the human-readable IANA assignment tables from the
  generator was manually updated.
2024-12-01 22:21:19 +00:00
Alberto Bertogli
76a72367ae dkim: Implement internal dkim signing and verification
This patch implements internal DKIM signing and verification.
2024-03-12 20:43:21 +00:00
Alberto Bertogli
8ded1f6f5e Auto-format protobuf files
This patch runs clang-format on the protobuf files, and also adds a
Makefile target for auto-formatting code (Go and protobuf) for
convenience.
2023-10-03 23:34:23 +01:00
Alberto Bertogli
e85ad54a73 Regenerate auto-generated files
This patch regenerates the auto-generated files.

There are no significant changes, the protobuf just get an updated
comment due to protoc version change, but it is just informational.

Two new TLS ciphers are added, matching the new IANA assignments.
2023-03-03 11:24:40 +00:00
Alberto Bertogli
4a00a83c23 Add tracing annotations
This patch changes several internal packages to receive and pass tracing
annotations, making use of the new tracing library, so we can have
better debugging information.
2022-11-13 11:09:19 +00:00
Alberto Bertogli
6dfff9a790 modules: Update Go modules and regenerate protobufs
This patch does a general pass updating Go modules to recent versions, and
regenerates the protobufs accordingly.

The main purpose is to make sure people building from source are using
relatively recent versions of our dependencies.
2022-08-27 23:39:40 +01:00
Alberto Bertogli
f303e43082 aliases: Implement catch-all
This patch implements support for catch-all aliases, where users can add
a `*: destination` alias. Mails sent to unknown users (or other aliases)
will not be rejected, but sent to the indicated destination instead.

Please see https://github.com/albertito/chasquid/issues/23 and
https://github.com/albertito/chasquid/pull/24 for more discussion and
background.

Thanks to Alex Ellwein (aellwein@github) for the alternative patch and
help with testing; and to ThinkChaos (ThinkChaos@github) for help with
testing.
2022-03-11 20:51:06 +00:00
Alberto Bertogli
3255ff6801 modules: Update Go modules and regenerate protobufs
This patch does a general pass updating Go modules to recent versions, and
regenerates the protobufs accordingly.

The main purpose is to make sure people building from source are using
relatively recent versions of our dependencies.
2022-03-11 18:28:11 +00:00
Alberto Bertogli
fb680336f0 modules: Update Go modules and regenerate protobufs
This patch does a general pass updating Go modules to recent versions,
and regenerates the protobufs accordingly.

The main purpose is to make sure people building from source are using
relatively recent versions of our dependencies.

We also regenerate protobufs since the newer versions of the liberaries
have a much cleaner dependency tree, which speeds up fetches.
2021-05-31 11:43:06 +01:00
Alberto Bertogli
1fcc4ffe0f queue: Remove dependency on external protobuf package
The queue protobuf definition currently uses the well-known timestamp
protobuf package.

This adds a build-time dependency on it, which is fairly harmless when
building from source (since the golang protobuf compiler includes it
already), but adds overhead for packaging on distributions.

Since this is the only external proto dependency we have, and the
protobuf message itself is trivial, this patch removes it an instead
embeds a compatible definition.

That way we remove the dependency and simplify packaging, with almost
negligible code overhead.

The change is fully backwards compatible and has no functional changes.
2020-09-12 10:56:17 +01:00
Alberto Bertogli
7fe42a368a monitoring: Add OpenMetrics exporter
This patch makes chasquid's monitoring server expose an OpenMetrics
metrics endpoint.

It adds a new package "expvarom" which implements an HTTP handler that
exports expvar variables in the OpenMetrics text format.

Then, the handler is registered by the monitoring server at /metrics
(where most things expect it to be).

The existing exported variables are also extended with descriptions,
which is optional, but improves the readability of the metrics.
2020-08-21 12:07:33 +01:00
Alberto Bertogli
50986a7b7e Update protobuf library to v2
There is a new protobuf library (and corresponding code generator) for
Go: google.golang.org/protobuf.

It is fairly compatible with the previous v1 API
(github.com/golang/protobuf), but there are some changes.

This patch adjusts the code and generated files to the new API.

The on-wire/on-disk format remains unchanged so this should be
transparent to the users.
2020-05-16 10:12:51 +01:00
Alberto Bertogli
7b0703eaa0 queue: Check that we can create the queue directory
When creating a new Queue instance, we os.MkdirAll the queue directory.

Currently we don't check if it fails, which will cause us to find out
about problems when the queue is first used, where it is more annoying
to troubleshoot.

This patch adjusts the code so that we check and propagate the error.
That way, problems with the queue directory will be more evident and
easier to handle.
2020-04-14 12:01:01 +01:00
Alberto Bertogli
25ebc4f2e2 Avoid unnecessary calls to fmt.Sprintf
The linter caught some unnecessary calls to fmt.Sprintf. This patch
removes them.

There are no functional changes.
2020-04-14 12:01:01 +01:00
Alberto Bertogli
a6a964ac3e test: Move testing couriers to testlib
The testing couriers are currently only used in the queue tests, but we
also want to use them in smtpsrv tests so we can make them more robusts
by checking the emails got delivered.

This patch moves the testing couriers to testlib, and makes both queue
and smtpsrv use them.
2019-12-01 19:09:12 +00:00
Alberto Bertogli
0718749314 Update auto-generated code
This patch updates the auto-generated code to match the latest tooling
versions.

In particular, the protobufs are regenerated, and the new version no
longer supports unkeyed literals, so some minor changes are needed.

Other than that, the cipher list is extended with the latest ciphers.
2019-10-24 21:37:09 +01:00
Alberto Bertogli
2943f994e7 Use context.WithTimeout instead of context.WithDeadline
There are a few context.WithDeadline calls that can be simplified by
using context.WithTimeout.

At the time they were added, WithTimeout was too new so we didn't want
to depend on it. But now that the minimum Go version has been raised to
1.9, we can simplify the calls.

This patch does that simplification, which is purely mechanical, and
does not change the logic itself.
2019-07-13 13:44:25 +01:00
Alberto Bertogli
a5edd9053f queue: Make DSN tidier, especially in handling multi-line errors
This patch contains some changes to generate tidier DSNs, which should
make them slightly more readable.

In particular, it also makes it able to handle multi-line errors much
better than before.
2019-05-04 21:28:07 +01:00
Alberto Bertogli
ec95131bb4 Miscellaneous style fixes
This patch has some miscellaneous style fixes to issues found by the
staticcheck tool.

There are no functional changes.
2019-02-10 12:46:15 +00:00
Alberto Bertogli
1ecc957aba queue: Internationalized Delivery Status Notifications (DSN)
Our non-delivery status notifications are quite simple today, but that
makes it much more difficult to support internationalization and
cross-language reporting.

There is a standard for internationalized DSNs, RFC 6533 (which builds
on top of the structured DSNs from RFC 3464).

This patch changes our DSN messages to be based on those standards, so
it is easier for MUAs to display reports according to the users'
languages preferences.

Note we still use message/rfc822 + 8bit to transmit the message, instead
of message/global, for compatibility reasons. This seems to be more
universally compatible, but the decision might be revisited in the
future. See RFC 5335 (section 4.6 in particular).
2019-01-18 23:27:10 +00:00
Alberto Bertogli
4db9ffec09 Code style improvements
This patch contains some minor code style improvements, to leave the
linter happier and generally follow best practices in some areas where
things snuck through.
2018-12-01 11:58:50 +00:00
Alberto Bertogli
2064e9e65d queue: Increase retry time from 12h to 20h
Some transient issues might take more than 12h to resolve, specially if
they happen overnight.

20h gives a bit more margin for retries, while still being short enough
so that users are notified early.
2018-05-20 12:05:32 +01:00
Alberto Bertogli
f3b01cb493 docs: Add missing docstrings, adjust wording to match standard style
This patch adds a missing docstrings for exported identifiers, and
adjust some of the existing ones to match the standard style.

In some cases, the identifiers were un-exported after noticing they had
no external users.

Besides improving documentation, it also reduces the linter noise
significantly.
2018-03-04 16:00:06 +00:00
Alberto Bertogli
0611b7a7fc test: Add small miscellaneous tests
This patch extends various packages and integration tests, increasing
test coverage. They're small enough that it's not worth splitting them
up, as it would add a lot of noise to the history.
2018-03-02 19:37:37 +00:00
Alberto Bertogli
033a5d657b Use the external log, spf and systemd packages
The log, spf and systemd packages have been externalized; use them
instead of the internal version to avoid having two versions of the same
thing.
2017-09-17 22:17:14 +01:00
Alberto Bertogli
9864f40f3b test: Tidy up creation and removal of test directories
We have many places in our tests where we create temporary directories,
which we later remove (most of the time). We have at least 3 helpers to
do this, and various places where it's done ad-hoc (and the cleanup is
not always present).

To try to reduce the clutter, and make the tests more uniform and
readable, this patch introduces two helpers in a new "testutil" package:
one for creating and one for removing temporary directories.

These new functions are safer, better tested, and make the tests more
consistent.  All the tests are updated to use them.
2017-07-14 02:02:43 +01:00
Alberto Bertogli
fea808f8e3 queue: Get the DSN domain from the message
Picking the domain used in the DSN message "From" header is more
complicated than it needs to be, causing confusing code paths and having
different uses for the hostname, which should be purely aesthetic.

This patch makes the queue pick the DSN "From" domain from the message
itself, by looking for a local domain in either the sender or the
original recipients. We should find at least one, otherwise it'd be
relaying.

This allows the code to be simplified, and we can narrow the scope of
the hostname option even further.
2016-11-03 00:51:59 +00:00
Alberto Bertogli
8cbc4f9ca6 queue: Use a PRNG to generate IDs
The queue IDs are internal to chasquid, and we don't need them to be
cryptographically secure, so this patch changes the ID generation to use
the PRNG.

This also helps avoid entropy issues.
2016-11-01 23:56:04 +00:00
Alberto Bertogli
1bc111f783 Improve the readability of some log messages
This patch contains a few changes to logging messages, to improve log
readability.

While at it, an obsolete TODO in the SMTP courier is removed.
2016-11-01 23:56:04 +00:00
Alberto Bertogli
60a7932bd3 log: Replace glog with a new logging module
glog works fine and has great features, but it does not play along well
with systemd or standard log rotators (as it does the rotation itself).

So this patch replaces glog with a new logging module "log", which by
default logs to stderr, in a systemd-friendly manner.

Logging to files or syslog is still supported.
2016-11-01 23:56:04 +00:00
Alberto Bertogli
c00648b4ae queue: Calculate next delay based on creation time
Calculating the next delay based on the previous delay causes daemon
restarts to start from scratch, as we don't persist it.

This can cause a few server restarts to generate many unnecessary sends.

This patch changes the next delay calculation to use the creation time
instead, and also adds a <=1m random perturbation to avoid all queued
emails to be retried at the exact same time after a restart.
2016-10-21 22:20:49 +01:00
Alberto Bertogli
8e9e4eddc5 queue: Rewrite the aliases test to remove races
TestAliases is unfortunately racy, and cause occasional failures.

This patch rewrites it to be more end-to-end, similar to TestBasic,
which should remove the races while keeping the main objectives of the
test.
2016-10-21 22:20:49 +01:00
Alberto Bertogli
d660f88f67 queue: Send DSN for messages that time out in the queue
The queue currently only considers failed recipients when deciding
whether to send a DSN or not. This is a bug, as recipients that time out
are not taken into account.

This patch fixes that issue by including both failed and pending
recipients in the DSN.

It also adds more comprehensive tests for this case, both in the queue
and in the dsn generation code.
2016-10-21 22:20:49 +01:00
Alberto Bertogli
febe96697a maillog: Introduce a special-purpose mail logging package
The default INFO logs are more oriented towards debugging and can be
a bit too verbose when looking for high-level information.

This patch introduces a new "maillog" package, used to log messages of
particular relevance to mail transmission at a higher level.
2016-10-21 22:20:47 +01:00
Alberto Bertogli
fe146f00e5 queue: Always use the main domain for sending DSNs
Today, we pick the domain used to send the DSN from based on what we
presented to the client at EHLO time, which itself may be based on the
TLS negotiation (which is not necessarily trusted).

This is complex, not necessarily correct, and involves passing the
domain around through the queue and persisting it in the items.

So this patch simplifies that handling by always using the main domain
as specified by the configuration.
2016-10-21 22:18:53 +01:00
Alberto Bertogli
1d7a207e00 Minor code aesthetic improvements, based on vet+fmt+lint
This patch is the result of running go vet, go fmt -s and the linter,
and fixing some of the things they noted/suggested.

There shouldn't be any significant logic changes, it's mostly
readability improvements.
2016-10-21 22:13:39 +01:00
Alberto Bertogli
09d3c73f6c queue: Simplify sending loop
This patch simplifies the sending loop code:

 - Move the recipient sending function from a closure to a method.
 - Simplify the status update logic: we now update and write
   unconditionally (as we should have been doing).
 - Create a function for counting recipients in a given status.

It also adds a test for the removal of completed items from the queue,
which was not covered before and came up during development.
2016-10-21 22:13:39 +01:00
Alberto Bertogli
c4e8b22fd0 Introduce expvar counters
This patch introduces expvar counters to chasquid and the queue
packages.

For now there's only a handful of counters, but they will be expanded in
future patches.
2016-10-10 00:51:05 +01:00
Alberto Bertogli
641406cede queue: Fix race in tests
The test courier has a racy map access, and this started to manifest
after some recent changes. This patch fixes the race by implementing the
corresponding locks.
2016-10-10 00:51:05 +01:00
Alberto Bertogli
3e6dd12d06 Improve debugging and tracing information
This patch reviews various debug and informational messages, making more
uniform use of tracing, and extends the monitoring http server with
useful information like an index and a queue dump.
2016-10-10 00:51:05 +01:00
Alberto Bertogli
55b03c8cf0 queue: Use a local envelope-from when forwarding
If there's an alias to forward email to a non-local domain, using the original
From is problematic, as we may not be an authorized sender for it.

Some MTAs (like Exim) will do it anyway, others (like gmail) will construct a
special address based on the original address.

This patch implements the latter approach, which is safer and allows the
receiver to properly enforce SPF.

We construct a (hopefully) reasonable From based on the local user, and
embedding the original From (but transformed for IDNA, as the receiver may not
support SMTPUTF8).
2016-10-10 00:51:05 +01:00
Alberto Bertogli
04dd8b9534 Remove unreachable code, and don't leak contexts
This patch performs some minor cleanups for things detected by "go vet":

 - Remove one line of unreachable code.
 - Don't leak contexts until their deadline expires, cancel them.
2016-10-10 00:51:04 +01:00
Alberto Bertogli
1d3675a133 queue: Send delivery status notifications on failures
When we permanently failed to deliver to one or more recipients, send delivery
status notifications back to the sender.

To do this, we need to extend a couple of internal structures, to keep track
of the original destinations (so we can include them in the message, for
reference), and the hostname we're identifying ourselves as (this is arguable
but we're going with it for now, may change later).
2016-10-10 00:51:04 +01:00
Alberto Bertogli
0bf5d9b242 Distinguish between permanent and transient errors
This patch makes the queue and couriers distinguish between permanent and
transient errors when delivering mail to individual recipients.

Pipe delivery errors are always permanent.

Procmail delivery errors are almost always permanent, except if the command
exited with code 75, which is an indication of transient.

SMTP delivery errors are almost always transient, except if the DNS resolution
for the domain failed.
2016-10-10 00:51:04 +01:00
Alberto Bertogli
667358d72e courier: Tidy up the Procmail courier
This patch tidies up the Procmail courier:
 - Move the configuration options to the courier instance, instead of using
   global variables.
 - Implement more useful string replacement options.
 - Use exec.CommandContext for running the command with a timeout.

As a consequence of the first item, the queue now takes the couriers via its
constructor.
2016-10-10 00:51:04 +01:00