This commit is contained in:
Shawn Iverson 2020-09-07 17:17:31 -04:00
commit 87958bf2d8
3 changed files with 422 additions and 369 deletions

View file

@ -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;
}

View file

@ -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};
}

View file

@ -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");