Part 4c of relay outbound: the relay now delivers what the downstream hands it through Outbound/set (#258), using the DANE, MTA-STS and DNSSEC code shared with the server (#259). The downstream doesn't hand anything over yet; that's 4d.
Delivery (crates/relay/src/deliver/)
One domain at a time:
MX hosts best first, at most five, two addresses each;
no MX means the domain itself;
a null MX (RFC 7505) refuses with 556 5.1.10;
a domain that doesn't exist (NXDOMAIN) is refused for good.
TLS, with the same rules as the server:
DANE applies when the MX answer, the host's addresses and its TLSA records are all DNSSEC-signed, and takes precedence over MTA-STS;
a signed zone with unusable TLSA records still requires encryption, and bogus TLSA records skip the host;
MTA-STS enforce skips unlisted hosts and needs a WebPKI certificate valid for the host; testing only reports;
otherwise the new outboundTls setting applies: opportunistic (the default) or required, with no certificate check;
opportunistic TLS that fails to set up is tried once more in the clear.
The SMTP client: EHLO, STARTTLS (nothing may be sent ahead of the handshake), MAIL FROM with only the parameters the server offers (SIZE, BODY, SMTPUTF8, RET/ENVID; NOTIFY/ORCPT on RCPT), and dot-stuffed DATA streamed from the spool with the bytes otherwise untouched. Replies are length-checked.
MTA-STS policies are fetched over HTTPS: WebPKI against the system roots, no redirects, 64 KiB max, cached for max_age. As RFC 8461 asks, a policy that can't be fetched leaves the cached one in force.
Status
Each recipient gets the remote server's own reply: refused is no, deferred stays queued, and after DATA it's yes.
When no host could take the attempt, recipients stay queued with the reason as a 4.4.x reply.
Retries follow outboundRetry. At expiresAt, what is still queued becomes no with the last reply seen.
destroy is refused while an attempt is under way. Finished messages the downstream never clears are deleted after 7 days.
Events: outbound.attempt, outbound.tls (TLS-RPT policy type, policy strings, and result type), and outbound.expired.
Footprint: the release binary goes from 5.4 MB to 8.1 MB (glibc), under the 15 MB budget. Idle memory not re-measured.
Tests
New delivery tests run against a scripted MX on localhost with fixed DNS answers:
per-recipient yes/no/queued with the real replies (RL-T11, relay side);
STARTTLS used when offered;
dot-stuffing;
null MX and nonexistent domains;
DANE with the right key delivers and with a wrong one doesn't, and MTA-STS enforce refuses an untrusted certificate and an unlisted MX (RL-T12);
expiry and cleanup.
Unit tests cover reply parsing, EHLO keywords, parameter filtering and the dot-stuffer.
All 25 relay tests pass, and system::relay::relay_link_tests still passes. fmt, clippy and the fork checks are clean.
Not tested: resolution through real DNS, including the startup check that the resolver validates DNSSEC; and real MTA-STS HTTPS fetches.
Part 4c of relay outbound: the relay now delivers what the downstream hands it through `Outbound/set` (#258), using the DANE, MTA-STS and DNSSEC code shared with the server (#259). The downstream doesn't hand anything over yet; that's 4d.
**Delivery** (`crates/relay/src/deliver/`)
- One domain at a time:
- MX hosts best first, at most five, two addresses each;
- no MX means the domain itself;
- a null MX (RFC 7505) refuses with `556 5.1.10`;
- a domain that doesn't exist (NXDOMAIN) is refused for good.
- TLS, with the same rules as the server:
- DANE applies when the MX answer, the host's addresses and its TLSA records are all DNSSEC-signed, and takes precedence over MTA-STS;
- a signed zone with unusable TLSA records still requires encryption, and bogus TLSA records skip the host;
- MTA-STS `enforce` skips unlisted hosts and needs a WebPKI certificate valid for the host; `testing` only reports;
- otherwise the new `outboundTls` setting applies: `opportunistic` (the default) or `required`, with no certificate check;
- opportunistic TLS that fails to set up is tried once more in the clear.
- The SMTP client: EHLO, STARTTLS (nothing may be sent ahead of the handshake), MAIL FROM with only the parameters the server offers (SIZE, BODY, SMTPUTF8, RET/ENVID; NOTIFY/ORCPT on RCPT), and dot-stuffed DATA streamed from the spool with the bytes otherwise untouched. Replies are length-checked.
- MTA-STS policies are fetched over HTTPS: WebPKI against the system roots, no redirects, 64 KiB max, cached for `max_age`. As RFC 8461 asks, a policy that can't be fetched leaves the cached one in force.
**Status**
- Each recipient gets the remote server's own reply: refused is `no`, deferred stays `queued`, and after DATA it's `yes`.
- When no host could take the attempt, recipients stay queued with the reason as a `4.4.x` reply.
- Retries follow `outboundRetry`. At `expiresAt`, what is still queued becomes `no` with the last reply seen.
- `destroy` is refused while an attempt is under way. Finished messages the downstream never clears are deleted after 7 days.
- Events: `outbound.attempt`, `outbound.tls` (TLS-RPT policy type, policy strings, and result type), and `outbound.expired`.
**Footprint:** the release binary goes from 5.4 MB to 8.1 MB (glibc), under the 15 MB budget. Idle memory not re-measured.
**Tests**
- New delivery tests run against a scripted MX on localhost with fixed DNS answers:
- per-recipient yes/no/queued with the real replies (RL-T11, relay side);
- STARTTLS used when offered;
- dot-stuffing;
- null MX and nonexistent domains;
- DANE with the right key delivers and with a wrong one doesn't, and MTA-STS `enforce` refuses an untrusted certificate and an unlisted MX (RL-T12);
- expiry and cleanup.
- Unit tests cover reply parsing, EHLO keywords, parameter filtering and the dot-stuffer.
- All 25 relay tests pass, and `system::relay::relay_link_tests` still passes. fmt, clippy and the fork checks are clean.
- Not tested: resolution through real DNS, including the startup check that the resolver validates DNSSEC; and real MTA-STS HTTPS fetches.
The relay now sends what the downstream hands it: by MX, one domain at a
time, with DANE where the MX and the host's addresses are signed and its
TLSA records are, MTA-STS where the domain enforces a policy, and
otherwise STARTTLS per the new outboundTls setting. Each recipient's
deliveryStatus is the remote server's own reply; retries follow
outboundRetry, and what is still queued at expiresAt is refused with the
last reply. Sessions report outbound.attempt and outbound.tls events, the
latter for TLS-RPT. DNS, DANE and MTA-STS come from crates/outbound-tls.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Part 4c of relay outbound: the relay now delivers what the downstream hands it through
Outbound/set(#258), using the DANE, MTA-STS and DNSSEC code shared with the server (#259). The downstream doesn't hand anything over yet; that's 4d.Delivery (
crates/relay/src/deliver/)556 5.1.10;enforceskips unlisted hosts and needs a WebPKI certificate valid for the host;testingonly reports;outboundTlssetting applies:opportunistic(the default) orrequired, with no certificate check;max_age. As RFC 8461 asks, a policy that can't be fetched leaves the cached one in force.Status
no, deferred staysqueued, and after DATA it'syes.4.4.xreply.outboundRetry. AtexpiresAt, what is still queued becomesnowith the last reply seen.destroyis refused while an attempt is under way. Finished messages the downstream never clears are deleted after 7 days.outbound.attempt,outbound.tls(TLS-RPT policy type, policy strings, and result type), andoutbound.expired.Footprint: the release binary goes from 5.4 MB to 8.1 MB (glibc), under the 15 MB budget. Idle memory not re-measured.
Tests
enforcerefuses an untrusted certificate and an unlisted MX (RL-T12);system::relay::relay_link_testsstill passes. fmt, clippy and the fork checks are clean.