JR's patch to not send confirms for obvious spam
"J.R. Oldroyd" <[email protected]> Mon, 24 May 2004 13:03:55 -0400
| Newsgroups | gmane.mail.spam.active-spam-killer.general |
|---|---|
| Message-ID | <[email protected]> |
I am the honored recipient of an average of 350 spam messages a day
which ASK has been filtering out for me very successfully for quite some
time now.
In recent weeks and months, however, I have noticed that the number of
spam messages getting through has been increasing. On some days as much
as 10-15% was getting through, which at 350/day becomes a noticably
annoying amount.
On analysis it seems that all of the spam that is getting through falls
into two categories:
- message appears to be from mailer-daemon
- spammer is replying to ASK's confirm message
I would like to offer the patch below which addresses both of these
problems. Using it, I am back to zero spam, and so far, no false
positives. This is a patch to ASK 2.4.1-1.
The patch makes four changes:
1. The mailer-daemon problem is due spam matching ASK's mailer-daemon
check when in fact the message is not a mailer-daemon or postmaster
message. Much spam contains headers something like:
From: <[email protected]>
Reply-To: <postmaster@##.##.##.##>
(##.## = some IP address). ASK 2.4.1-1 uses the Reply-To header in
preference to the From header when checking to see if the message
is from mailer-daemon. This is wrong. The Reply-To header should
not be checked in this case. The patch fixes this.
2. The second problem, that of spammers replying to confirm messages,
can obviously be reduced if the number of confirms sent out in the
first place is reduced. On looking at the spam I get, I observed
that in over 50% of the spam to which ASK is sending confirms,
none of my recipient addresses were included in the message headers.
This can happen in three cases, (a) if a message is sent to an alias
that forwards to me, (b) if the message is to a mailinglist I am
on, or (c) if the message is spam and the spammer used my address
in the envelope but a different recipient list (or no list) in the
headers. I can match my aliases and mailinglists in my whitelist.
The remaining messages can simply be classed as Junk and not sent
a confirm.
By doing this, the number of confirms ASK sends is reduced by
over 50%. One would presume that this would therefore reduce the
chances of spammers replying to confirms by about half, and thereby
cut spam that is getting through by about half. In fact, however,
I have seen no spam in my inbox at all since developing this patch,
suggesting that the spammers that do reply to confirms tend to be
predominantly those who don't include my address in their message.
Perhaps something to do with their use of automated scripts to
send bulk, non-personalized email and to look for and reply to
ASK's confirms.
So rather than a 50% decline in spam getting through, this change
seems to give a considerably better result.
Before using this patch, it is necessary to make sure that all
your genuine incoming mail is to an address on your rc_mymails
list, or it will match your whitelist, or it contains your mailkey.
Since some users may not wish work in this way, I have introduced a
new .askrc variable to entirely enable or disable this functionality.
By default, ASK behaves as before, but if you include:
# Set if you don't want to send ASK confirms for mail which
# doesn't include any of your rc_mymails addresses
rc_dont_confirm_unknown_recip = true
in your .askrc, you get the new functionality.
3. The patch also restores the use of the rc_junkmailbox and
rc_bulkmailbox variables. If these variables are defined, email
classified as Junk or Bulk will be saved in those files. If the
variables are not defined, such email is saved in the queue dir,
which is the current functionality.
4. Finally, the patch also adds a blank line in the log at the start
of each ASK run and corrects an instance where "mailer-daemon" was
logged as "mailer_daemon". This is just to make the log a little
easier to read and search.
OK, here's the patch. I welcome folks' comments. These diffs are all
to files in /usr/lib/ask. To apply the patch, save it in a file, and
do something like this:
# cd /usr/lib
# mv ask ask-2.4.1-1
# mkdir ask-2.4.1-1-jr
# ln -s ask-2.4.1-1-jr ask
# cp ask-2.4.1-1/* ask
# cd ask
# patch -p1 <patchfile
Then edit your .askrc and add the rc_dont_confirm_unknown_recip variable
as shown above.
Good luck.
diff -c ask-2.4.1-1/askconfig.py ask-2.4.1-1-jr/askconfig.py
*** ask-2.4.1-1/askconfig.py 2003-05-24 11:27:42.000000000 -0400
--- ask-2.4.1-1-jr/askconfig.py 2004-05-04 19:47:32.000000000 -0400
***************
*** 134,139 ****
--- 134,141 ----
self.rc_confirm_mailbody = self.__expand("rc_confirm_mailbody", mandatory = 0)
self.rc_blacklist_mailbody = self.__expand("rc_blacklist_mailbody", mandatory = 0)
+ self.rc_dont_confirm_unknown_recip = self.__expand("rc_dont_confirm_unknown_recip", mandatory = 0, boolean = 1)
+
self.rc_confirm_dirs = self.__expand("rc_confirm_dirs", listmode = 1, mandatory = 0)
self.rc_blacklist_dirs = self.__expand("rc_blacklist_dirs", listmode = 1, mandatory = 0)
self.rc_confirm_langs = self.__expand("rc_confirm_langs", listmode = 1, mandatory = 0)
diff -c ask-2.4.1-1/askmain.py ask-2.4.1-1-jr/askmain.py
*** ask-2.4.1-1/askmain.py 2003-05-24 11:27:42.000000000 -0400
--- ask-2.4.1-1-jr/askmain.py 2004-05-06 09:56:17.000000000 -0400
***************
*** 81,86 ****
--- 81,87 ----
"""
## Initial Logging
+ self.log.write(1, "")
self.log.write(1, "----- ASK v%s Started -----" % askversion.AskVersion.version)
self.msg.read(filehandle)
***************
*** 133,139 ****
## an invalid sender address)
if self.msg.is_from_mailerdaemon():
! self.log.write(1, "Sender is mailer_daemon")
if self.msg.sent_by_ask():
self.log.write(1, "Header X-AskVersion found. Probable invalid reply from a confirmation message. Ignoring.")
--- 134,140 ----
## an invalid sender address)
if self.msg.is_from_mailerdaemon():
! self.log.write(1, "Sender is mailer-daemon")
if self.msg.sent_by_ask():
self.log.write(1, "Header X-AskVersion found. Probable invalid reply from a confirmation message. Ignoring.")
***************
*** 228,236 ****
if self.msg.is_queued():
self.log.write(1, "Message is already queued. Won't resend.")
else:
! self.log.write(1, "Sending confirmation.")
! self.msg.queue_mail()
! self.msg.send_confirmation()
return(self.config.RET_PROCMAIL_STOP)
--- 229,240 ----
if self.msg.is_queued():
self.log.write(1, "Message is already queued. Won't resend.")
else:
! if self.msg.match_recipient() == "" and self.config.rc_dont_confirm_unknown_recip:
! self.msg.junk_mail("(Junk) Message with none of our addresses in recipient list")
! else:
! self.log.write(1, "Sending confirmation.")
! self.msg.queue_mail()
! self.msg.send_confirmation()
return(self.config.RET_PROCMAIL_STOP)
diff -c ask-2.4.1-1/askmessage.py ask-2.4.1-1-jr/askmessage.py
*** ask-2.4.1-1/askmessage.py 2003-05-24 11:27:42.000000000 -0400
--- ask-2.4.1-1-jr/askmessage.py 2004-05-06 08:58:47.000000000 -0400
***************
*** 428,434 ****
Returns true if this message comes from MAILER-DAEMON. False otherwise
"""
! if re.match("mailer-daemon|postmaster@|<>", self.get_sender()[1], re.IGNORECASE):
return 1
else:
return 0
--- 428,434 ----
Returns true if this message comes from MAILER-DAEMON. False otherwise
"""
! if re.match("mailer-daemon|postmaster@|<>", self.get_sender(ignore_header = "Reply-To")[1], re.IGNORECASE):
return 1
else:
return 0
***************
*** 464,470 ****
"""
self.log.write(1, " junk_mail(): Queueing Junk Message")
! self.queue_mail(custom_header = custom_header)
return 0
#------------------------------------------------------------------------------
--- 464,474 ----
"""
self.log.write(1, " junk_mail(): Queueing Junk Message")
! if self.config.rc_junkmailbox:
! self.mail.deliver_mail(self.config.rc_junkmailbox, self.tmpfile, custom_header)
! else:
! self.queue_mail(custom_header = custom_header)
!
return 0
#------------------------------------------------------------------------------
***************
*** 475,481 ****
"""
self.log.write(1, " bulk_mail(): Queueing Bulk Message")
! self.queue_mail(custom_header = custom_header)
return 0
#------------------------------------------------------------------------------
--- 479,489 ----
"""
self.log.write(1, " bulk_mail(): Queueing Bulk Message")
! if self.config.rc_bulkmailbox:
! self.mail.deliver_mail(self.config.rc_bulkmailbox, self.tmpfile, custom_header)
! else:
! self.queue_mail(custom_header = custom_header)
!
return 0
#------------------------------------------------------------------------------
***************
*** 513,521 ****
queueFileName = self.queue_file()
! self.log.write(1, " send_confirmation(): Sending confirmation to %s" % self.get_real_sender()[1])
! self.mail.mailfrom = self.__match_recipient()
## Add Precedence: bulk and (if appropriate) "In-Reply-To"
--- 521,532 ----
queueFileName = self.queue_file()
! self.mail.mailfrom = self.match_recipient()
! if self.mail.mailfrom == "":
! self.mail.mailfrom = self.config.rc_mymails[0]
!
! self.log.write(1, " send_confirmation(): Sending confirmation from %s to %s" % (self.mail.mailfrom, self.get_real_sender()[1]))
## Add Precedence: bulk and (if appropriate) "In-Reply-To"
***************
*** 561,566 ****
--- 572,579 ----
os.unlink(tempFile)
+ return 1
+
#------------------------------------------------------------------------------
def __check_confirm_list(self, address):
***************
*** 744,750 ****
self.log.write(1, " send_nastygram(): Sending nastygram back")
! self.mail.mailfrom = self.__match_recipient()
self.mail.send_mail(self.get_real_sender()[1],
"Please stop sending me emails",
--- 757,763 ----
self.log.write(1, " send_nastygram(): Sending nastygram back")
! self.mail.mailfrom = self.match_recipient()
self.mail.send_mail(self.get_real_sender()[1],
"Please stop sending me emails",
***************
*** 1059,1065 ****
fh_output.close()
#------------------------------------------------------------------------------
! def __match_recipient(self):
"""
This function will do its best to determine which of our rc_mymails was
the recipient of the current message. If no entry can be found in the
--- 1072,1078 ----
fh_output.close()
#------------------------------------------------------------------------------
! def match_recipient(self):
"""
This function will do its best to determine which of our rc_mymails was
the recipient of the current message. If no entry can be found in the
***************
*** 1070,1081 ****
for (recipient_name, recipient_mail) in self.get_recipients():
for our_address in self.config.rc_mymails:
if recipient_mail == our_address:
! self.log.write(1, " __match_recipient(): Found a match with %s" % our_address)
return our_address
! self.log.write(1, " __match_recipient(): No Match found. Using %s" % self.config.rc_mymails[0])
! return self.config.rc_mymails[0]
#------------------------------------------------------------------------------
--- 1083,1094 ----
for (recipient_name, recipient_mail) in self.get_recipients():
for our_address in self.config.rc_mymails:
if recipient_mail == our_address:
! self.log.write(1, " match_recipient(): Found a match with %s" % our_address)
return our_address
! self.log.write(1, " match_recipient(): No Match found.")
! return ""
#------------------------------------------------------------------------------
-------------------------------------------------------
This SF.Net email is sponsored by: Oracle 10g
Get certified on the hottest thing ever to hit the market... Oracle 10g.
Take an Oracle 10g class now, and we'll give you the exam FREE.
http://ads.osdn.com/?ad_id=3149&alloc_id=8166&op=click