Skip to content
Browse files

Add Postfix XCLIENT support to smtp-forward plugin

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.
  • Loading branch information...
1 parent de72a73 commit f9d84d94c787e4842ccc5ff5edd7bf652f5aa102 @msimerson msimerson committed Feb 13, 2014
Showing with 101 additions and 16 deletions.
  1. +101 −16 plugins/queue/smtp-forward
View
117 plugins/queue/smtp-forward
@@ -9,6 +9,8 @@ smtp-forward
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.
@@ -19,31 +21,43 @@ 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) {
- 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;
- }
- $self->log(LOGWARN, "WARNING: Ignoring additional arguments.")
- if (@args > 2);
+ if (@args <= 0) {
+ die "No SMTP server specified in smtp-forward config";
+ };
+
+ if ($args[0] =~ /^([\.\w_-]+)$/) {
+ $self->{_smtp_server} = $1;
}
else {
- die("No SMTP server specified in smtp-forward config");
+ 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 {
@@ -56,8 +70,12 @@ sub hook_queue {
Port => $self->{_smtp_port},
Timeout => 60,
Hello => $self->qp->config("me"),
- )
- || die $!;
+ ) || 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) {
@@ -81,3 +99,70 @@ sub hook_queue {
$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;
+}

0 comments on commit f9d84d9

Please sign in to comment.
Something went wrong with that request. Please try again.