f9d84d94c7
manually merged in PR #2 from cventers XCLIENT support allows Qpsmtpd to forward client information, such as the IP address and HELO information, to Postfix such that it can use that information in access control decisions and logging. XCLIENT is documented here: http://www.postfix.org/XCLIENT_README.html This patch adds a "xclient" argument to smtp-forward which enables the use of the XCLIENT verb if it is advertised by the server smtp-forward is delivering mail to.
169 lines
4.4 KiB
Perl
169 lines
4.4 KiB
Perl
#!perl -w
|
|
|
|
=head1 NAME
|
|
|
|
smtp-forward
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This plugin forwards the mail via SMTP to a specified server, rather than
|
|
delivering the email locally.
|
|
|
|
It also supports the Postfix XCLIENT extension.
|
|
|
|
=head1 CONFIG
|
|
|
|
It takes one required parameter, the IP address or hostname to forward to.
|
|
|
|
queue/smtp-forward 10.2.2.2
|
|
|
|
Optionally you can also add a port:
|
|
|
|
queue/smtp-forward 10.2.2.2 9025
|
|
|
|
And a flag:
|
|
|
|
queue/smtp-forward 10.2.2.2 9025 xclient
|
|
|
|
=cut
|
|
|
|
use Net::SMTP;
|
|
use Net::Cmd qw//;
|
|
|
|
sub init {
|
|
my ($self, $qp, @args) = @_;
|
|
|
|
if (@args <= 0) {
|
|
die "No SMTP server specified in smtp-forward config";
|
|
};
|
|
|
|
if ($args[0] =~ /^([\.\w_-]+)$/) {
|
|
$self->{_smtp_server} = $1;
|
|
}
|
|
else {
|
|
die "Bad data in smtp server: $args[0]";
|
|
}
|
|
|
|
$self->{_smtp_port} = 25;
|
|
if (@args > 1 and $args[1] =~ /^(\d+)$/) {
|
|
$self->{_smtp_port} = $1;
|
|
}
|
|
|
|
for (my $i = 2; $i < @args; $i++) {
|
|
if ($args[$i] !~ /^(\w+)$/) {
|
|
$self->log(LOGWARN, "WARNING: Rejecting invalid flag");
|
|
next;
|
|
}
|
|
my $flag = lc($1);
|
|
$self->log(LOGWARN, "WARNING: Unknown flag $flag") unless $flag eq 'xclient';
|
|
$self->{_flags}{$flag} = 1;
|
|
}
|
|
}
|
|
|
|
sub hook_queue {
|
|
my ($self, $transaction) = @_;
|
|
|
|
$self->log(LOGINFO,
|
|
"forwarding to $self->{_smtp_server}:$self->{_smtp_port}");
|
|
my $smtp = Net::SMTP->new(
|
|
$self->{_smtp_server},
|
|
Port => $self->{_smtp_port},
|
|
Timeout => 60,
|
|
Hello => $self->qp->config("me"),
|
|
) || die $!;
|
|
|
|
|
|
my $xcret = $self->xclient($smtp);
|
|
return(DECLINED, $xcret) if defined $xcret;
|
|
|
|
$smtp->mail($transaction->sender->address || "")
|
|
or return (DECLINED, "Unable to queue message ($!)");
|
|
for ($transaction->recipients) {
|
|
$smtp->to($_->address)
|
|
or return (DECLINED, "Unable to queue message ($!)");
|
|
}
|
|
$smtp->data() or return (DECLINED, "Unable to queue message ($!)");
|
|
$smtp->datasend($transaction->header->as_string)
|
|
or return (DECLINED, "Unable to queue message ($!)");
|
|
$transaction->body_resetpos;
|
|
while (my $line = $transaction->body_getline) {
|
|
$smtp->datasend($line)
|
|
or return (DECLINED, "Unable to queue message ($!)");
|
|
}
|
|
$smtp->dataend() or return (DECLINED, "Unable to queue message ($!)");
|
|
my $qid = $smtp->message();
|
|
my @list = split(' ', $qid);
|
|
$qid = pop(@list);
|
|
|
|
$smtp->quit() or return (DECLINED, "Unable to queue message ($!)");
|
|
$self->log(LOGINFO, "finished queueing");
|
|
return (OK, "queued as $qid");
|
|
}
|
|
|
|
sub xclient {
|
|
my ($self, $smtp) = @_;
|
|
|
|
return unless $self->{_flags}{xclient};
|
|
|
|
my $parts = $smtp->supports('XCLIENT');
|
|
if (!defined($parts)) { # what parts do they want?
|
|
return "Unable to queue message (Server does not advertise XCLIENT support)";
|
|
};
|
|
|
|
my %haveparts;
|
|
for my $part (split(/\s+/, $parts)) {
|
|
next unless $part =~ /^(\w+)$/;
|
|
$haveparts{uc($part)} = 1;
|
|
}
|
|
|
|
my $conn = $self->qp->connection;
|
|
my @rparts;
|
|
|
|
if ($haveparts{NAME}) {
|
|
my $name = $conn->remote_host || '[UNAVAILABLE]';
|
|
$name = '[UNAVAILABLE]' if ($name eq 'Unknown');
|
|
push(@rparts, "NAME=$name");
|
|
}
|
|
|
|
if ($haveparts{ADDR}) {
|
|
my $ip = $conn->remote_ip;
|
|
push(@rparts, "ADDR=$ip");
|
|
}
|
|
|
|
if ($haveparts{PORT}) {
|
|
my $port = $conn->remote_port;
|
|
push(@rparts, "PORT=$port");
|
|
}
|
|
|
|
my $hello_name = $self->connection->hello_host;
|
|
$hello_name ||= '[UNAVAILABLE]';
|
|
if ($haveparts{HELO}) {
|
|
push(@rparts, "HELO=$hello_name");
|
|
}
|
|
|
|
my $hello = $conn->hello;
|
|
if ($haveparts{PROTO} && defined($hello)) {
|
|
my $proto = (uc($hello) eq 'EHLO') ? 'ESMTP' : 'SMTP';
|
|
push(@rparts, "PROTO=$proto");
|
|
}
|
|
|
|
while (scalar(@rparts)) {
|
|
my @items;
|
|
my $cursz = 0;
|
|
while (defined(my $item = $rparts[0])) {
|
|
my $len = length($item);
|
|
last if ($cursz + $len > 500);
|
|
$cursz += $len;
|
|
push(@items, shift @rparts);
|
|
}
|
|
|
|
last unless @items;
|
|
if ($smtp->command('XCLIENT', @items)->response() != Net::Cmd::CMD_OK) {
|
|
return "Unable to queue message (XCLIENT failed)";
|
|
}
|
|
}
|
|
|
|
$smtp->hello($hello_name) or return "Unable to queue message (HELLO after XCLIENT failed)";
|
|
return;
|
|
}
|