Front page | perl.qpsmtpd |
Postings from April 2012
[PATCH] imported check_qmail_deliverable
From:
Matt Simerson
Date:
April 7, 2012 19:23
Subject:
[PATCH] imported check_qmail_deliverable
Message ID:
1333851774-72331-1-git-send-email-matt@tnpi.net
requires Qmail::Deliverable, which is a modern replacement for the
apparently popular but no longer available check_deliver plugin.
---
STATUS | 5 -
plugins/check_qmail_deliverable | 202 +++++++++++++++++++++++++++++++++++++++
2 files changed, 202 insertions(+), 5 deletions(-)
create mode 100755 plugins/check_qmail_deliverable
diff --git a/STATUS b/STATUS
index 81cf0df..78ef005 100644
--- a/STATUS
+++ b/STATUS
@@ -18,11 +18,6 @@ Roadmap
- Add user configuration plugin infrastructure
- Add plugin API for checking if a local email address is valid
-
- - Include the popular check_delivery[1] functionality via the user API
- [1] until then get it from
- http://www.openminddev.net/files/qpsmtpd/plugins/check_delivery/
-
- Add API to reject individual recipients after the RCPT has been
accepted and generate individual bounce messages.
diff --git a/plugins/check_qmail_deliverable b/plugins/check_qmail_deliverable
new file mode 100755
index 0000000..6230e66
--- /dev/null
+++ b/plugins/check_qmail_deliverable
@@ -0,0 +1,202 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+check_qmail_deliverable - Check that the recipient address is deliverable
+
+=head1 DESCRIPTION
+
+See the description of Qmail::Deliverable.
+
+This B<qpsmtpd plugin> uses the client/server interface and needs a running
+qmail-deliverabled. If no connection can be made, deliverability is simply
+assumed.
+
+The modules LWP (libwww-perl) and HTTP::Daemon, available from CPAN, are
+required for qmail-deliverabled and Qmail::Deliverable::Client.
+
+=head1 CONFIGURATION
+
+=over 4
+
+=item server host:port
+
+Hostname (or IP address), and port (both!) of the qmail-deliverabled server. If
+none is specified, the default (127.0.0.1:8998) is used.
+
+=item server smtproutes:host:port
+
+If the specification is prepended by the literal text C<smtproutes:>, then for
+recipient domains listed in your /var/qmail/control/smtproutes use their
+respective hosts for the check. For other domains, the given host is used. The
+port has to be the same across all servers.
+
+Example:
+
+ check_qmail_deliverable server smtproutes:127.0.0.1:8998
+
+Use "smtproutes:8998" (no second colon) to simply skip the deliverability
+check for domains not listed in smtproutes.
+
+=back
+
+=head1 CAVEATS
+
+Given a null host in smtproutes, the normal MX lookup should be used. This
+plugin does not do this, because we don't want to harrass arbitrary servers.
+
+Connection failure is *faked* when there is no smtproute.
+
+=head1 LEGAL
+
+This software is released into the public domain, and does not come with
+warranty or guarantee of any kind. Use it at your own risk.
+
+=head1 AUTHOR
+
+Juerd <#####@juerd.nl>
+
+=head1 SEE ALSO
+
+L<Qmail::Deliverable>, L<qmail-deliverabled>, L<Qmail::Deliverable::Client>
+
+=cut
+
+#################################
+#################################
+
+BEGIN {
+ use FindBin qw($Bin $Script);
+ if (not $INC{'Qpsmtpd.pm'}) {
+ my $dir = '$PLUGINS_DIRECTORY';
+ -d and $dir = $_ for qw(
+ /home/qpsmtpd/plugins
+ /home/smtp/qpsmtpd/plugins
+ /usr/local/qpsmtpd/plugins
+ /usr/local/share/qpsmtpd/plugins
+ /usr/share/qpsmtpd/plugins
+ );
+
+ my $file = "the 'plugins' configuration file";
+ -f and $file = $_ for qw(
+ /home/qpsmtpd/config/plugins
+ /home/smtp/qpsmtpd/config/plugins
+ /usr/local/qpsmtpd/config/plugins
+ /usr/local/etc/qpsmtpd/plugins
+ /etc/qpsmtpd/plugins
+ );
+
+ # "die" would print "BEGIN failed" garbage
+ print STDERR <<"END";
+
+This is a plugin for qpsmtpd and should not be run manually.
+
+To install the plugin:
+
+ ln -s $Bin/$Script $dir/
+
+And add "$Script server 127.0.0.1:8998" to $file, before rcpt_ok.
+For configuration instructions, read "man $Script"
+
+(Paths may vary.)
+
+END
+ exit 255;
+ }
+}
+
+#################################
+#################################
+
+use Qmail::Deliverable::Client qw(deliverable);
+use strict;
+
+my %smtproutes;
+my $shared_domain; # global variable to be closed over by the SERVER callback
+
+sub register {
+ my ($self, $qp, @args) = @_;
+ if (@args % 2) {
+ $self->log(LOGWARN, "Odd number of arguments, using default config");
+ } else {
+ my %args = @args;
+ if ($args{server} =~ /^smtproutes:/) {
+
+ my ($fallback, $port) = $args{server} =~ /:(?:(.*?):?)(\d+)/;
+
+ open my $fh, "/var/qmail/control/smtproutes"
+ or warn "Could not read smtproutes";
+ for (readline $fh) {
+ my ($domain, $mx) = /^(.*?) : \[? ( [^\]:\s]* )/x;
+ $smtproutes{$domain} = $mx;
+ }
+
+ $Qmail::Deliverable::Client::SERVER = sub {
+ my $server = _smtproute($shared_domain);
+ return "$server:$port" if defined $server;
+ return "$fallback:$port" if defined $fallback;
+ return;
+ };
+
+ } elsif ($args{server}) {
+ $Qmail::Deliverable::Client::SERVER = $args{server};
+ }
+ }
+ $self->register_hook("rcpt", "rcpt_handler");
+}
+
+sub rcpt_handler {
+ my ($self, $transaction, $rcpt) = @_;
+
+ my $address = $rcpt->address;
+ $self->log(LOGINFO, "Checking deliverability for recipient '$address'");
+
+ $shared_domain = $rcpt->host;
+
+ my $rv = deliverable $address;
+
+ if (not defined $rv or not length $rv) {
+ $self->log(LOGWARN, "Unknown error.");
+ return DECLINED;
+ }
+
+ my $k = 0; # known status code
+ $self->log(LOGINFO, "Permission failure"), $k++ if $rv == 0x11;
+ $self->log(LOGINFO, "qmail-command in dot-qmail"), $k++ if $rv == 0x12;
+ $self->log(LOGINFO, "bouncesaying with program"), $k++ if $rv == 0x13;
+ $self->log(LOGINFO, "Temporarily undeliverable: group/world writable"), $k++
+ if $rv == 0x21;
+ $self->log(LOGINFO, "Temporarily undeliverable: sticky home directory"),$k++
+ if $rv == 0x22;
+ $self->log(LOGINFO, "Error: $Qmail::Deliverable::Client::ERROR"), $k++
+ if $rv == 0x2f;
+ $self->log(LOGINFO, "Normal delivery"), $k++ if $rv == 0xf1;
+ $self->log(LOGINFO, "Deliverable through vpopmail"), $k++ if $rv == 0xf2;
+ $self->log(LOGINFO, "SHOULD NOT HAPPEN"), $k++ if $rv == 0xfe;
+ $self->log(LOGINFO, "Address is not local"), $k++ if $rv == 0xff;
+
+ $self->log(LOGINFO, sprintf("Unknown: 0x%02x", $rv)) if $rv and not $k;
+
+ return DECLINED if $rv;
+ return DENY, "Sorry, no mailbox here by that name. qd (#5.1.1)";
+}
+
+sub _smtproute {
+ my ($domain) = @_;
+ my @parts = split /\./, $domain;
+ if (exists $smtproutes{$domain}) {
+ return undef if $smtproutes{$domain} eq "";
+ return $smtproutes{$domain};
+ }
+ for (reverse 1 .. @parts) {
+ my $wildcard = join "", map ".$_", @parts[-$_ .. -1];
+ if (exists $smtproutes{$wildcard}) {
+ return undef if $smtproutes{$wildcard} eq "";
+ return $smtproutes{$wildcard};
+ }
+ }
+ return undef if not exists $smtproutes{""};
+ return undef if $smtproutes{""} eq "";
+ return $smtproutes{""};
+}
+
--
1.7.9.4
-
[PATCH] imported check_qmail_deliverable
by Matt Simerson