Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

mx - Mail Exchanger (MX) information about internet hosts

by grinder (Bishop)
on Oct 08, 2003 at 17:15 UTC ( #297667=sourcecode: print w/ replies, xml ) Need Help??

Category: E-Mail Programs
Author/Contact Info /msg me
Description: A tool to find out far more than you ever wanted to know about MX records. In case you didn't know, MX records are a certain type of record used to discover which host will accept SMTP mail for a given domain.
#! /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

Comment on mx - Mail Exchanger (MX) information about internet hosts
Download Code

Back to Code Catacombs

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: sourcecode [id://297667]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (8)
As of 2015-07-03 08:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (49 votes), past polls