From c3c8a5107d39e3b7980d5aa25ad77cacd8b77085 Mon Sep 17 00:00:00 2001 From: mpaperno Date: Thu, 30 Aug 2012 00:01:15 -0400 Subject: Old versions archived. --- previous-versions/spampd-0.0.5.pl | 510 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 510 insertions(+) create mode 100644 previous-versions/spampd-0.0.5.pl (limited to 'previous-versions/spampd-0.0.5.pl') diff --git a/previous-versions/spampd-0.0.5.pl b/previous-versions/spampd-0.0.5.pl new file mode 100644 index 0000000..05c32de --- /dev/null +++ b/previous-versions/spampd-0.0.5.pl @@ -0,0 +1,510 @@ +#! /usr/bin/perl + +# $Id: assassind,v 1.1 2002/04/28 19:08:22 dave Exp $ +# $Source: /var/cvs/src/assassind/assassind,v $ +# Copyright (c) 2002 Dave Carrigan + +package Assassind; + +use strict; +use Net::Server::PreFork; +use Net::SMTP::Server::Client; +use IO::File; +use Getopt::Long; +use Data::Dumper; +use Mail::SpamAssassin; +use Mail::SpamAssassin::NoMailAudit; +#use Mail::Audit; +use Net::SMTP; +use Error qw(:try); + +our @ISA = qw(Net::Server::PreFork); +our $VERSION = '1.0.1'; + +sub dead_letter { + my($self, $client, $message) = @_; + + my $filename = join("/", $self->{assassind}->{dead_letters}, + sprintf("assassind.%d.%d.%f.dead", time(), $$, rand)); + + my $dead = IO::File->new; + unless ($dead->open(">$filename")) { + $self->log(0, "Can't open dead letter file $filename: $!"); + return; + } + chmod 0600, $filename; + + try { + if (defined $message) { + $dead->print($message, "\n") or + throw Error -text => "Can't print to dead letter: $!"; + } + foreach (@{$client->{TO}}) { + $dead->print("TO $_\n") or + throw Error -text => "Can't print to dead letter: $!"; + } + $dead->print("FROM ", $client->{FROM}, "\n") or + throw Error -text => "Can't print to dead letter: $!"; + $dead->print($client->{MSG}) or + throw Error -text => "Can't print to dead letter: $!"; + } catch Error with { + my $e = shift; + $self->log(0, "Warning!!!! Couldn't print dead letter: " . $e->stringify); + }; + + unless ($dead->close) { + $self->log(0, "Warning!!!! Could not close the dead letter file: $!"); + } +} + +sub relay_message { + my($self, $client) = @_; + + my $start = time; + my $msg_resp; + + # Now read in message + my $message = $client->{MSG}; + + # Skip processing message over 256K (need to make this an option) + if ( length($message) < ($self->{assassind}->{maxsize} * 1024) ) { + + my @msglines = split (/\r?\n/, $message); + my $arraycont = @msglines; for(0..$arraycont) { $msglines[$_] .= "\r\n"; } + # Audit the message + my $mail = Mail::SpamAssassin::NoMailAudit->new ( + data => \@msglines, + add_From_line => 0 + ); + + my $assassin = $self->{assassind}->{assassin}; + # Check spamminess and rewrite mail if high spam factor or option -a (tag All) + my $status = $assassin->check($mail); + if ( $status->is_spam || $self->{assassind}->{tagall} ) { + $status->rewrite_mail; + } + + # Build the message to send back + $msg_resp = join '',$mail->header,"\n",@{$mail->body}; + + # Log what we did, FWIW + my $was_it_spam; + if($status->is_spam) { $was_it_spam = 'identified spam'; } else { $was_it_spam = 'clean message'; } + my $msg_score = int($status->get_hits); + my $msg_threshold = int($status->get_required_hits); + #$current_user ||= '(unknown)'; + $self->log(2, "$was_it_spam ($msg_score/$msg_threshold) in ". sprintf("%3d", time - $start) ." seconds."); + + $status->finish(); + + } else { + + $msg_resp = $message; + $self->log(2, "Scanning skipped due to size (". length($message) .")"); + + } + +# my $message = [split(/\r?\n/, $client->{MSG})]; +# my $auditor = Mail::Audit->new(data => $message); +# my $assassin = $self->{assassind}->{assassin}; +# my $status = $assassin->check($auditor); + +# my $score = $status->get_hits; +# my $spam_color = 'red'; +# foreach my $color (qw(green blue yellow orange)) { +# if ($score <= $self->{assassind}->{$color}) { +# $spam_color = $color; +# last; +# } +# } + +# $auditor->put_header('X-Spam-Color', $spam_color); +# my $is_spam =$status->is_spam? 'Yes' : 'No'; +# $auditor->put_header('X-Spam-Status', +# sprintf("%s, hits=%.2f required=%.2f tests=%s", +# $is_spam, +# $status->get_hits, +# $status->get_required_hits, +# $status->get_names_of_tests_hit)); + +# if ($spam_color ne 'green') { +# foreach (split(/\n/, $status->get_report)) { +# $auditor->put_header('X-Spam-Report', $_); +# } +# } + +# $status->finish; + + my $smtp = Net::SMTP->new($self->{assassind}->{relayhost}, Hello => $self->{assassind}->{heloname}); + unless (defined $smtp) { + $self->log(1, "Connection to SMTP server failed"); + $self->dead_letter($client); + return; + } + + try { + $smtp->mail($client->{FROM}); + throw Error -text => sprintf("Relay failed; server said %s %s", + $smtp->code, $smtp->message) unless $smtp->ok; + + foreach (@{$client->{TO}}) { + $smtp->recipient($_); + throw Error -text => sprintf("Relay failed; server said %s %s", + $smtp->code, $smtp->message) unless $smtp->ok; + } + + $smtp->data($msg_resp); +# $smtp->data; + throw Error -text => sprintf("Relay failed; server said %s %s", + $smtp->code, $smtp->message) unless $smtp->ok; + +# $smtp->datasend($auditor->header); +# $smtp->datasend("\n"); +# foreach (@{$auditor->body}) { +# $smtp->datasend($_ . "\r\n"); +# } +# $smtp->dataend; +# throw Error -text => sprintf("Relay failed; server said %s %s", +# $smtp->code, $smtp->message) unless $smtp->ok; + + $smtp->quit; + throw Error -text => sprintf("Relay failed; server said %s %s", + $smtp->code, $smtp->message) unless $smtp->ok; + $self->log(4, "Message relayed successfully."); + } catch Error with { + my $e = shift; + $self->dead_letter($client, $e->stringify); + }; +} + +sub process_request { + my $self = shift; + my $client = Net::SMTP::Server::Client->new($self->{server}->{client}); + if ($client->process) { + $self->log(4, "Received message"); + $SIG{TERM} = sub { + $self->dead_letter($client, "Process interrupted by SIGTERM"); + }; + $self->relay_message($client); + $SIG{TERM} = sub { exit 0; }; + } else { + $self->log(1, "An error occurred while receiving message"); + } + $self->{assassind}->{instance} = 1 unless defined $self->{assassind}->{instance}; + exit 0 if $self->{assassind}->{instance} > $self->{assassind}->{maxrequests}++; +} + +my $relayhost = 'localhost'; +my $host = 'localhost'; +my $port = 2025; +my $maxrequests = 20; +my $dead_letters = '/var/tmp'; +my $pidfile = '/var/run/assassind.pid'; +my $user = 'mail'; +my $group = 'mail'; +my $tagall = 0; +my $maxsize = 256; +my $heloname = 'spamfilter.localdomain'; +# my $auto_whitelist = 0; +# my $stop_at_threshold = 0; + +my %options = (port => \$port, + host => \$host, + relayhost => \$relayhost, + 'dead-letters' => \$dead_letters, + pid => \$pidfile, + user => \$user, + group => \$group, + maxrequests => \$maxrequests, + tagall => \$tagall, + maxsize => \$maxsize, + heloname => \$heloname + ); + +usage(1) unless GetOptions(\%options, + 'port=i', + 'host=s', + 'relayhost=s', + 'maxrequests=i', + 'dead-letters=s', + 'user=s', + 'group=s', + 'pid=s', + 'tagall=i', + 'maxsize=i', + 'heloname=s', + 'auto-whitelist', + 'stop-at-threshold', + 'debug', + 'help'); +usage(0) if $options{help}; + +my $assassin = Mail::SpamAssassin->new({ + 'dont_copy_prefs' => 1, + 'stop_at_threshold' => $options{'stop_at_threshold'} || 0, + 'debug' => $options{'debug'} || 0 }); + +$options{'auto-whitelist'} and eval { + require Mail::SpamAssassin::DBBasedAddrList; + + # create a factory for the persistent address list + my $addrlistfactory = Mail::SpamAssassin::DBBasedAddrList->new(); + $assassin->set_persistent_address_list_factory ($addrlistfactory); +}; + +$assassin->compile_now(); +$/ = "\n"; # argh, Razor resets this! Bad Razor! + +my $server = bless { + server => {host => $host, + port => [ $port ], + log_file => 'Sys::Syslog', + syslog_ident => 'spampd', + syslog_facility => 'mail', + background => 1, + pid_file => $pidfile, + user => $user, + group => $group, + }, + assassind => {maxrequests => $maxrequests, + relayhost => $relayhost, + dead_letters => $dead_letters, + tagall => $tagall, + maxsize => $maxsize, + assassin => $assassin, + heloname => $heloname, + }, + }, 'Assassind'; +$server->run; + +sub usage { + print < +[B<--port=n>] +[B<--host=host>] +[B<--relayhost=hostname[:port]>] +[B<--user=username>] +[B<--group=groupname>] +[B<--maxrequests=n>] +[B<--dead-letters=/path>] +[B<--pid=filename>] +[B<--tagall=n>] +[B<--maxsize=n>] +[B<--auto-whitelist>] +[B<--stop-at-threshold>] +[B<--debug>] +[B<--heloname=hostname>] + +B B<--help> + +=head1 DESCRIPTION + +I is a relaying SMTP proxy that filters spam using +SpamAssassin. The proxy is designed to be robust in the face of +exceptional errors, and will (hopefully) never lose a message. + +I is meant to be used as a system-wide message processor, so +the proxy does not make any changes to existing message contents or +headers; instead choosing just to add three headers of its own, which +end users can use to make decisions about filtering (or not filtering) +their spam. + +The most important header that I adds is the B +header. This header will have one of five values: I, I, +I, I and I. Green messages are very unlikely to be +spam, while red messages are almost guaranteed to be spam. You can use +this header as the basis for your own message filtering rules, using any +common message filtering system (procmail, sieve, etc.). + +I also adds a B filter. This header is the +same as the header generated by the standard SpamAssassin message +processor, and contains the message's SpamAssassin score and other +information. + +Finally, I adds one or more B headers, which +contain a plain-text report of the rules that SpamAssassin used to +assign the message its score. + +I logs all aspects of its operation to syslog(8), using the +mail syslog facility. + +=head1 OPERATION + +I is meant to operate as a mail relay that sits between the +Internet and your internal mail system. The three most common +configurations include + +=over 5 + +=item Running between firewall and internal mail server + +The firewall would be configured to forward all of its mail to the port +that I listens on, and I would relay its messages +to port 25 of your internal server. I could either run on its +own host (and listen on any port) or it could run on the mail server +(and listen on any port except port 25). This is I default +mode of operation. + +=item Running on the firewall with an internal mail server + +I would accept messages on port 25 and forward them to the +mail server that is also listening on port 25. Note that I +does not do anything other than check for spam, so it is not suitable as +an anti-relay system. If your current mail system is configured +correctly for anti-relaying, it should continue to work correctly in +this configuration, but you may want to verify this using one of the +standard open-relay blackhole testing systems. + +=item Running on the mail server, which is not behind a firewall + +In this configuration I would listen on port 25, while your +mail server would be configured to listen on some other port. + +=back + +OPTIONS + +=over 5 + +=item B<--port=n> + +Specifies what port I listens on. By default, it listens on +port 2025. + +=item B<--relayhost=hostname[:port]> + +Specifies the hostname where I will relay all +messages. Defaults to I. If the port is not provided, that +defaults to 25. + +=item B<--user=username> +=item B<--group=groupname> + +Specifies the user and group that the proxy will run as. Default is +I/I. + +=item B<--maxrequests=n> + +I works by forking child servers to handle each message. The +B parameter specifies how many requests will be handled +before the child exits. Since a child never gives back memory, a large +message can cause it to become quite bloated; the only way to reclaim +the memory is for the child to exit. The default is 20. + +=item B<--dead-letters=/path> + +Specifies the directory where I will store any message that +it fails to deliver. The default is F. You should periodically +examine this directory to see if there are any messages that couldn't be +delivered. + +B This path should not be on the same partition as your mail +server's message spool, because if your mail server rejects a message +because of a full disk, I will not be able to save the +message, and it will be lost. + +=item B<--pid=filename> + +Specifies a filename where I will write its process ID so +that it is easy to kill it later. The directory that will contain this +file must be writable by the I user. The default is +F. + +=item B<--green=n> +=item B<--blue=n> +=item B<--yellow=n> +=item B<--orange=n> + +Specifies the spam score thresholds for each color. The defaults are 5, +6, 10 and 20. Anything over 20 will have a color of red. + +=back + +=head1 EXAMPLES + +=over 5 + +=item Running between firewall and internal mail server + +This is I's default configuration, where it listens on port +2025 on the same host as the mail server. + + assassind + +=item Running on the firewall with an internal mail server + + assassind --port=25 --relayhost=internal.serv.er + +=item Running on the mail server, which is not behind a firewall + +This scenario assumes that the real mail server is running on port 2025 +of the same host. + + assassind --port=25 --relayhost=localhost:2025 + +=back + +=head1 AUTHOR + +Dave Carrigan, + +This program is Copyright © 2002, Dave Carrigan. All rights +reserved. This program is free software; you can redistribute it and/or +modify it under the same terms as Perl. + +This program is distributed "as is", without warranty of any kind, +either expressed or implied, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose. The +entire risk as to the quality and performance of the program is with +you. Should the program prove defective, you assume the cost of all +necessary servicing, repair or correction. + + +=head1 SEE ALSO + +perl(1), Spam::Assassin(3), http://www.rudedog.org/assassind/ + +=head1 BUGS + +Due to the nature of Perl's SMTP::Server module, a SMTP message is +stored completely in memory. However, as soon as the module receives its +entire message data from the SMTP client, it returns a 250, signifying +to the client that the message has been delivered. However, this means +that there is a period of time where the message is vulnerable to being +lost if the I process is killed before it has relayed or +saved the message. Caveat Emptor! -- cgit v1.2.3-24-g4f1b