Initial commit.
Signed-off-by: Thomas Hochstein <thh@inter.net>
This commit is contained in:
commit
0bd5c08c20
224
checkmail.pl
Normal file
224
checkmail.pl
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
#!/usr/bin/perl -w
|
||||||
|
#
|
||||||
|
# checkmail.pl
|
||||||
|
##############
|
||||||
|
|
||||||
|
# (c) 2002-2005 Thomas Hochstein <thh@inter.net>
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
# more details.
|
||||||
|
|
||||||
|
# Versionsnummer ######################
|
||||||
|
$ver = '0.2 beta (20050803)';
|
||||||
|
|
||||||
|
# Modules #############################
|
||||||
|
use Getopt::Std;
|
||||||
|
use Net::DNS;
|
||||||
|
use Net::SMTP;
|
||||||
|
|
||||||
|
# Konfiguration #######################
|
||||||
|
# Hier passende Werte einsetzen! #
|
||||||
|
#######################################
|
||||||
|
%config=();
|
||||||
|
# HELO-/EHLO-Parameter - a valid hostname you own
|
||||||
|
$config{'helo'} = 'testhost.domain.example';
|
||||||
|
# MAIL FROM:-Parameter - a valid address you control
|
||||||
|
$config{'from'} = 'mailtest@testhost.domain.example';
|
||||||
|
# Zufaelliger Localpart fuer -r - a valid random localpart
|
||||||
|
$config{'rand'} = 'ZOq62fow1i';
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Hauptprogramm #######################
|
||||||
|
|
||||||
|
# Konfiguration einlesen
|
||||||
|
my %options;
|
||||||
|
getopts('hqlrf:m:', \%options);
|
||||||
|
|
||||||
|
if ($options{'h'} or (!$options{'f'} and !$ARGV[0])) {
|
||||||
|
print "$0 v $ver\nUsage: $0 [-hqlr] [-m <host>] -f <file>|<address>\n";
|
||||||
|
print "Options: -h display this notice\n";
|
||||||
|
print " -q quiet (no output, just exit with 0/1/2/3)\n";
|
||||||
|
print " -l extended logging\n";
|
||||||
|
print " -r test random address to verify verification\n";
|
||||||
|
print " -m <host> no DNS lookup, just test this host\n";
|
||||||
|
print " -f <file> parse file (one address per line)\n";
|
||||||
|
print " <address> mail address to check\n\n";
|
||||||
|
exit(100);
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($options{'f'}) {
|
||||||
|
if (-e $options{'f'}) {
|
||||||
|
open FILE, "<$options{'f'}" or die("ERROR: Could not open file $options{'f'} for reading: $!");
|
||||||
|
} else {
|
||||||
|
die("ERROR: File $options{'f'} does not exist!\n");
|
||||||
|
};
|
||||||
|
$log = '';
|
||||||
|
while(<FILE>) {
|
||||||
|
chomp;
|
||||||
|
($status,$log) = checkdns($_,$log);
|
||||||
|
};
|
||||||
|
close FILE;
|
||||||
|
# force exit(0)
|
||||||
|
$status = 0;
|
||||||
|
} else {
|
||||||
|
($status,$log) = checkdns($ARGV[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
print $log if ($options{'l'});
|
||||||
|
|
||||||
|
# status 0: valid / batch processing
|
||||||
|
# 1: invalid
|
||||||
|
# 2: cannot verify
|
||||||
|
# 3: temporary (?) failure
|
||||||
|
exit($status);
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Subroutinen #########################
|
||||||
|
|
||||||
|
sub checkdns {
|
||||||
|
# - fester Host angegeben (-m)?
|
||||||
|
# - sonst: MX-Record ermitteln
|
||||||
|
# - bei Verbindungsproblemen naechsten MX versuchen
|
||||||
|
# - falls kein MX vorhanden, Fallback auf A
|
||||||
|
# -> jeweils Adresse testen via checksmtp()
|
||||||
|
my ($address,$logging) = @_;
|
||||||
|
my ($rr,$mailhost,$status,@mx);
|
||||||
|
my $dnsresult = 'okay';
|
||||||
|
# (my $lp = $address) =~ s/^([^@]+)@.*/$1/;
|
||||||
|
(my $domain = $address) =~ s/[^@]+\@(\S*)$/$1/;
|
||||||
|
|
||||||
|
$logging .= "\n----- BEGIN $address -----\n";
|
||||||
|
|
||||||
|
# DNS-Lookup unterdrueckt?
|
||||||
|
if ($options{'m'}) {
|
||||||
|
print " Connection to $options{'m'} forced by -m.\n";
|
||||||
|
$logging .= "Connection to $options{'m'} forced by -m.\n";
|
||||||
|
($status,$logging) = checksmtp($options{'m'},$address,$domain,$logging);
|
||||||
|
$logging .= "----- END $address -----\n";
|
||||||
|
return ($status,$logging);
|
||||||
|
};
|
||||||
|
|
||||||
|
# Resolver-Objekt
|
||||||
|
$resolve = Net::DNS::Resolver -> new();
|
||||||
|
$resolve->usevc(1);
|
||||||
|
$resolve->tcp_timeout(15);
|
||||||
|
|
||||||
|
# MX-Record feststellen
|
||||||
|
@mx = mx($resolve,$domain) or $dnsresult = $resolve->errorstring;
|
||||||
|
print " $domain (MX: $dnsresult)\n" if !($options{'q'});
|
||||||
|
|
||||||
|
if (@mx) {
|
||||||
|
WALKMX: foreach $rr (@mx) {
|
||||||
|
$mailhost = $rr->exchange;
|
||||||
|
print " MX: $mailhost / $address\n" if !($options{'q'});
|
||||||
|
$logging .= "Try MX: $mailhost\n";
|
||||||
|
($status,$logging) = checksmtp($mailhost,$address,$domain,$logging);
|
||||||
|
last WALKMX if ($status < 3);
|
||||||
|
};
|
||||||
|
} elsif ($dnsresult eq 'NXDOMAIN' or $dnsresult eq 'NOERROR' or $dnsresult eq 'REFUSED') {
|
||||||
|
# wenn kein MX-Record: A-Record feststellen
|
||||||
|
$logging .= "MX error: $dnsresult\n";
|
||||||
|
$dnsresult = 'okay';
|
||||||
|
$query = $resolve->search($domain) or $dnsresult = $resolve->errorstring;
|
||||||
|
print " $domain (A: $dnsresult)\n" if !($options{'q'});
|
||||||
|
if ($query) {
|
||||||
|
foreach $rr ($query->answer) {
|
||||||
|
next unless $rr->type eq "A";
|
||||||
|
$mailhost = $rr->address;
|
||||||
|
print " A: $mailhost / $address\n" if !($options{'q'});
|
||||||
|
$logging .= "Try A: $mailhost\n";
|
||||||
|
($status,$logging) = checksmtp($mailhost,$address,$domain,$logging);
|
||||||
|
};
|
||||||
|
} elsif ($dnsresult eq 'NXDOMAIN' or $dnsresult eq 'NOERROR' or $dnsresult eq 'REFUSED') {
|
||||||
|
# wenn auch kein A-Record: what a pity ...
|
||||||
|
print " > NO DNS-RECORD (MX/A) FOUND.\n" if !($options{'q'});
|
||||||
|
$logging .= "A error: $dnsresult\n";
|
||||||
|
$status = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
$logging .= "----- END $address -----\n";
|
||||||
|
return ($status,$logging);
|
||||||
|
};
|
||||||
|
|
||||||
|
sub checksmtp {
|
||||||
|
# - zu $mailhost verbinden, $adresse testen (SMTP-Dialog bis RCPT TO)
|
||||||
|
# - ggf. (-r) testen, ob sicher ungueltige Adresse abgelehnt oder
|
||||||
|
# alles angenommen wird
|
||||||
|
my($mailhost,$address,$domain,$logging)=@_;
|
||||||
|
my($smtp,$status,$valid);
|
||||||
|
$logging .= "-------------------------\n";
|
||||||
|
CONNECT: if ($smtp = Net::SMTP->new($mailhost,Hello => $config{'helo'},Timeout => 30)) {
|
||||||
|
$logging .= $smtp->banner;
|
||||||
|
$logging .= "EHLO $config{'helo'}\n";
|
||||||
|
$logging .= parse_reply($smtp->code,$smtp->message);
|
||||||
|
$smtp->mail($config{'from'});
|
||||||
|
$logging .= "MAIL FROM:<$config{'from'}>\n";
|
||||||
|
$logging .= parse_reply($smtp->code,$smtp->message);
|
||||||
|
# wird RCPT TO akzeptiert?
|
||||||
|
$valid = $smtp->to($address);
|
||||||
|
$logging .= "RCPT TO:<$address>\n";
|
||||||
|
if ($smtp->code > 0) {
|
||||||
|
# es kam eine Antwort auf RCPT TO
|
||||||
|
$logging .= parse_reply($smtp->code,$smtp->message);
|
||||||
|
if ($valid) {
|
||||||
|
# RCPT TO akzeptiert
|
||||||
|
$status = 0;
|
||||||
|
if ($options{'r'}) {
|
||||||
|
# werden sicher ungueltige Adressen abgewiesen?
|
||||||
|
$valid = $smtp->to($config{'rand'}.'@'.$domain);
|
||||||
|
$logging .= 'RCPT TO:<'.$config{'rand'}.'@'.$domain.">\n";
|
||||||
|
if ($smtp->code > 0) {
|
||||||
|
# es kam eine Antwort auf RCPT TO (fuer $rand)
|
||||||
|
$logging .= parse_reply($smtp->code,$smtp->message);
|
||||||
|
if ($valid) {
|
||||||
|
# ungueltiges RCPT TO akzeptiert
|
||||||
|
print " > Sorry, cannot verify. You'll have to send a testmail ...\n" if !($options{'q'});
|
||||||
|
$status = 2;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
# Timeout nach RCPT TO (fuer $rand)
|
||||||
|
print " > Temporary failure.\n" if !($options{'q'});
|
||||||
|
$logging .= "---Timeout---\n";
|
||||||
|
$smtp->quit;
|
||||||
|
$status = 3;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
print " > Address is valid.\n" if (!$status and !$options{'q'});
|
||||||
|
} else {
|
||||||
|
# RCPT TO nicht akzeptiert
|
||||||
|
print " > Address is INVALID.\n" if !($options{'q'});
|
||||||
|
$status = 1;
|
||||||
|
};
|
||||||
|
# Verbindung beenden
|
||||||
|
$smtp->quit;
|
||||||
|
$logging .= "QUIT\n";
|
||||||
|
$logging .= parse_reply($smtp->code,$smtp->message);
|
||||||
|
} else {
|
||||||
|
# Timeout nach RCPT TO
|
||||||
|
print " > Temporary failure.\n" if !($options{'q'});
|
||||||
|
$logging .= "---Timeout---\n";
|
||||||
|
$smtp->quit;
|
||||||
|
$status = 3;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
# Verbindung fehlgeschlagen
|
||||||
|
print " > Temporary failure.\n" if !($options{'q'});
|
||||||
|
$logging .= "---Timeout---\n";
|
||||||
|
$status = 3;
|
||||||
|
};
|
||||||
|
return ($status,$logging);
|
||||||
|
};
|
||||||
|
|
||||||
|
sub parse_reply {
|
||||||
|
my($code,$message)=@_;
|
||||||
|
my($reply);
|
||||||
|
$reply = $code . ' ' . $message;
|
||||||
|
return $reply;
|
||||||
|
}
|
||||||
|
|
126
checkmail.readme
Normal file
126
checkmail.readme
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
NAME
|
||||||
|
checkmail
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
checkmail.pl [-hqlr] [-m <host>] -f <file>|<address>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
checkmail prüft die Zustellbarkeit von E-Mail-Adressen. Es ist
|
||||||
|
entweder die zu prüfende Adresse als letztes Argument oder
|
||||||
|
mit dem Parameter -f eine Datei mit zu prüfenden Adressen zu
|
||||||
|
übergeben.
|
||||||
|
|
||||||
|
Argumente:
|
||||||
|
|
||||||
|
-h
|
||||||
|
Mit dem Argument -h wird eine kurze Hilfe ausgegeben.
|
||||||
|
|
||||||
|
-q
|
||||||
|
Mit dem Argument -q werden alle Ausgaben unterdrückt;
|
||||||
|
das Script beendet sich nur mit einem Exit-Status zwischen
|
||||||
|
0 und 3.
|
||||||
|
|
||||||
|
-l
|
||||||
|
Mit dem Argument -l wird ein ausführliches Logging des
|
||||||
|
SMTP-Dialogs aktiviert.
|
||||||
|
|
||||||
|
-r
|
||||||
|
Mit dem Argument -r wird auch eine gezielt ungültige Adresse
|
||||||
|
geprüft, um festzustellen, ob der Host nicht einfach jede
|
||||||
|
Mailadresse ohne Prüfung akzeptiert.
|
||||||
|
-m
|
||||||
|
Mit dem Argument -m, gefolgt nach Leerzeichen von einem
|
||||||
|
Hostnamen, kann statt der Abfrage des für die Domain
|
||||||
|
zuständigen Hosts per DNS die Zustellfähigkeit bei einem
|
||||||
|
bestimmten Host geprüft werden.
|
||||||
|
|
||||||
|
Beispiel: checkmail.pl -m test.host.example mail@domain.example
|
||||||
|
|
||||||
|
-f
|
||||||
|
Mit dem Argument -f, gefolgt nach Leerzeichen von einem
|
||||||
|
Dateinamen, kann eine ganze Reihe von zu prüfenden Mailadressen
|
||||||
|
aus einer Datei eingelesen werden, die jeweils eine Adresse
|
||||||
|
pro Zeile enthält.
|
||||||
|
|
||||||
|
Beispiel: checkmail.pl -f adressen.txt
|
||||||
|
|
||||||
|
|
||||||
|
Basiskonfiguration:
|
||||||
|
|
||||||
|
Die Basiskonfiguration erfolgt innerhalb des Scripts. Anzugeben
|
||||||
|
sind:
|
||||||
|
|
||||||
|
$config{'helo'}:
|
||||||
|
Der im SMTP-Dialog für HELO/EHLO zu verwendende Hostname.
|
||||||
|
|
||||||
|
$config{'from'}:
|
||||||
|
Die Absenderadresse (Envelope-From:) für den Test.
|
||||||
|
|
||||||
|
$config{'rand'}:
|
||||||
|
Der Localpart der für den Parameter -r erforderlichen
|
||||||
|
zufälligen Adresse.
|
||||||
|
|
||||||
|
Funktionsweise:
|
||||||
|
|
||||||
|
Vor dem ersten Aufruf ist innerhalb des Scripts die
|
||||||
|
Basiskonfiguration vorzunehmen.
|
||||||
|
|
||||||
|
Danach kann das Script unter Angabe der zu prüfenden E-Mail-Adresse
|
||||||
|
aufgerufen werden. Es versucht den oder die zuständigen MX (Mail
|
||||||
|
eXchanger) für die Domain der Mailadresse zu ermitteln (ggf., falls
|
||||||
|
nicht vorhanden, an den entsprechenden Host zuzustellen), nach dort
|
||||||
|
zu verbinden und den SMTP-Dialog bis zum "RCPT TO:" durchzuführen,
|
||||||
|
um dann die Antwort des Mailservers auszuwerten.
|
||||||
|
|
||||||
|
Um Mailserver zu erkennen, die zunächst jede Mailadresse akzeptieren
|
||||||
|
und unerwünschte Mail erst später bouncen oder unterdrücken, kann
|
||||||
|
danach ein weiterer Test mit einer sicher ungültigen Mailadresse
|
||||||
|
ausgeführt werden (-r).
|
||||||
|
|
||||||
|
Nicht nur das Ergebnis des Tests, sondern auch der Dialog mit dem
|
||||||
|
Mailserver kann vermittels des Parameters -l ausgegeben werden; dies
|
||||||
|
ist hilfreich, um auszuschließen, daß die die Testverbindung aus
|
||||||
|
andere Gründen abgewiesen wird.
|
||||||
|
|
||||||
|
Wenn (gar) keine Textausgabe gewünscht ist, kann diese vermittels -q
|
||||||
|
unterdrückt werden; das Script beendet sich dann mit einem der vier
|
||||||
|
folgenden Statuscodes:
|
||||||
|
|
||||||
|
0: Adresse gültig oder Massenaufruf (-f)
|
||||||
|
1: Adresse ungültig
|
||||||
|
2: Adresse kann nicht geprüft werden (-r und negativer Test)
|
||||||
|
3: temporärer Fehler
|
||||||
|
|
||||||
|
Mehrere Mailadressen können mit Hilfe des Parameters -f innerhalb
|
||||||
|
einer Datei (eine Adresse pro Zeile) übergeben werden; in diesem
|
||||||
|
Fall wird immer der Status "0" zurückgegeben.
|
||||||
|
|
||||||
|
Schließlich kann mit Hilfe des Parameters -m die DNS-Abfrage
|
||||||
|
unterdrückt und die Verbindung zu einem bestimmten Host erzwungen
|
||||||
|
werden.
|
||||||
|
|
||||||
|
Hinweis: Der Test sollte nicht von einem Dialup-Host oder einem Host
|
||||||
|
auf einer sonstigen Blacklist aus durchgeführt werden! Ggf. kann ein
|
||||||
|
erneuter Aufruf von checkmail.pl -l für Klarheit sorgen, ob der Test
|
||||||
|
wegen der Auswertung einer Blacklist - unabhängig von der verwendeten
|
||||||
|
Adresse - fehlschlägt.
|
||||||
|
|
||||||
|
DEPENDANCIES
|
||||||
|
Die folgenden CPAN-Module werden neben Perl 5.6.1 oder höher benötigt:
|
||||||
|
|
||||||
|
Net::DNS
|
||||||
|
|
||||||
|
BUGS
|
||||||
|
- Fehler und Fehleingaben werden größtenteils nicht abgefangen.
|
||||||
|
|
||||||
|
Weitere Bugs nimmt <thh@inter.net> gerne entgegen.
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Thomas Hochstein <thh@inter.net>
|
||||||
|
|
||||||
|
VERSION
|
||||||
|
V 0.2 [beta]
|
||||||
|
|
||||||
|
COPYRIGHT
|
||||||
|
© 2002-2005 Thomas Hochstein.
|
||||||
|
See source for license und warranty.
|
Loading…
Reference in a new issue