#! /usr/bin/perl -w
use strict;
use Net::DNS;
use Socket qw/inet_aton/;
my $r = Net::DNS::Resolver->new;
for my $domain( @ARGV ) {
print "$domain\n" if @ARGV > 1;
my %res;
my $rr = $r->query( $domain, 'MX' );
if( !$rr ) {
if( $r->errorstring ne 'NOERROR' ) {
print "\tMX lookup error: ", $r->errorstring, "\n";
}
$rr = $r->query( $domain, 'A' );
if( !$rr ) {
print "\tA lookup error: ", $r->errorstring, "\n";
}
else {
for my $a( $rr->answer ) {
next unless $a->type eq "A";
my $ptr_rr = $r->query( join( '.', reverse( split /\./
+, $a->address )) . '.in-addr.arpa', 'PTR' );
if( !$ptr_rr ) {
push @{$res{0}}, {
ip => $a->address,
forw => $a->address,
back => $r->errorstring,
};
}
else {
$_->type eq 'PTR' and push @{$res{0}}, {
ip => $a->address,
forw => $a->address,
back => lc $_->ptrdname,
} for( $ptr_rr->answer );
}
}
}
}
else {
for my $mx( $rr->answer ) {
if( $mx->type eq 'CNAME' ) {
my $a_rr = $r->query( $mx->cname, 'A' );
if( !$a_rr ) {
push @{$res{-1}}, { ip => $mx->cname, forw => $r->
+errorstring, back => 'CNAME' };
}
else {
$_->type eq "A"
and push @{$res{-1}}, { ip => $mx->cname, forw
+ => $_->address, back => 'CNAME' }
for( $a_rr->answer );
}
next;
}
next unless $mx->type eq 'MX';
my $a_rr = $r->query( $mx->exchange, 'A' );
if( !$a_rr ) {
push @{$res{$mx->preference ? $mx->preference : 0}}, {
ip => $mx->exchange,
forw => $r->errorstring,
back => $r->errorstring,
};
next;
}
my @a;
for my $a( $a_rr->answer ) {
next unless $a->type eq "A";
my $ptr_rr = $r->query( join( '.', reverse( split /\./
+, $a->address )) . '.in-addr.arpa', 'PTR' );
if( !$ptr_rr ) {
push @{$res{$mx->preference}}, {
ip => $a->address,
forw => $mx->exchange,
back => $r->errorstring,
};
}
else {
$_->type eq 'PTR' and push @{$res{$mx->preference}
+}, {
ip => $a->address,
forw => lc $mx->exchange,
back => lc $_->ptrdname,
} for( $ptr_rr->answer );
}
}
}
}
for my $rank( sort { $a <=> $b } keys %res ) {
for my $host( sort {
# sort on IP addresses
(inet_aton($a->{ip}) ? inet_aton($a->{ip}) : $a->{ip}) cmp
(inet_aton($b->{ip}) ? inet_aton($b->{ip}) : $b->{ip})
||
# followed on whether the reverse resolution works
($a->{forw} eq $a->{back} ? 0 : 1) cmp ($b->{forw} eq $b->
+{back} ? 0 : 1)
||
# and the reverse lookups
$a->{back} cmp $b->{back}
} @{$res{$rank}} ) {
if( $host->{forw} eq $host->{back} ) {
printf( "%4d %-15s %s\n", $rank, $host->{ip}, $host->{
+forw} );
}
else {
printf( "%4d %-15s forw=%s back=%s\n", $rank, $host->{
+ip}, $host->{forw}, $host->{back} );
}
}
}
}
continue {
print "\n" if scalar @ARGV > 1;
}
=head1 NAME
mx - Mail Exchanger (MX) information about internet hosts
=head1 SYNOPSIS
B<mx> host [...]
=head1 DISCRIPTION
Look up the MX records of an internet host I<e.g.> C<example.com> or C
+<perl.com>. Take the
resulting records and look up the A records, in order to obtain the nu
+meric IP addresses
of the mail exchangers. Take the IP addresses, and perform reverse loo
+kups on them, to see
how these addresses resolve.
=head1 PREREQUISITES
This program depends on L<Net::DNS|Net::DNS>. It also depends on L<Soc
+ket|Socket> but this is usually
bundled with the standard distribution.
This program works correctly for Perl version 5.005_03 and beyond.
=head1 OPTIONS
=over 5
None.
=head1 EXAMPLES
These lookups were performed in October 2003. The exact results return
+ed may vary in the future.
C<mx perl.com>
0 199.45.135.9 forw=mail.perl.com back=perl.com
The domain C<[perl.com> has a single MX record, with a distance of 0.
+This points to a host named C<mail.perl.com>,
who has the IP address C<199.45.135.9>. This address resolves to C<per
+l.com>
C<mx mongueurs.net>
5 81.80.147.197 sferics.mongueurs.net
The domain C<mongueurs.net> has a single MX record with a distance of
+5. This points to a host with matching A and PTR records.
C<mx netcom.com>
5 mx07.netcom.com NXDOMAIN
5 207.69.200.17 forw=mx12.netcom.com back=wanamaker.mail.atl.eart
+hlink.net
5 207.69.200.30 forw=mx08.netcom.com back=strange.mail.mindspring
+.net
5 207.69.200.36 forw=mx09.netcom.com back=pickering.mail.mindspri
+ng.net
5 207.69.200.65 forw=mx05.netcom.com back=samuel.mail.atl.earthli
+nk.net
5 207.69.200.66 forw=mx02.netcom.com back=timothy.mail.atl.earthl
+ink.net
5 207.69.200.80 forw=mx03.netcom.com back=gideon.mail.atl.earthli
+nk.net
5 207.69.200.82 forw=mx11.netcom.com back=kendall.mail.mindspring
+.net
5 207.69.200.93 forw=mx04.netcom.com back=cave.mail.atl.earthlink
+.net
5 207.69.200.104 forw=mx14.netcom.com back=carlin.mail.atl.earthli
+nk.net
5 207.69.200.106 forw=mx00.netcom.com back=aaron.mail.atl.earthlin
+k.net
5 207.69.200.152 forw=mx01.netcom.com back=albert.mail.atl.earthli
+nk.net
5 207.69.200.154 forw=mx13.netcom.com back=watson.mail.atl.earthli
+nk.net
5 207.69.200.159 forw=mx06.netcom.com back=james.mail.atl.earthlin
+k.net
This domain has a number of mail exchangers, but one lacks any additio
+nal MX information.
C<mx goodaura.net>
MX lookup error: query timed out
A lookup error: query timed out
No C<MX> records were found for this domain. The script then tried to
+see
if an C<A> record existed for the domain but that failed too. You are
+unlikely
to be able exchange mail with this domain (and if you receive mail fro
+m them
you may as well route it to C</dev/null>). An additional check confirm
+s this:
C<host -t ns goodaura.net>
goodaura.net name server ns2.suspended-for.spam-and-abuse.com
goodaura.net name server ns1.suspended-for.spam-and-abuse.com
Hmmm.
C<mx starmedia.com>
10 207.153.203.64 forw=mx1.latinmail.com back=NXDOMAIN
A domain that has a mail exchanger, but the internet host in question
+does not have a PTR
record to turn the IP address back into a domain name. Sometimes the s
+ign of a spammer, sometimes
the sign of a clueless admin, or careless or unhelpful ISP.
C<mx whois.sc>
10 10.11.12.13 forw=localhost.com back=NXDOMAIN
A very interesting domain to receive mail from. You are unlikely to be
+ able to reply.
=head1 SEE ALSO
L<Net::DMS>
=head1 BUGS
I once came across a domain that uses CNAMEs for MX records. Ewww! Nau
+ghty! The code deals with this,
but I have lost track of the domain in question, so I can't show an ex
+ample of that. Suggestions
accepted.
=head1 COPYRIGHT
Copyright 2003 David Landgren.
This script is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=head1 AUTHOR
David Landgren
aka grinder on perlmonks
join(chr(64) => qw[david landgren]) . q{.net}
=cut
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.