[Greylist-users] Handling servers that don't wait on their retries

Ian Ballantyne ian at midori.shacknet.nu
Wed Feb 25 17:33:43 PST 2009


Hi,

Before I even get started here with my second attempt to post this, I've made
a slight mess tonight, sending mails to the wrong address (requests instead
of users), messing up my config so that mails get rejected that shouldn't have 
been, and sending other strange mails as a result.  To those at the receiving 
end of my mistakes tonight, sorry.....

On to the post as it was originally intended:

This problem has been around for a while for me, and I have had a solution in
place now also for a long time now which I would like to share, and which I hope
will get included in the relaydelay script.

There are some mail servers that connect to mine and get greylisted for a while.  
These however do not wait with their retries, instead retrying repeatedly until 
the block expires.  These retries are usually spaced only a few seconds apart, 
resulting in hundreds of retries during the block period and an unnecessary 
server and network load.  I have complained to the server admins, pointing out
RFC 2821 section 4.5.4.1, however they do not change their configurations 
instead telling me their servers are fine.

I modified the relaydelay script and added functionality that rejects emails if 
the sending server refuses to behave correctly and not retry too often during
the block period.  It is simply checks the blocked_count and if it gets over a 
threshhold, then it rejects the mail with a 554, 5.7.0 error.  As far as I can
tell, this is the most appropriate error respons code in this situation.  I have
not programmed in anything to undo the block, it gets automatically removed
when the record expires and is moved to the reporting table after 36 days.

I have included two patches below, one for the conf file and one for the perl
script.  Don't kill me for my coding, I'm no perl programmer, so I hope what I've
written is ok.  

I would appreciate some feedback on this.  My hope is that if this gets spread
widely enough, admins of misbehaving mail servers will correct their configs.
What do the people on this list think of this?

And finally, I hope you can understand what I'm on about.  I'm rather tired, 
which usually means I can't express myself that well...... (addenum: and make
some really stupid mistakes...)

regards from Vienna, Austria
Ian




--- relaydelay.0.04.conf        2009-02-25 22:30:25.000000000 +0100
+++ relaydelay.0.05.conf        2009-02-25 22:21:22.000000000 +0100
@@ -155,6 +155,11 @@
 #   used if $reverse_mail_tracking is enabled.
 $reverse_mail_life_secs = 4 * 24 * 3600;  # 4 Days

+# This sets the maximum number of retires we are willing to tolerate
+#   before deciding that a mail server does not sufficiently observe
+#   RFC 2821 section 4.5.4.1. Anything over this will get rejected.
+$max_retry_attempts = 10;
+
 #############################################################
 # End of options for use in external config file
 #############################################################


-------------------------------------------------------


--- relaydelay.0.04.pl  2009-02-25 22:22:13.000000000 +0100
+++ relaydelay.0.05.pl 2009-02-25 22:24:41.000000000 +0100
@@ -186,6 +186,11 @@
 #   used if $reverse_mail_tracking is enabled.
 my $reverse_mail_life_secs = 4 * 24 * 3600;  # 4 Days

+# This sets the maximum number of retires we are willing to tolerate
+#   before deciding that a mail server does not sufficiently observe
+#   RFC 2821 section 4.5.4.1. Anything over this will get rejected.
+my $max_retry_attempts = 10;
+

 #############################################################
 # End of options for use in external config file
@@ -735,7 +740,7 @@
   }

   # Check to see if we already know this triplet set, and if the initial block is expired
-  my $query = "SELECT id, NOW() > block_expires, origin_type, relay_ip FROM relaytofrom "
+  my $query = "SELECT id, NOW() > block_expires, origin_type, relay_ip, blocked_count FROM relaytofrom "
     .         "  WHERE record_expires > NOW() "
     .         "    AND mail_from = " . $dbh->quote($mail_from)
     .         "    AND rcpt_to   = " . $dbh->quote($rcpt_to);
@@ -757,12 +762,18 @@

   my $sth = $dbh->prepare($query) or goto DB_FAILURE;
   $sth->execute() or goto DB_FAILURE;
-  ($rowid, my $block_expired, my $origin_type, my $recorded_relay_ip) = $sth->fetchrow_array();
+  ($rowid, my $block_expired, my $origin_type, my $recorded_relay_ip, my $blocked_count) = $sth->fetchrow_array();
   goto DB_FAILURE if ($sth->err);
   $sth->finish();

   if (defined $rowid) {
-    if ($block_expired) {
+    if ($blocked_count > $max_retry_attempts) {
+      # Too many retries, the mail server sending the mail doesn't observe RFC 2821, section 4.5.4.1
+      #    Reject the mail.
+      print "  Email is known but too many retires to deliver $blocked_count  Rejecting with 550.\n" if ($verbose);
+      goto BOUNCE_MAIL;
+    }
+    elsif ($block_expired) {
       print "  Email is known and block has expired.  Passing the mail.  rowid: $rowid\n" if ($verbose);
       # If this record is a reverse tracking record with unknown IP, then
       #   update it to include the now-known IP (if tracking is enabled)
@@ -857,10 +868,12 @@


   BOUNCE_MAIL:
-  # We don't use this anywhere yet, but may in future...
-  # set privdata so later callbacks won't have problems
-  $privdata = "0";
-  $ctx->setpriv(\$privdata);
+  if (defined $rowid) {
+    $dbh->do("UPDATE relaytofrom SET blocked_count = blocked_count + 1 WHERE id = $rowid") or goto DB_FAILURE;
+    $privdata = "$rowids\x00$mail_from\x00$rcpt_to";
+  }
+  $ctx->setpriv($privdata_ref);
+  $ctx->setreply("554", "5.7.0", "Rejected by Greylisting, too many delivery retries. See RFC 2821 section 4.5.4.1 and contact 
your mail server administrator.");
   # Indicate the message should be aborted (want a custom error code?)
   return SMFIS_REJECT;


-------------------------------------------------------


More information about the Greylist-users mailing list