Distributed James Server — Delivery Submission Notifications

DSN introduced in RFC-3461 allows a SMTP sender to demand status messages, defined in RFC-3464 to be sent back to the Return-Path upon delivery progress.

DSN support is not enabled by default, as it needs specific configuration of the mailetcontainer.xml to be specification compliant.

To enable it you need to:

  • Add DSN SMTP hooks as part of the SMTP server stack

  • Configure mailetcontainer.xml to generate DSN bounces when needed

Enabling DSN in SMTP server stack

For this simply add the DSN hooks in the handler chain in smtpserver.xml :

<smtpserver enabled="true">
    <...> <!-- The rest of your SMTP configuration, unchanged -->
    <handlerchain>
        <handler class="org.apache.james.smtpserver.dsn.DSNEhloHook"/>
        <handler class="org.apache.james.smtpserver.dsn.DSNMailParameterHook"/>
        <handler class="org.apache.james.smtpserver.dsn.DSNRcptParameterHook"/>
        <handler class="org.apache.james.smtpserver.dsn.DSNMessageHook"/>
        <...> <!-- other handlers, like: -->
        <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/> <!-- for instance -->
    </handlerchain>
</smtpserver>

Enabling DSN generation as part of mail processing

For the below conditions to be matched we assume you follow RemoteDelivery error handling for MXs, which is a requirement for detailed RemoteDelivery error and delay handling on top of the {server-name}.

Here is a sample mailetcontainer.xml achieving the following DSN generation:

  • Generate a generic delivered notification if LocalDelivery succeeded, if requested

  • Generate a generic failed notification in case of local errors, if requested

  • Generate a specific failed notification in case of a non existing local user, if requested

  • Generate a specific failed notification in case of an address rewriting loop, if requested

  • Generate a failed notification in case of remote permanent errors, if requested. We blame the remote server…​

  • Generate a delayed notification in case of temporary remote errors we are about to retry, if requested. We blame the remote server…​

  • Generate a failed notification in case of temporary remote errors we are not going to retry (failed too many time), if requested. We blame the remote server…​

<mailetcontainer enableJmx="true">
    <!-- Common processing settings are unchanged -->

    <processors>
        <processor state="root" enableJmx="true">\
            <!-- Content of root processor is unchanged -->
        </processor>

        <processor state="transport" enableJmx="true">
            <!-- transport processor unchanged -->
        </processor>

        <processor state="error" enableJmx="true">
            <mailet match="DSNFailureRequested" class="DSNBounce">
                <prefix>[FAILED]</prefix>
                <passThrough>true</passThrough>
                <messageString>Hi. This is the James mail server at [machine].
I'm afraid I wasn't able to deliver your message to the following addresses.
This is a permanent error; I've given up. Sorry it didn't work out.  Below
I include the list of recipients, and the reason why I was unable to deliver
your message.</messageString>
                <action>failed</action>
                <defaultStatus>5.0.0</defaultStatus>
            </mailet>
            <mailet match="All" class="ToRepository">
                <repositoryPath>cassandra://var/mail/error/</repositoryPath>
            </mailet>
        </processor>

        <processor state="local-delivery" enableJmx="true">
            <!-- Your local-delivery pipeline -->
            <mailet match="All" class="LocalDelivery">
                <!-- Do not abort the pipeline yet -->
                <consume>false</consume>
            </mailet>
            <!-- Tell the world we succeeded -->
            <mailet match="DSNSuccessRequested" class="DSNBounce">
                <prefix>[SUCCESS]</prefix>
                <passThrough>true</passThrough>
                <messageString>Hi. This is the James mail server at [machine].
I successfully delivered your message to the following addresses.
Note that it indicates your recipients received the message but do
not imply they read it.</messageString>
                <action>delivered</action>
                <defaultStatus>2.0.0</defaultStatus>
            </mailet>
            <mailet match="All" class="Null"/> <!-- ignore people not having requesting a dsn success bounce -->
        </processor>

        <processor state="relay" enableJmx="true">
            <!-- Perform at most 5 RemoteDelivery attempts -->
            <mailet match="AtMost=5" class="RemoteDelivery">
                <outgoingQueue>outgoing</outgoingQueue>
                <maxRetries>0</maxRetries>
                <maxDnsProblemRetries>0</maxDnsProblemRetries>
                <deliveryThreads>10</deliveryThreads>
                <sendpartial>true</sendpartial>
                <!-- Use a custom processor for error handling -->
                <bounceProcessor>remote-delivery-error</bounceProcessor>
            </mailet>
            <!-- When retries are exceeded, consider the mail as a permanent failure -->
            <mailet match="DSNFailureRequested" class="DSNBounce">
                <prefix>[FAILED]</prefix>
                <passThrough>true</passThrough>
                <messageString>Hi. This is the James mail server at [machine].
I'm afraid I wasn't able to deliver your message to the following addresses.
This is a permanent error; I've given up. Sorry it didn't work out.
The remote server we should relay this mail to keep on failing.
Below I include the list of recipients, and the reason why I was unable to deliver
your message.</messageString>
                <action>failed</action>
                <defaultStatus>5.0.0</defaultStatus>
            </mailet>
            <mailet match="All" class="ToRepository">
                <repositoryPath>cassandra://var/mail/error/remote-delivery/permanent/</repositoryPath>
            </mailet>
        </processor>

        <processor state="remote-delivery-error" enableJmx="true">
            <matcher name="dsn-permanent" match="org.apache.james.mailetcontainer.impl.matchers.And">
                <matcher match="IsRemoteDeliveryPermanentError"/>
                <matcher match="DSNFailureRequested"/>
            </matcher>
            <matcher name="dsn-temporary" match="org.apache.james.mailetcontainer.impl.matchers.And">
                <matcher match="IsRemoteDeliveryTemporaryError"/>
                <matcher match="DSNDelayRequested"/>
            </matcher>

            <mailet match="dsn-permanent" class="DSNBounce">
                <prefix>[FAILED]</prefix>
                <passThrough>true</passThrough>
                <messageString>Hi. This is the James mail server at [machine].
I'm afraid I wasn't able to deliver your message to the following addresses.
This is a permanent error; I've given up. Sorry it didn't work out.
The remote server we should relay this mail to returns a permanent error.
Below I include the list of recipients, and the reason why I was unable to deliver
your message.</messageString>
                <action>failed</action>
                <defaultStatus>5.0.0</defaultStatus>
            </mailet>

            <mailet match="dsn-temporary" class="DSNBounce">
                <prefix>[DELAYED]</prefix>
                <passThrough>true</passThrough>
                <messageString>Hi. This is the James mail server at [machine].
I'm afraid I wasn't able to deliver your message to the following addresses yet.
This is a temporary error: I will keep on trying.
Below I include the list of recipients, and the reason why I was unable to deliver
your message.</messageString>
                <action>delayed</action>
                <defaultStatus>4.0.0</defaultStatus>
            </mailet>

            <!-- Error management for remote delivery error handling as described in remote-delivery-error-handling.adoc -->
        </processor>

        <processor state="local-address-error" enableJmx="true">
            <mailet match="DSNFailureRequested" class="DSNBounce">
                <prefix>[FAILED]</prefix>
                <passThrough>true</passThrough>
                <messageString>Hi. This is the James mail server at [machine].
I'm afraid I wasn't able to deliver your message to the following addresses.
This is a permanent error; I've given up. Sorry it didn't work out.
The following addresses do not exist here. Sorry.</messageString>
                <action>failed</action>
                <defaultStatus>5.0.0</defaultStatus>
            </mailet>
            <mailet match="All" class="ToRepository">
                <repositoryPath>cassandra://var/mail/address-error/</repositoryPath>
            </mailet>
        </processor>

        <processor state="relay-denied" enableJmx="true">
            <!-- This is an abuse, you likely do not want to be polite with these people. we just keep a copy for later audit & replay -->
            <mailet match="All" class="ToRepository">
                <repositoryPath>cassandra://var/mail/relay-denied/</repositoryPath>
                <notice>Warning: You are sending an e-mail to a remote server. You must be authenticated to perform such an operation</notice>
            </mailet>
        </processor>

        <processor state="rrt-error" enableJmx="false">
            <mailet match="All" class="ToRepository">
                <repositoryPath>cassandra://var/mail/rrt-error/</repositoryPath>
                <passThrough>true</passThrough>
            </mailet>
            <mailet match="IsSenderInRRTLoop" class="Null"/>
            <mailet match="DSNFailureRequested" class="DSNBounce">
                <prefix>[FAILED]</prefix>
                <passThrough>true</passThrough>
                <messageString>Hi. This is the James mail server at [machine].
I'm afraid I wasn't able to deliver your message to the following addresses.
This is a permanent error; I've given up. Sorry it didn't work out.
The following addresses is caught in a rewriting loop. An admin should come and fix it (you likely want to report it).
Once resolved the admin should be able to resume the processing of your email.
Below I include the list of recipients, and the reason why I was unable to deliver
your message.</messageString>
                <action>failed</action>
                <defaultStatus>5.1.6</defaultStatus>
            </mailet>
        </processor>
    </processors>
</mailetcontainer>

Limitations

The out of the box tooling do not allow generating relayed DSN notification as RemoteDelivery misses a success callback.