--- ./relaydelay.pl	Mon Jul 21 20:30:59 2003
+++ /usr/local/sbin/relaydelay.pl	Fri Feb  4 11:29:03 2005
@@ -37,6 +37,7 @@
 use Errno qw(ENOENT);
 
 use DBI;
+use FileHandle;
 
 use strict;
 
@@ -54,6 +55,9 @@
 # If you do/don't want to see debugging messages printed to stdout,
 #   then set this appropriately.
 my $verbose = 1;
+my $run_as_daemon = 0; # Default is to still run in the foreground
+my $log_file = '';     # Default is to use STDOUT for messages.
+                       # A log file should be used for daemonized operation.
 
 # Database connection params
 my $database_type = 'mysql';
@@ -132,6 +136,7 @@
 # If both are enabled, relay_ip is considered to take precedence, and is 
 #   checked first.  A match there will ignore the rcpt checks.
 my $check_wildcard_relay_ip = 1;
+my $check_wildcard_relay_domain = 1;
 my $check_wildcard_rcpt_to = 1;
 
 # Set this to a nonzero value if you want to wait until after the DATA
@@ -378,7 +383,8 @@
   }
 
   # Only if we have some rowids, do we update the count of passed messages
-  if ($rowids > 0) {
+  #if ($rowids > 0) {
+	if (substr($rowids,0,1) > 0) {
     # split up the rowids and update each in turn
     my @rowids = split(",", $rowids);
     foreach my $rowid (@rowids) {
@@ -447,7 +453,8 @@
   
   # only increment the aborted_count if have some rowids 
   #   (this means we didn't expect/cause an abort, but something else did)
-  if ($rowids > 0) {
+  #if ($rowids > 0) {
+	if (substr($rowids,0,1) > 0) {
     # Ok, we need to update the db, so get a handle
     my $dbh = db_connect(0) or goto DB_FAILURE;
   
@@ -641,7 +648,53 @@
   # Check for local IP relay whitelisting from the sendmail access file
   # FIXME - needs to be implemented
   #
-
+  # Check wildcard black or whitelisting based on sender domain
+  #   Do the check in such a way that more exact matches are returned first
+  if ($check_wildcard_relay_domain) {
+ 	my $tstr = $mail_from;
+  	if ($tstr =~ /\A<(.*)>\Z/) {  # Remove outer angle brackets if present
+    	$tstr = $1;
+  	}
+  	$tstr =~ /@([^@]*)\Z/;  # strip off everything before and including the last @
+  	$tstr = $1;
+
+	my @domainarray = split('\.', $tstr);
+	while (@domainarray) {
+	    my $sender_domain = join('.', @domainarray);
+
+	    my $subquery = "mail_from = " . $dbh->quote($sender_domain);
+	    my $query = "SELECT id, block_expires > NOW(), block_expires < NOW() FROM relaytofrom "
+	      .         "  WHERE record_expires > NOW() "
+	      .         "    AND relay_ip IS NULL "
+	      .         "    AND rcpt_to IS NULL "
+	      .         "    AND origin_type = \"MANUAL\" "
+	      .         "    AND ($subquery) "
+	      .         "  ORDER BY length(relay_ip) DESC";
+	                                                                                                                                  
+	    my $sth = $dbh->prepare($query) or goto DB_FAILURE;
+	    $sth->execute() or goto DB_FAILURE;
+	    ($rowid, my $blacklisted, my $whitelisted) = $sth->fetchrow_array();
+	    goto DB_FAILURE if ($sth->err);
+	    $sth->finish();
+	                                                                                                                                  
+	    if (defined $rowid) {
+	      if ($blacklisted) {
+		print "  Sender domain parse: $sender_domain\n" if ($verbose);
+	        print "  Blacklisted domain.  Skipping checks and rejecting the mail.\n" if ($verbose);
+	        goto DELAY_MAIL;
+		last;
+	      }
+	      if ($whitelisted) {
+		print "  Sender domain parse: $sender_domain\n" if ($verbose);
+	        print "  Whitelisted domain.  Skipping checks and passing the mail.\n" if ($verbose);
+	        reverse_track($dbh, $mail_from, $rcpt_to) if ($reverse_mail_tracking and $rcpt_mailer !~ /\Alocal\Z/i);
+	        goto PASS_MAIL;
+		last;
+      	      }
+            }
+	    shift(@domainarray);
+        }
+  }
   # Check wildcard black or whitelisting based on ip address or subnet
   #   Do the check in such a way that more exact matches are returned first
   if ($check_wildcard_relay_ip) {
@@ -888,7 +941,8 @@
     #   recipients, and we need it for logging.
     # The format of the privdata is one or more rowids seperated by commas, followed by 
     #   a null, and the envelope from.
-    if ($rowids > 0) {
+    #if ($rowids > 0) {
+	if (substr($rowids,0,1) > 0) {
        $rowids .= ",$rowid";
     } else {
       $rowids = $rowid;  
@@ -921,8 +975,6 @@
   # make sure the config is only loaded once per instance
   return if ($config_loaded);
 
-  print "Loading Config File: $config_file\n";
-
   # Read and setup our configuration parameters from the config file
   my($msg);
   my($errn) = stat($config_file) ? 0 : 0+$!;
@@ -935,11 +987,24 @@
   #do $config_file;
   if ($@ ne '') { die "Error in config file $config_file: $@" }
 
-  $config_loaded = 1;
-}
+  if ($log_file) { # Keep the output in a log file
+    open STDOUT, ">>$log_file" or die "Couldn't redirect STDOUT to $log_file: $!";
+    STDOUT->autoflush(1);
+  }
 
+  print "Loaded Config File: $config_file\n" if $verbose;
 
+  $config_loaded = 1;
+}
 
+sub catch_sig {
+  my $signame = shift;
+  print "Got a SIG$signame.\nClosing DB connection.\n" if $verbose;
+  db_disconnect();
+  print "Exiting relaydelay daemon process.\n";
+  close(STDOUT);
+  exit 0;
+}
 
 my %my_callbacks =
 (
@@ -975,33 +1040,25 @@
   # Make sure there are no errors in the config file before we start, and load the socket info
   load_config();
 
-  # Record pid to file
-  if (defined $relaydelay_pid_file) {
-    open(PIDF, ">$relaydelay_pid_file") ||
-      die "Unable to record PID to '$relaydelay_pid_file': $!\n";
-    print PIDF "$$\n";
-    close PIDF;
-  }
-
-  print "Using connection '$milter_socket_connection' for filter $milter_filter_name\n";
+  print "Using connection '$milter_socket_connection' for filter $milter_filter_name\n" if $verbose;
 
   if ($milter_socket_connection =~ /^local:(.+)$/i) {
     my $unix_socket = $1;
 
     if (-e $unix_socket) {
-      print "Attempting to unlink local UNIX socket '$unix_socket' ... ";
+      print "Attempting to unlink local UNIX socket '$unix_socket' ... " if $verbose;
 
       if (unlink($unix_socket) == 0) {
-        print "failed.\n";
+        print "failed.\n" if $verbose;
         exit;
       }
-      print "successful.\n";
+      print "successful.\n" if $verbose;
     }
   }
 
   if (not Sendmail::Milter::setconn("$milter_socket_connection")) {
-    print "Failed to set up connection: $?\n";
-    exit;
+    die "Failed to set up connection: $?\n";
+#    exit;
   }
 
   # Make sure we can connect to the database 
@@ -1016,8 +1073,41 @@
   #
 
   if (not Sendmail::Milter::register("$milter_filter_name", \%my_callbacks, SMFI_CURR_ACTS)) {
-    print "Failed to register callbacks for $milter_filter_name.\n";
-    exit;
+    die "Failed to register callbacks for $milter_filter_name.\n";
+#    exit;
+  }
+
+  if ($run_as_daemon) {
+    if (not $log_file) {print "Warning: Running as a daemon, but output has not been redirected to a log file.\n";}
+# I don't think we have to worry about reaping zombies since we're only 
+# spawning a single child process then exiting:
+#    $SIG{CHLD} = 'IGNORE'; # Automatically reap children
+    defined(my $child_pid = fork()) or die "Couldn't fork daemon process:$!";
+    if ($child_pid) {
+      # I must be the parent:
+      print "Spawned relaydelay daemon process $child_pid.\n" if $verbose;
+      exit 0;
+    }
+  }
+
+  # I must be the child (or using foreground operation):
+  # Record pid to file
+  if (defined $relaydelay_pid_file) {
+    open(PIDF, ">$relaydelay_pid_file") ||
+      die "Unable to record PID to '$relaydelay_pid_file': $!\n";
+    print PIDF "$$\n";
+    close PIDF;
+  }
+
+  if ($run_as_daemon) {
+    # Be a nice daemon:
+    POSIX::setsid or die "Couldn't start a new session: $!";
+    chdir '/' or die "Couldn't chdir to /: $!";
+    open STDIN, '/dev/null' or die "Couldn't redirect STDIN from /dev/null: $!";
+    open STDERR, '>&STDOUT' or die "Couldn't dup STDOUT: $!";
+    my $sigset = POSIX::SigSet->new();
+    my $action = POSIX::SigAction->new('catch_sig',$sigset,&POSIX::SA_NODEFER);
+    POSIX::sigaction(&POSIX::SIGQUIT, $action);
   }
 
   print "Starting Sendmail::Milter $Sendmail::Milter::VERSION engine.\n";
@@ -1035,8 +1125,10 @@
 
 # Make sure when threads are recycled that we release the global db connection
 END {
-  print "Closing DB connection.\n";
-  db_disconnect();
+  if (not $run_as_daemon) { # Signal handler does this in daemon mode
+    print "Closing DB connection.\n" if $verbose;
+    db_disconnect();
+  }
 }
 
 
--- ./relaydelay.conf	Mon Jul 21 20:30:59 2003
+++ ../relaydelay.conf	Fri Feb  4 11:32:02 2005
@@ -23,6 +23,8 @@
 # If you do/don't want to see debugging messages printed to stdout,
 #   then set this appropriately.
 $verbose = 1;
+$run_as_daemon = 0;
+$log_file = '/var/log/relaydelay.log';
 
 # Database connection params
 $database_type = 'mysql';
@@ -63,7 +65,7 @@
 #   already pretty high, and should be sufficient for all but very 
 #   large sites.
 # Setting to zero makes the number of threads unlimited.
-$maximum_milter_threads = 40;
+$maximum_milter_threads = 80;
 
 # This determines how many seconds we will block inbound mail that is
 #   from a previously unknown [ip,from,to] triplet.  If it is set to
@@ -101,6 +103,7 @@
 # If both are enabled, relay_ip is considered to take precedence, and is 
 #   checked first.  A match there will ignore the rcpt checks.
 $check_wildcard_relay_ip = 1;
+$check_wildcard_relay_domain = 1;
 $check_wildcard_rcpt_to = 1;
 
 # Set this to a nonzero value if you want to wait until after the DATA
