Shell Scripting the Smugness Right Off My Face
When the whiny bitches start nagging me with questions about the mail server, my response is usually "No, nothing's wrong with e-mail." I'm smug like this, because I run qmail. qmail never breaks.
Problem is, qmail isn't the only thing that handles my mail. I also use procmail, which does break sometimes. Especially when I dictate that a message clearly marked as a virus should be written to a special file and not delivered to the intended recipient. Sometimes, that doesn't work the way I want it to, and of course, this didn't come up in testing.
So this morning, the whiny bitches were right. Normal messages were getting treated as viruses, and getting written to the quarantine file instead of the users' inboxes. Now I have a problem: I've got a billion messages that were never delivered to their intended recipients. And you know they're going to complain if they don't get their mail. What is a sysadmin to do?
Fix it, that's what. Quit dicking around, get curt with people who call and interrupt you, and find a solution. Here it is.
First, you're not stupid. Suspicious messages shouldn't be automatically deleted until you've reliably proven that your filtering methods are sound. In this case, they were not. Say you've set it up so that the suspect mail goes to /var/mail/attachmentsdenied. This is a typical mbox-style mail file: it's not collision-proof, but we're trying to store suspicious mail, not pamper it.
We have to split these messages up, because we have to parse each one individually. My old formail fix to the rescue. You'll need formail (reformail if you use maildrop) and safecat. Copy the mailbox to a different location, such as "denied.txt" to prevent procmail from writing to it while you're trying to read from it.
$ /var/qmail/bin/maildirmake ./Denied/
$ formail -I'From ' -s maildir ./Denied/ < ./denied.txt
Now you need a gnarly shell script that will rip open each of the
messages, understand who sent them, who was supposed to receive them,
and make all right again. I think it goes a little something like
this:
#!/bin/sh
# The maildir application will turn each message into a file,
# ending with a "." and your hostname. My host is named "mordheim".
for i in *.mordheim
do
# Set variables for the contents of the "From" and "To" fields.
#
# The sed command merely removes trailing whitespace, so that
# " bob@domain.com" becomes "bob@domain.com".
#
# The 822field program is part of the mess822 package available
# at http://cr.yp.to/mess822.html.
HDRFROM=`822field "From: " < $i | sed -e 's/^[ ]*//'`
HDRTO=`822field "To: " < $i | sed -e 's/^[ ]*//'`
# Print to stdout: Who is the message from?
echo -n "from="
echo $HDRFROM
# For debugging only: Print to stdout: Who is the message to?
echo -n " to="
echo $HDRTO
echo ""
# This helps me match a message to a filename while testing the
# script.
echo "sending message $i..."
# The meat of the script: new-inject (also part of the mess822
# package) reads control information from a variety of places.
#
# Running "man new-inject" will explain its behavior in detail.
# Rewrite the From field.
export QMAIL-INJECT="F"
# What the From field is rewritten to be.
export QMAILNAME=$HDRFROM
# Strip the "Delivered-To" field out of the header and
# reinject the message into the MTA. Without removing the
# "Delivered-To" line, qmail will automatically assume the
# message is looping, which, in a way, it is. But we have to
# let it in order for it to get delivered properly.
#
# This can also be done easily in Perl, but sed is faster and
# uses less memory.
sed -e '/^Delivered-To/d'<$i | /usr/local/bin/new-inject
# More debugging: Tell me when the message has been injected.
echo "sent to $HDRTO"
done
# When finished with each message, quit.
exit 0;
Never forget that new-inject supports the "-n" flag, which writes the message to stdout instead of injecting it into the MTA. This is invaluable for testing purposes.
Of course, now that I've run the script and delivered all of the orphaned mail, the phone calls aren't "Is mail broken? I'm not getting any messages." They're "Is mail broken? I just got a bunch of mail." Some people can never be satisfied. With a little shell scripting and some awesome tools, I still consider this day saved.
Now the question on my mind is "How would I have fixed this if I'd been using Maildir?" I'll have to meditate on that one for a few. But first, a celebratory dance.
No comments:
Post a Comment