mirror of
https://github.com/MailScanner/v5.git
synced 2024-09-20 15:26:08 +08:00
Merge branch 'master' of https://github.com/MailScanner/v5
This commit is contained in:
commit
87958bf2d8
|
@ -1,9 +1,9 @@
|
|||
#!/usr/bin/perl -U -I /usr/share/MailScanner/perl
|
||||
|
||||
# (c) 2019 MailScanner Project <https://www.mailscanner.info>
|
||||
# Version 1.5
|
||||
# (c) 2019-2020 MailScanner Project <https://www.mailscanner.info>
|
||||
# Version 1.6
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
@ -107,474 +107,502 @@ sub smtp_id {
|
|||
|
||||
sub connect_callback
|
||||
{
|
||||
my $ctx = shift;
|
||||
my $hostname = shift;
|
||||
my $sockaddr_in = shift;
|
||||
my ($port, $iaddr);
|
||||
my $ip;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
my $ctx = shift;
|
||||
my $hostname = shift;
|
||||
my $sockaddr_in = shift;
|
||||
my ($port, $iaddr);
|
||||
my $ip;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
|
||||
my $message = $hostname;
|
||||
|
||||
if (defined $sockaddr_in)
|
||||
{
|
||||
my $family = sockaddr_family($sockaddr_in);
|
||||
if ($family eq AF_INET) {
|
||||
($port, $iaddr) = sockaddr_in($sockaddr_in);
|
||||
$ip = inet_ntop(AF_INET, $iaddr);
|
||||
$message .= ' [' . $ip . ']';
|
||||
} elsif ($family eq AF_INET6) {
|
||||
($port, $iaddr) = sockaddr_in6($sockaddr_in);
|
||||
$ip = inet_ntop(AF_INET6, $iaddr);
|
||||
$message .= ' [' . $ip . ']';
|
||||
} else {
|
||||
$ip = '';
|
||||
}
|
||||
my $message = $hostname;
|
||||
|
||||
if (defined $sockaddr_in)
|
||||
{
|
||||
my $family = sockaddr_family($sockaddr_in);
|
||||
if ($family eq AF_INET) {
|
||||
($port, $iaddr) = sockaddr_in($sockaddr_in);
|
||||
$ip = inet_ntop(AF_INET, $iaddr);
|
||||
$message .= ' [' . $ip . ']';
|
||||
} elsif ($family eq AF_INET6) {
|
||||
($port, $iaddr) = sockaddr_in6($sockaddr_in);
|
||||
$ip = inet_ntop(AF_INET6, $iaddr);
|
||||
$message .= ' [' . $ip . ']';
|
||||
} else {
|
||||
$ip = '';
|
||||
}
|
||||
|
||||
# Localhost relaying email? Accept the message for delivery if enabled
|
||||
my $loopback = MailScanner::Config::Value('milterignoreloopback');
|
||||
if ($loopback == 1 && ($ip =~ /^127/ || $ip =~ /^::1$/)) {
|
||||
MailScanner::Log::DebugLog("Localhost relay detected");
|
||||
return Sendmail::PMilter::SMFIS_ACCEPT;
|
||||
}
|
||||
}
|
||||
|
||||
# Localhost relaying email? Accept the message for delivery if enabled
|
||||
my $loopback = MailScanner::Config::Value('milterignoreloopback');
|
||||
if ($loopback == 1 && ($ip =~ /^127/ || $ip =~ /^::1$/)) {
|
||||
MailScanner::Log::DebugLog("Localhost relay detected");
|
||||
return Sendmail::PMilter::SMFIS_ACCEPT;
|
||||
}
|
||||
|
||||
|
||||
MailScanner::Log::DebugLog("connect_callback: ip = $ip");
|
||||
MailScanner::Log::DebugLog("connect_callback: ip = $ip");
|
||||
|
||||
${$message_ref} = $message;
|
||||
${$message_ref} = $message;
|
||||
|
||||
$ctx->setpriv($message_ref);
|
||||
$ctx->setpriv($message_ref);
|
||||
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
}
|
||||
|
||||
sub helo_callback
|
||||
{
|
||||
my $ctx = shift;
|
||||
my $helohost = shift;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
my $message = "Received: from $helohost";
|
||||
# Watch for the second callback
|
||||
if ( $message ne substr(${$message_ref}, 0, length($message)) ) {
|
||||
${$message_ref} = $message . ' (' . ${$message_ref} . ')' ."\n";
|
||||
MailScanner::Log::DebugLog("helo_callback: $message");
|
||||
}
|
||||
my $ctx = shift;
|
||||
my $helohost = shift;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
my $message = "Received: from $helohost";
|
||||
# Watch for the second callback
|
||||
if ( $message ne substr(${$message_ref}, 0, length($message)) ) {
|
||||
${$message_ref} = $message . ' (' . ${$message_ref} . ')' ."\n";
|
||||
MailScanner::Log::DebugLog("helo_callback: $message");
|
||||
}
|
||||
|
||||
$ctx->setpriv($message_ref);
|
||||
$ctx->setpriv($message_ref);
|
||||
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
}
|
||||
|
||||
sub envrcpt_callback
|
||||
{
|
||||
my $ctx = shift;
|
||||
my @args = @_;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
my $symbols = $ctx->{symbols};
|
||||
my $mailfrom;
|
||||
my $ctx = shift;
|
||||
my @args = @_;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
my $symbols = $ctx->{symbols};
|
||||
my $mailfrom;
|
||||
|
||||
# Watch for second callback
|
||||
if ( ${$message_ref} !~ /MailScanner Milter/ ) {
|
||||
# Watch for second callback
|
||||
if ( ${$message_ref} !~ /MailScanner Milter/ ) {
|
||||
|
||||
# Is this a subsequent message? Grab previous message id to reconstruct header
|
||||
if ( ${$message_ref} =~ /^[0-9A-F]{7,20}|[0-9B-DF-HJ-NP-TV-Zb-df-hj-np-tv-z]{8,20}$/ ) {
|
||||
# Read envelope
|
||||
my $queuehandle = new FileHandle;
|
||||
my $incoming = MailScanner::Config::Value('inqueuedir');
|
||||
MailScanner::Log::DebugLog("envrcpt_callback: incoming = " . @{$incoming}[0]);
|
||||
if ($incoming eq '') {
|
||||
MailScanner::Log::WarnLog("Unable to determine incoming queue!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
my $file = @{$incoming}[0] . '/temp-' . ${$message_ref};
|
||||
my $file2 = @{$incoming}[0] . '/' . ${$message_ref};
|
||||
# Is this a subsequent message? Grab previous message id to reconstruct header
|
||||
if ( ${$message_ref} =~ /^[0-9A-F]{7,20}|[0-9B-DF-HJ-NP-TV-Zb-df-hj-np-tv-z]{8,20}$/ ) {
|
||||
# Read envelope
|
||||
my $queuehandle = new FileHandle;
|
||||
my $incoming = MailScanner::Config::Value('inqueuedir');
|
||||
MailScanner::Log::DebugLog("envrcpt_callback: incoming = " . @{$incoming}[0]);
|
||||
if ($incoming eq '') {
|
||||
MailScanner::Log::WarnLog("Unable to determine incoming queue!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
my $file = @{$incoming}[0] . '/temp-' . ${$message_ref};
|
||||
my $file2 = @{$incoming}[0] . '/' . ${$message_ref};
|
||||
|
||||
MailScanner::Log::DebugLog("envrcpt_callback: file = $file");
|
||||
MailScanner::Log::DebugLog("envrcpt_callback: file = $file");
|
||||
|
||||
my $ret;
|
||||
$ret = MailScanner::Lock::openlock($queuehandle,'+<' . $file, 'w');
|
||||
if ($ret != 1) {
|
||||
MailScanner::Log::WarnLog("Unable to to open temp queue file for reading!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
|
||||
my $data;
|
||||
my $pos;
|
||||
# Locate predata header
|
||||
while($data = readline $queuehandle) {
|
||||
last if $data !~ /^(O<|S<)/;
|
||||
}
|
||||
|
||||
${$message_ref} = $data;
|
||||
MailScanner::Lock::unlockclose($queuehandle);
|
||||
|
||||
# Done with previous message, make ready for mailscanner
|
||||
move($file, $file2);
|
||||
my $ret;
|
||||
$ret = MailScanner::Lock::openlock($queuehandle,'+<' . $file, 'w');
|
||||
if ($ret != 1) {
|
||||
MailScanner::Log::WarnLog("Unable to to open temp queue file for reading!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
# Capture the Mail From address for further processing
|
||||
if (defined($symbols->{'M'}) && defined($symbols->{'M'}->{'{mail_addr}'})) {
|
||||
$mailfrom=$symbols->{'M'}->{'{mail_addr}'};
|
||||
} else {
|
||||
# Null Sender
|
||||
# RFC 1123, 821
|
||||
$mailfrom='';
|
||||
my $data;
|
||||
my $pos;
|
||||
# Locate predata header
|
||||
while($data = readline $queuehandle) {
|
||||
last if $data !~ /^(O<|S<|E<)/;
|
||||
}
|
||||
|
||||
${$message_ref} = 'S<' . $mailfrom . ">\n" . ${$message_ref};
|
||||
${$message_ref} = $data;
|
||||
MailScanner::Lock::unlockclose($queuehandle);
|
||||
|
||||
if (defined($symbols->{'H'}) && defined($symbols->{'H'}->{'{tls_version}'}) && defined($symbols->{'H'}->{'{cipher}'}) && defined($symbols->{'H'}->{'{cipher_bits}'})) {
|
||||
${$message_ref} .= ' (using ' . $symbols->{'H'}->{'{tls_version}'} . ' with cipher ' . $symbols->{'H'}->{'{cipher}'} . ' (' . $symbols->{'H'}->{'{cipher_bits}'} . '/' . $symbols->{'H'}->{'{cipher_bits}'} . ' bits))' . "\n";
|
||||
MailScanner::Log::DebugLog("envrcpt_callback: ssl/tls detected");
|
||||
}
|
||||
if (!defined($symbols->{'H'}->{'{cert_subject}'})) {
|
||||
${$message_ref} .= ' (no client certificate requested)';
|
||||
MailScanner::Log::DebugLog("envrcpt_callback: no client certificate requested");
|
||||
}
|
||||
if (defined($symbols->{'M'}->{'{auth_type}'}) && defined($symbols->{'M'}->{'{auth_authen}'})) {
|
||||
${$message_ref} .= ' (Authenticated sender: ' . $symbols->{'M'}->{'{auth_authen}'} . ")\n";
|
||||
MailScanner::Log::DebugLog("envrcpt_callback: Authenticated sender: " . $symbols->{'M'}->{'{auth_authen}'});
|
||||
} else {
|
||||
${$message_ref} .= "\n";
|
||||
}
|
||||
${$message_ref} .= ' by ' . hostname . ' (MailScanner Milter) with SMTP id ';
|
||||
# Done with previous message, make ready for mailscanner
|
||||
move($file, $file2);
|
||||
}
|
||||
|
||||
# Build original recipient milter header
|
||||
${$message_ref} = 'O' . $args[0] . "\n" . ${$message_ref};
|
||||
|
||||
$ctx->setpriv($message_ref);
|
||||
# Capture the Mail From address for further processing
|
||||
if (defined($symbols->{'M'}) && defined($symbols->{'M'}->{'{mail_addr}'})) {
|
||||
$mailfrom=$symbols->{'M'}->{'{mail_addr}'};
|
||||
} else {
|
||||
# Null Sender
|
||||
# RFC 1123, 821
|
||||
$mailfrom='';
|
||||
}
|
||||
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
${$message_ref} = 'S<' . $mailfrom . ">\n" . ${$message_ref};
|
||||
|
||||
if (defined($symbols->{'H'}) && defined($symbols->{'H'}->{'{tls_version}'}) && defined($symbols->{'H'}->{'{cipher}'}) && defined($symbols->{'H'}->{'{cipher_bits}'})) {
|
||||
${$message_ref} .= "\t(using " . $symbols->{'H'}->{'{tls_version}'} . ' with cipher ' . $symbols->{'H'}->{'{cipher}'} . ' (' . $symbols->{'H'}->{'{cipher_bits}'} . '/' . $symbols->{'H'}->{'{cipher_bits}'} . ' bits))' . "\n";
|
||||
MailScanner::Log::DebugLog("envrcpt_callback: ssl/tls detected");
|
||||
}
|
||||
if (!defined($symbols->{'H'}->{'{cert_subject}'})) {
|
||||
${$message_ref} .= "\t(no client certificate requested)";
|
||||
MailScanner::Log::DebugLog("envrcpt_callback: no client certificate requested");
|
||||
}
|
||||
if (defined($symbols->{'M'}->{'{auth_type}'}) && defined($symbols->{'M'}->{'{auth_authen}'})) {
|
||||
${$message_ref} .= "\t(Authenticated sender: " . $symbols->{'M'}->{'{auth_authen}'} . ")\n";
|
||||
MailScanner::Log::DebugLog("envrcpt_callback: Authenticated sender: " . $symbols->{'M'}->{'{auth_authen}'});
|
||||
} else {
|
||||
${$message_ref} .= "\n";
|
||||
}
|
||||
${$message_ref} .= "\tby " . hostname . ' (MailScanner Milter) with SMTP id ';
|
||||
}
|
||||
|
||||
my $rcptto = $args[0];
|
||||
my $esmtpnotify = '';
|
||||
# Capture the ESMTP options for pass through MailScanner engine
|
||||
# RFC 3461
|
||||
foreach (@args)
|
||||
{
|
||||
|
||||
if ($_ =~ /^ORCPT=rfc822;/)
|
||||
{
|
||||
MailScanner::Log::DebugLog("envrcpt_callback: ORCPT argument found: " . $_);
|
||||
s/^ORCPT=rfc822;//;
|
||||
if ($rcptto !~ /$_/) {
|
||||
$rcptto = '<' . $_ . '>';
|
||||
}
|
||||
}
|
||||
|
||||
if ($_ =~ /^NOTIFY=/)
|
||||
{
|
||||
MailScanner::Log::DebugLog("envrcpt_callback: NOTIFY argument found: " . $_);
|
||||
$esmtpnotify = $_;
|
||||
}
|
||||
}
|
||||
|
||||
# Build original recipient milter header
|
||||
${$message_ref} = 'O' . $rcptto . "\n" . ${$message_ref};
|
||||
|
||||
# Add options milter header
|
||||
if ($esmtpnotify ne '')
|
||||
{
|
||||
${$message_ref} = 'E<' . $esmtpnotify . ">\n" . ${$message_ref};
|
||||
}
|
||||
|
||||
$ctx->setpriv($message_ref);
|
||||
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
}
|
||||
|
||||
sub header_callback
|
||||
{
|
||||
my $ctx = shift;
|
||||
my $headerf = shift;
|
||||
my $headerv = shift;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
my $symbols = $ctx->{symbols};
|
||||
my $id;
|
||||
my $datestring = strftime "%a, %e %b %Y %T %z (%Z)", localtime;
|
||||
my $ctx = shift;
|
||||
my $headerf = shift;
|
||||
my $headerv = shift;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
my $symbols = $ctx->{symbols};
|
||||
my $id;
|
||||
my $datestring = strftime "%a, %e %b %Y %T %z (%Z)", localtime;
|
||||
|
||||
# Postfix queue id revealed during this phase, capture it if defined
|
||||
# and populate the rest of the Received: header
|
||||
if (${$message_ref} =~ /SMTP\sid\s$/ ) {
|
||||
if (defined($symbols->{'L'}) && defined($symbols->{'L'}->{'i'}) ) {
|
||||
$id = $symbols->{'L'}->{'i'};
|
||||
if ( $id ne '' ) {
|
||||
MailScanner::Log::DebugLog("Postfix ID captured: $id");
|
||||
} else {
|
||||
$id = smtp_id;
|
||||
MailScanner::Log::DebugLog("Postfix id empty, generated one instead: $id");
|
||||
}
|
||||
# Postfix queue id revealed during this phase, capture it if defined
|
||||
# and populate the rest of the Received: header
|
||||
if (${$message_ref} =~ /SMTP\sid\s$/ ) {
|
||||
if (defined($symbols->{'L'}) && defined($symbols->{'L'}->{'i'}) ) {
|
||||
$id = $symbols->{'L'}->{'i'};
|
||||
if ( $id ne '' ) {
|
||||
MailScanner::Log::DebugLog("Postfix ID captured: $id");
|
||||
} else {
|
||||
$id = smtp_id;
|
||||
MailScanner::Log::DebugLog("Unable to capture real postfix id, generated one instead: $id");
|
||||
MailScanner::Log::DebugLog("Postfix id empty, generated one instead: $id");
|
||||
}
|
||||
|
||||
${$message_ref} .= $id;
|
||||
|
||||
# Check for only one original recipient and add 'for <>' if so.
|
||||
if (${$message_ref} =~ /^O(<.*>)\n(?!O)/ ) {
|
||||
${$message_ref} .= "\n for " . $1 . "; ";
|
||||
} else {
|
||||
${$message_ref} .= ";\n ";
|
||||
}
|
||||
${$message_ref} .= "$datestring\n";
|
||||
MailScanner::Log::DebugLog("header_callback: datestring = $datestring");
|
||||
} else {
|
||||
$id = smtp_id;
|
||||
MailScanner::Log::DebugLog("Unable to capture real postfix id, generated one instead: $id");
|
||||
}
|
||||
|
||||
${$message_ref} .= $id;
|
||||
|
||||
${$message_ref} .= $headerf . ': ' . $headerv . "\n";
|
||||
$ctx->setpriv($message_ref);
|
||||
# Check for only one original recipient and add 'for <>' if so.
|
||||
if (${$message_ref} =~ /^O(<.*>)\n(?!O)/ ) {
|
||||
${$message_ref} .= "\n\tfor " . $1 . "; ";
|
||||
} else {
|
||||
${$message_ref} .= ";\n\t";
|
||||
}
|
||||
${$message_ref} .= "$datestring\n";
|
||||
MailScanner::Log::DebugLog("header_callback: datestring = $datestring");
|
||||
}
|
||||
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
|
||||
${$message_ref} .= $headerf . ': ' . $headerv . "\n";
|
||||
$ctx->setpriv($message_ref);
|
||||
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
}
|
||||
|
||||
sub eoh_callback
|
||||
{
|
||||
my $ctx = shift;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
my $id;
|
||||
my $line;
|
||||
my $buffer;
|
||||
my $ip;
|
||||
my @to;
|
||||
my $from;
|
||||
my $ctx = shift;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
my $id;
|
||||
my $line;
|
||||
my $buffer;
|
||||
my $ip;
|
||||
my @to;
|
||||
my $from;
|
||||
|
||||
# Are we in scanner mode?
|
||||
my $scannermode = MailScanner::Config::Value('milterscanner');
|
||||
# Are we in scanner mode?
|
||||
my $scannermode = MailScanner::Config::Value('milterscanner');
|
||||
|
||||
MailScanner::Log::DebugLog("eoh_callback: scannermode = $scannermode");
|
||||
MailScanner::Log::DebugLog("eoh_callback: scannermode = $scannermode");
|
||||
|
||||
while (${$message_ref} =~ /([^\n]+)\n?/g) {
|
||||
my $line = $1;
|
||||
$buffer .= $1 . "\n";
|
||||
if ($line =~ /^Received: / ) {
|
||||
$ip = '127.0.0.1';
|
||||
if ($line =~ /^Received: .+?\(.*?\[(?:IPv6:)?([0-9a-f.:]+)\]/i) {
|
||||
$ip = $1;
|
||||
}
|
||||
MailScanner::Log::DebugLog("eoh_callback: ip = $ip");
|
||||
} elsif ( $line =~ m/^.*SMTP id / ) {
|
||||
# We may have added a trailing ; so remove it if so.
|
||||
$line =~ s/^.*SMTP id ([^;]*)(?:;?)/$1/;
|
||||
$id = $line;
|
||||
MailScanner::Log::DebugLog("eoh_callback: id = $id");
|
||||
last;
|
||||
} elsif ( $line =~ /^O/ ) {
|
||||
$line =~ s/^O//;
|
||||
$line =~ s/^\<//;
|
||||
$line =~ s/\>.*$//;
|
||||
push @to, $line;
|
||||
MailScanner::Log::DebugLog("eoh_callback: to = $line");
|
||||
} elsif ( $line =~ /^F/ ) {
|
||||
$line =~ s/^F//;
|
||||
$line =~ s/^\<//;
|
||||
$line =~ s/\>.*$//;
|
||||
$from = $line;
|
||||
MailScanner::Log::DebugLog("eoh_callback: from = $line");
|
||||
while (${$message_ref} =~ /([^\n]+)\n?/g) {
|
||||
my $line = $1;
|
||||
$buffer .= $1 . "\n";
|
||||
if ($line =~ /^Received: / ) {
|
||||
$ip = '127.0.0.1';
|
||||
if ($line =~ /^Received: .+?\(.*?\[(?:IPv6:)?([0-9a-f.:]+)\]/i) {
|
||||
$ip = $1;
|
||||
}
|
||||
MailScanner::Log::DebugLog("eoh_callback: ip = $ip");
|
||||
} elsif ( $line =~ m/^.*SMTP id / ) {
|
||||
# We may have added a trailing ; so remove it if so.
|
||||
$line =~ s/^.*SMTP id ([^;]*)(?:;?)/$1/;
|
||||
$id = $line;
|
||||
MailScanner::Log::DebugLog("eoh_callback: id = $id");
|
||||
last;
|
||||
} elsif ( $line =~ /^O/ ) {
|
||||
$line =~ s/^O//;
|
||||
$line =~ s/^\<//;
|
||||
$line =~ s/\>.*$//;
|
||||
push @to, $line;
|
||||
MailScanner::Log::DebugLog("eoh_callback: to = $line");
|
||||
} elsif ( $line =~ /^F/ ) {
|
||||
$line =~ s/^F//;
|
||||
$line =~ s/^\<//;
|
||||
$line =~ s/\>.*$//;
|
||||
$from = $line;
|
||||
MailScanner::Log::DebugLog("eoh_callback: from = $line");
|
||||
}
|
||||
}
|
||||
|
||||
if ( $id eq '' ) {
|
||||
# Missing a queue id, header_callback possibly skipped, reject
|
||||
MailScanner::Log::WarnLog("Postfix queue id is missing");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
|
||||
# Write header to file
|
||||
my $queuehandle = new FileHandle;
|
||||
my $incoming = MailScanner::Config::Value('inqueuedir');
|
||||
MailScanner::Log::DebugLog("eoh_callback: incoming = " . @{$incoming}[0]);
|
||||
if ($incoming eq '') {
|
||||
MailScanner::Log::WarnLog("Unable to determine incoming queue!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
my $file = @{$incoming}[0] . '/temp-' . $id;
|
||||
|
||||
MailScanner::Log::DebugLog("eoh_callback: file = $file");
|
||||
|
||||
my $ret;
|
||||
$ret = MailScanner::Lock::openlock($queuehandle,'>>' . $file, 'w');
|
||||
if ($ret != 1) {
|
||||
MailScanner::Log::WarnLog("Unable to to open queue temp file for writing!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
|
||||
$queuehandle->print(${$message_ref});
|
||||
|
||||
# Signal end of header
|
||||
$queuehandle->print("\n");
|
||||
|
||||
$queuehandle->flush();
|
||||
MailScanner::Lock::unlockclose($queuehandle);
|
||||
|
||||
# Determine what to do next
|
||||
if ($scannermode == 1) {
|
||||
MailScanner::Log::DebugLog("eoh_callback: scanner mode enabled");
|
||||
# Blacklist test
|
||||
# Build a message object to test against
|
||||
my $message = {};
|
||||
if ($from ne '<>') {
|
||||
$message->{from} = $from;
|
||||
} else {
|
||||
$message->{from} = '';
|
||||
}
|
||||
@{$message->{to}} = @to;
|
||||
$message->{clientip} = $ip;
|
||||
my $user;
|
||||
my $domain;
|
||||
($user, $domain) = MailScanner::Message::address2userdomain($message->{from});
|
||||
$message->{fromuser} = $user;
|
||||
$message->{fromdomain} = $domain;
|
||||
|
||||
my $touser;
|
||||
my $todomain;
|
||||
my $count;
|
||||
|
||||
foreach my $msgto (@{$message->{to}}) {
|
||||
($touser, $todomain) = MailScanner::Message::address2userdomain($msgto);
|
||||
push @{$message->{touser}}, $touser;
|
||||
push @{$message->{todomain}}, $todomain;
|
||||
$count++;
|
||||
}
|
||||
|
||||
my $test;
|
||||
# Check whitelist first
|
||||
$test = MailScanner::Config::Value('spamwhitelist', $message);
|
||||
|
||||
my $maxrecips = MailScanner::Config::Value('whitelistmaxrecips');
|
||||
$maxrecips = 999999 unless $maxrecips;
|
||||
|
||||
# Not in whitelist or a large number of recipients, then check blacklist
|
||||
if ($test == 0 || $count > $maxrecips) {
|
||||
MailScanner::Log::DebugLog("eom_callback: no whitelist match, proceeding to blacklist test");
|
||||
# test
|
||||
$test = MailScanner::Config::Value('spamblacklist', $message);
|
||||
|
||||
# Blacklisted, fire a reject
|
||||
if ($test == 1) {
|
||||
MailScanner::Log::DebugLog("eom_callback: blacklist match, firing reject");
|
||||
# Set reject code
|
||||
$ctx->setreply('554', '5.7.1', 'Message Blacklisted');
|
||||
|
||||
# Delete temp file
|
||||
unlink $file;
|
||||
return Sendmail::PMilter::SMFIS_REJECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $id eq '' ) {
|
||||
# Missing a queue id, header_callback possibly skipped, reject
|
||||
MailScanner::Log::WarnLog("Postfix queue id is missing");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
MailScanner::Log::DebugLog("eoh_callback: End of Header detected");
|
||||
|
||||
# Write header to file
|
||||
my $queuehandle = new FileHandle;
|
||||
my $incoming = MailScanner::Config::Value('inqueuedir');
|
||||
MailScanner::Log::DebugLog("eoh_callback: incoming = " . @{$incoming}[0]);
|
||||
if ($incoming eq '') {
|
||||
MailScanner::Log::WarnLog("Unable to determine incoming queue!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
my $file = @{$incoming}[0] . '/temp-' . $id;
|
||||
# Start storing just the id
|
||||
${$message_ref} = $id;
|
||||
|
||||
MailScanner::Log::DebugLog("eoh_callback: file = $file");
|
||||
$ctx->setpriv($message_ref);
|
||||
|
||||
my $ret;
|
||||
$ret = MailScanner::Lock::openlock($queuehandle,'>>' . $file, 'w');
|
||||
if ($ret != 1) {
|
||||
MailScanner::Log::WarnLog("Unable to to open queue temp file for writing!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
|
||||
$queuehandle->print(${$message_ref});
|
||||
|
||||
# Signal end of header
|
||||
$queuehandle->print("\n");
|
||||
|
||||
$queuehandle->flush();
|
||||
MailScanner::Lock::unlockclose($queuehandle);
|
||||
|
||||
# Determine what to do next
|
||||
if ($scannermode == 1) {
|
||||
MailScanner::Log::DebugLog("eoh_callback: scanner mode enabled");
|
||||
# Blacklist test
|
||||
# Build a message object to test against
|
||||
my $message = {};
|
||||
if ($from ne '<>') {
|
||||
$message->{from} = $from;
|
||||
} else {
|
||||
$message->{from} = '';
|
||||
}
|
||||
@{$message->{to}} = @to;
|
||||
$message->{clientip} = $ip;
|
||||
my $user;
|
||||
my $domain;
|
||||
($user, $domain) = MailScanner::Message::address2userdomain($message->{from});
|
||||
$message->{fromuser} = $user;
|
||||
$message->{fromdomain} = $domain;
|
||||
|
||||
my $touser;
|
||||
my $todomain;
|
||||
my $count;
|
||||
|
||||
foreach my $msgto (@{$message->{to}}) {
|
||||
($touser, $todomain) = MailScanner::Message::address2userdomain($msgto);
|
||||
push @{$message->{touser}}, $touser;
|
||||
push @{$message->{todomain}}, $todomain;
|
||||
$count++;
|
||||
}
|
||||
|
||||
my $test;
|
||||
# Check whitelist first
|
||||
$test = MailScanner::Config::Value('spamwhitelist', $message);
|
||||
|
||||
my $maxrecips = MailScanner::Config::Value('whitelistmaxrecips');
|
||||
$maxrecips = 999999 unless $maxrecips;
|
||||
|
||||
# Not in whitelist or a large number of recipients, then check blacklist
|
||||
if ($test == 0 || $count > $maxrecips) {
|
||||
MailScanner::Log::DebugLog("eom_callback: no whitelist match, proceeding to blacklist test");
|
||||
# test
|
||||
$test = MailScanner::Config::Value('spamblacklist', $message);
|
||||
|
||||
# Blacklisted, fire a reject
|
||||
if ($test == 1) {
|
||||
MailScanner::Log::DebugLog("eom_callback: blacklist match, firing reject");
|
||||
# Set reject code
|
||||
$ctx->setreply('554', '5.7.1', 'Message Blacklisted');
|
||||
|
||||
# Delete temp file
|
||||
unlink $file;
|
||||
return Sendmail::PMilter::SMFIS_REJECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MailScanner::Log::DebugLog("eoh_callback: End of Header detected");
|
||||
|
||||
# Start storing just the id
|
||||
${$message_ref} = $id;
|
||||
|
||||
$ctx->setpriv($message_ref);
|
||||
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
}
|
||||
|
||||
sub body_callback
|
||||
{
|
||||
my $ctx = shift;
|
||||
my $body_chunk = shift;
|
||||
my $len = shift;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
my $ctx = shift;
|
||||
my $body_chunk = shift;
|
||||
my $len = shift;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
|
||||
my $queuehandle = new FileHandle;
|
||||
my $incoming = MailScanner::Config::Value('inqueuedir');
|
||||
MailScanner::Log::DebugLog("body_callback: incoming = " . @{$incoming}[0]);
|
||||
if ($incoming eq '') {
|
||||
MailScanner::Log::WarnLog("Unable to determine incoming queue!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
my $file = @{$incoming}[0] . '/temp-' . ${$message_ref};
|
||||
my $queuehandle = new FileHandle;
|
||||
my $incoming = MailScanner::Config::Value('inqueuedir');
|
||||
MailScanner::Log::DebugLog("body_callback: incoming = " . @{$incoming}[0]);
|
||||
if ($incoming eq '') {
|
||||
MailScanner::Log::WarnLog("Unable to determine incoming queue!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
my $file = @{$incoming}[0] . '/temp-' . ${$message_ref};
|
||||
|
||||
MailScanner::Log::DebugLog("body_callback: file = $file");
|
||||
MailScanner::Log::DebugLog("body_callback: file = $file");
|
||||
|
||||
my $ret;
|
||||
$ret = MailScanner::Lock::openlock($queuehandle,'>>' . $file, 'w');
|
||||
if ($ret != 1) {
|
||||
MailScanner::Log::WarnLog("Unable to to open queue temp file for writing!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
$queuehandle->print($body_chunk);
|
||||
my $ret;
|
||||
$ret = MailScanner::Lock::openlock($queuehandle,'>>' . $file, 'w');
|
||||
if ($ret != 1) {
|
||||
MailScanner::Log::WarnLog("Unable to to open queue temp file for writing!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
$queuehandle->print($body_chunk);
|
||||
|
||||
$queuehandle->flush();
|
||||
MailScanner::Lock::unlockclose($queuehandle);
|
||||
$queuehandle->flush();
|
||||
MailScanner::Lock::unlockclose($queuehandle);
|
||||
|
||||
$ctx->setpriv($message_ref);
|
||||
$ctx->setpriv($message_ref);
|
||||
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
}
|
||||
|
||||
sub eom_callback
|
||||
{
|
||||
my $ctx = shift;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
my $ctx = shift;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
|
||||
# Store reference for subsequent messages in connection or for connection close
|
||||
$ctx->setpriv($message_ref);
|
||||
|
||||
# Send DISCARD signal to accept message and drop from postfix
|
||||
# for mailscanner processing
|
||||
Sendmail::PMilter::SMFIS_DISCARD;
|
||||
# Store reference for subsequent messages in connection or for connection close
|
||||
$ctx->setpriv($message_ref);
|
||||
|
||||
# Send DISCARD signal to accept message and drop from postfix
|
||||
# for mailscanner processing
|
||||
Sendmail::PMilter::SMFIS_DISCARD;
|
||||
}
|
||||
|
||||
sub close_callback
|
||||
{
|
||||
my $ctx = shift;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
my $ctx = shift;
|
||||
my $message_ref = $ctx->getpriv();
|
||||
|
||||
my $incoming = MailScanner::Config::Value('inqueuedir');
|
||||
MailScanner::Log::DebugLog("eom_callback: incoming = " . @{$incoming}[0]);
|
||||
if ($incoming eq '') {
|
||||
MailScanner::Log::WarnLog("Unable to determine incoming queue!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
my $file = @{$incoming}[0] . '/temp-' . ${$message_ref};
|
||||
my $file2 = @{$incoming}[0] . '/' . ${$message_ref};
|
||||
my $incoming = MailScanner::Config::Value('inqueuedir');
|
||||
MailScanner::Log::DebugLog("eom_callback: incoming = " . @{$incoming}[0]);
|
||||
if ($incoming eq '') {
|
||||
MailScanner::Log::WarnLog("Unable to determine incoming queue!");
|
||||
Sendmail::PMilter::SMFIS_TEMPFAIL;
|
||||
return;
|
||||
}
|
||||
my $file = @{$incoming}[0] . '/temp-' . ${$message_ref};
|
||||
my $file2 = @{$incoming}[0] . '/' . ${$message_ref};
|
||||
|
||||
move($file, $file2);
|
||||
move($file, $file2);
|
||||
|
||||
$ctx->setpriv(undef);
|
||||
$ctx->setpriv(undef);
|
||||
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
Sendmail::PMilter::SMFIS_CONTINUE;
|
||||
}
|
||||
|
||||
#
|
||||
# Create and write a PID file for a given process id
|
||||
#
|
||||
sub WritePIDFile {
|
||||
my($process,$PidFile) = @_;
|
||||
my($process,$PidFile) = @_;
|
||||
|
||||
my $pidfh = new FileHandle;
|
||||
$pidfh->open(">$PidFile")
|
||||
my $pidfh = new FileHandle;
|
||||
$pidfh->open(">$PidFile")
|
||||
or MailScanner::Log::WarnLog("Cannot write pid file %s, %s", $PidFile, $!);
|
||||
print $pidfh "$process\n";
|
||||
$pidfh->close();
|
||||
print $pidfh "$process\n";
|
||||
$pidfh->close();
|
||||
}
|
||||
|
||||
#
|
||||
# Start logging
|
||||
#
|
||||
sub StartLogging {
|
||||
my($filename) = @_;
|
||||
my($filename) = @_;
|
||||
|
||||
my $procname = 'MSMilter';
|
||||
my $procname = 'MSMilter';
|
||||
|
||||
my $logbanner = "MSMilter Daemon starting...";
|
||||
my $logbanner = "MSMilter Daemon starting...";
|
||||
|
||||
MailScanner::Log::Configure($logbanner, 'syslog');
|
||||
MailScanner::Log::Configure($logbanner, 'syslog');
|
||||
|
||||
# Need to know log facility *before* we have read the whole config file!
|
||||
my $facility = MailScanner::Config::QuickPeek($filename, 'syslogfacility');
|
||||
my $logsock = MailScanner::Config::QuickPeek($filename, 'syslogsockettype');
|
||||
# Need to know log facility *before* we have read the whole config file!
|
||||
my $facility = MailScanner::Config::QuickPeek($filename, 'syslogfacility');
|
||||
my $logsock = MailScanner::Config::QuickPeek($filename, 'syslogsockettype');
|
||||
|
||||
MailScanner::Log::Start($procname, $facility, $logsock);
|
||||
MailScanner::Log::Start($procname, $facility, $logsock);
|
||||
}
|
||||
|
||||
sub ExitParent {
|
||||
my($sig) = @_; # Arg is the signal name
|
||||
my($sig) = @_; # Arg is the signal name
|
||||
|
||||
# Reap children (wait up to one minute)
|
||||
my $counter=0;
|
||||
while ($counter < 30) {
|
||||
# Reap children (wait up to one minute)
|
||||
my $counter=0;
|
||||
while ($counter < 30) {
|
||||
if (wait() > 0) {
|
||||
# Still active children
|
||||
sleep(2);
|
||||
$counter++;
|
||||
MailScanner::Log::NoticeLog("Milter closing, waiting on children to finish...");
|
||||
# Still active children
|
||||
sleep(2);
|
||||
$counter++;
|
||||
MailScanner::Log::NoticeLog("Milter closing, waiting on children to finish...");
|
||||
} else {
|
||||
$counter=30;
|
||||
$counter=30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Cleanup partial writes
|
||||
my $incoming = MailScanner::Config::Value('inqueuedir');
|
||||
unlink glob @{$incoming}[0] . "/temp-*";
|
||||
# Cleanup partial writes
|
||||
my $incoming = MailScanner::Config::Value('inqueuedir');
|
||||
unlink glob @{$incoming}[0] . "/temp-*";
|
||||
|
||||
unlink $PidFile; # Ditch the pid file
|
||||
unlink $PidFile; # Ditch the pid file
|
||||
|
||||
MailScanner::Log::Stop();
|
||||
MailScanner::Log::Stop();
|
||||
|
||||
exit 0;
|
||||
exit 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -274,6 +274,10 @@ sub WriteHeader {
|
|||
$line =~ s/^S//;
|
||||
$line = 'S<' . $line . '>';
|
||||
$Tf->print($line . "\n");
|
||||
} elsif ($line =~ /^E/) {
|
||||
$line =~ s/^E//;
|
||||
$line = 'E<' . $line . '>';
|
||||
$Tf->print($line . "\n");
|
||||
}
|
||||
}
|
||||
if ($this->{body}[0] eq "ORIGINAL") {
|
||||
|
@ -734,7 +738,7 @@ sub Start {
|
|||
|
||||
# Locate predata header
|
||||
while($data = MailScanner::Sendmail::ReadRecord($$this{_handle})) {
|
||||
last if $data !~ /^(O<|S<)/;
|
||||
last if $data !~ /^(O<|S<|E<)/;
|
||||
$$this{_startpos}= tell $$this{_handle};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# MailScanner - SMTP Email Processor
|
||||
# Copyright (C) 2018 MailScanner project
|
||||
# Copyright (C) 2018-2020 MailScanner project
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -431,6 +431,12 @@ sub new {
|
|||
push @{$message->{metadata}}, "S$recdata";
|
||||
MailScanner::Log::DebugLog("MSMail: ReadQf: from = $recdata");
|
||||
$pos = tell $RQf
|
||||
} elsif ($recdata =~ /^E</) {
|
||||
$recdata =~ s/^E<//;
|
||||
$recdata =~ s/\>$//;
|
||||
push @{$message->{metadata}}, "E$recdata";
|
||||
MailScanner::Log::DebugLog("MSMail: ReadQf: from = $recdata");
|
||||
$pos = tell $RQf
|
||||
} else {
|
||||
last;
|
||||
}
|
||||
|
@ -893,6 +899,7 @@ sub new {
|
|||
my $recipientfound = 0;
|
||||
my $permfail = 0;
|
||||
my($sender);
|
||||
my $opts = '';
|
||||
my $messagesent = 0;
|
||||
my $InFrom = 0;
|
||||
my $response = '';
|
||||
|
@ -957,6 +964,12 @@ sub new {
|
|||
$sender = $line;
|
||||
MailScanner::Log::DebugLog("MSMail: KickMessage: sender = $sender");
|
||||
$msgstart = tell $queuehandle;
|
||||
} elsif ($line =~ /^E</) {
|
||||
$line =~ s/^E<//;
|
||||
$line =~ s/\>$//;
|
||||
$opts = $line;
|
||||
MailScanner::Log::DebugLog("MSMail: KickMessage: options = $opts");
|
||||
$msgstart = tell $queuehandle;
|
||||
} else {
|
||||
last;
|
||||
}
|
||||
|
@ -1023,7 +1036,15 @@ sub new {
|
|||
# From received success
|
||||
my $recipientsok = 1;
|
||||
foreach my $myrecipient (@recipient) {
|
||||
$req = 'RCPT TO: ' . $myrecipient . "\n";
|
||||
$req = 'RCPT TO: ' . $myrecipient;
|
||||
|
||||
# RFC 3461
|
||||
if ($opts ne '') {
|
||||
$req = $req . ' ' . $opts;
|
||||
}
|
||||
|
||||
$req = $req . "\n";
|
||||
|
||||
$socket->send($req);
|
||||
$socket->recv($response, 1024);
|
||||
if ($response =~ /^250/ ) {
|
||||
|
@ -1152,7 +1173,7 @@ sub new {
|
|||
$response =~ s/\n//;
|
||||
while(!eof($queuehandle)) {
|
||||
$line = readline $queuehandle;
|
||||
last unless ($line =~ /^(?:O|S)</);
|
||||
last unless ($line =~ /^(?:O|S|E)</);
|
||||
$queuehandle2->print($line);
|
||||
}
|
||||
$queuehandle2->print('X-'. $orgname . '-MailScanner-Relay-Reject: ' . $response . "\n");
|
||||
|
|
Loading…
Reference in a new issue