views:

577

answers:

6

I have a local DNS script that I've inherited from a past employee that needs to compare some values to see if they match specific MX records and IP addresses.

The MX part I have down okay:

120 $doug = doug.local
139 if ($mx_record =~ /$doug/) {
140         print ("Mail on doug.\n");
141 }
142         else {
143                 print ("Not on doug.\n");
144 }

$mx_record is a line from an mx query that would look like this:

thomas.            302     IN      MX      10 doug.local.

Now I need to see if the A record matches.

The $a_record variable from the query looks like this.

thomas.            300     IN      A       10.0.0.47

How do I do a conditional statement to match an IP address?

I need to define the IP in a variable and then see if the $a_record variable contains that defined IP.

+1  A: 

This will match a entry that looks like a ip address, but will also match 999.999.999.999. Make sure to validate the matched address before using it.

if ($mx_record =~ /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) {...}
Eric Strom
This is the right approach...as long as each part of the quad is validated to be in the 0-25 range.
semiuseless
+1  A: 

try this:

if ($mx_record =~ /A\w+(<record_ip>(?:/[0-9]{1,3}.){4})/ {
   print $record_ip
}

This will check for A followed by whitespace, followed by a dotted quad. The quad is saved in the variable $record_ip

Andrew
Why are you restricting the pattern to digits that are 0-5? How could that match a private in the 192.168.x.x space?
semiuseless
... becuase I suddenly started thinking about old (non-CIDR netmasks... will fix.
Andrew
Why would you post code that you haven't tested? You want \s+, not \w+. You should quote the \., or it matches any character. And the regex looks for addresses with a trailing dot. The only reason this code might have worked is that the \n at the end of the line got parsed as part of the address. Try instead: if (/A\s+((?:[0-9]{1,3}\.){3}[0-9]{1,3})/)
+2  A: 

I'm sure there's a better way, but this will get you pretty close:

if($a_record =~ /((?:\d{1,3}\.){3}\d{1,3})/) {
     warn "IP was: $1";
}

# IP was: 10.0.0.47

This matches 10.0.0. then the final 47.

lost-theory
A: 
my $a_record = 'thomas.            300     IN      A       10.0.0.47';
my $ip = '10.0.0.47';

if ($a_record =~ /\b(\Q$ip\E)$/) {
    print "Matches: $1\n";
}
Leonardo Herrera
A: 

Since the IP address might not be in its most compact decimal notation, I suggest you should use the inet_aton() function from the Socket module:

use Socket;

my $ip_to_match = inet_aton('10.0.0.47');
...
if (inet_aton($field) == $ip_to_match) {
    ...
}

NB: assumes that the DNS record has already been split down to its component parts

Alnitak
+3  A: 

If you just want to match an IPv4 regex, use Regexp::Common::net.

Rather than running a regex over the whole line, its safer to tokenize them and match individual parts as necessary.

use strict;
use warnings;

use Data::Dumper;

sub parse_record {
    my $line = shift;

    # special rules for parsing different types
    my %More4Type = (
        MX      => sub { return( priority => $_[0], provider => $_[1] ) },
        default => sub { return( provider => $_[0] ) }
    );

    my(%record, @more);
    (@record{qw(host uhh class type)}, @more) = split /\s+/, $line;
    my $more_func = $More4Type{$record{type}} || $More4Type{default};
    %record = (%record, $more_func->(@more));

    return \%record;
}

while(my $line = <DATA>) {
    print Dumper parse_record($line);
}

__END__
thomas.            302     IN      MX      10 doug.local.
thomas.            300     IN      A       10.0.0.47
google.com.     24103   IN NS ns2.google.com.

Now that you've parsed the line, its a simple matter of looking at $record{type} and $record{provider} or whatever you need. For a little bit of effort this is far more flexible and less bug prone.

Though there's probably something on CPAN to do the parsing for you.

Schwern