1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
#!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;
}
|