Selective sendmail postmaster

Recently, I see more "DSN: Return receipt" requests from UBE/spam messages. Since, the sender's and envelope addresses are in general forged and invalid these DSNs lead to bounces (in fact double bounces) and, if your Sendmail is configured like ours, will produce "Postmaster notify" messages.

Sendmail has an option to disallow or ignore receipts (noreceipts in PRIVACY_FLAGS) but I have chosen to allow them. Nevertheless, the "Postmaster notify" messages caused by double bounces are annoying.

For some years, I have already had a script running that discarded messages caused by formerly removed accounts. If a message is received for a locally unknown user Sendmail will generate a "User Unknown" notification. However, if the original sender's address is invalid (like with most UBE/spam) this notification will bounce back and it will cause a "postmaster notify" which we again want to discard.

My script check_invalid_bounce is simple (and probably there is a more elegant solution but it does the job). It is fired off from procmail with (something like)

# Postmaster notifications: Delete User Unknown bounces
:0 BH
* ^From: .*MAILER-DAEMON@myserver...
* ^Subject: (Postmaster notify|Returned mail): .*
* ? check_invalid_bounce
  LOG="   --- user unknown bounce$NL"

If check_invalid_bounce returns TRUE the message will be delivered to the folder doublebounces.


Since I already had this script I extended it to also discard messages notifying the postmaster of a bouncing "DSN: return receipt". Fortunately, this is easier than the original task.

The scripts essential lines are the following. (Note that this might not work at all. Use at your own risk.)

# Mail.log
# List the usernames of invalid accounts as a egrep pattern
# parse input (message at stdin), get MTA msg ID and the bouncing address
INPUTMSG=$(sed -ne '
  /^with id [a-zA-Z0-9]\+$/{s/with id \([a-zA-Z0-9]\+\)$/id=\1/;p}
  /^\(550 5\.1\.1 \|>>> RCPT To:\)<..*@...*>/{s/.*<\(..*@...*\)>.*/addr=\1/;p}
  | head -n2)

In most cases this will store in variable INPUTMSG 2 lines similar to


Because we had sed running anyway we dare to use it again to simplify parsing INPUTMSG even though it is a bit expensive. (If you prefer to do this in Perl feel free to go ahead, and send me a copy of the script.)

# get sendmail's message id
MTAMSGID=$(echo "$INPUTMSG" | sed -ne '/^id=/{s/^id=//;p}' )
# get the address that bounces
BOUNCEADDR=$(echo "$INPUTMSG" | sed -ne '/^addr=/{s/^addr=//;p}' )

There will always be a MTAMSGID. BOUNCEADDR could be empty, though.

# return true if it's a bounce of a DSN: Return receipt
grep -q -- "$MTAMSGID: DSN: Return receipt$" /var/log/mail.log \
&& exit 0

You see, getting rid of the DSNs is simply. Finding messages bouncing off invalid accounts is not that easy because the "User unknown" log message does not show up in the same line as BOUNCEADDR. I use grep's -A parameter (yes it is dirty) to also print the line after the line with BOUNCEADDR. If an INVALID user shows up there the script returns TRUE.

# bail out if no address found
if [ "/$BOUNCEADDR/" = '//' ] ; then exit 1 ; fi
# return true if it's a bounce of an invalid account
grep -A1 -- "$BOUNCEADDR" /var/log/mail.log \
| egrep -q "to=$INVALID" \
&& exit 0
# else
exit 1

Of course, this is no acceptable approach for high volume mail servers. Though, those are likely to ignore DSN and to discard double bounces all together with e.g.

define(`confPRIVACY_FLAGS', `goaway,noreceipts')dnl