Reversing the IP address in Linux using perl - perl

I have a job where I need to do nslookup on an IP address. If it matches then I need to print the name of the host. The problem is that the IP address comes reversed when running the command.
nslookup 10.11.12.13
13.12.11.10.in-addr.arpa
I tried to use reverse but that reversed everything which is not what I want.
my $ip = '13.12.11.10';
$result = reverse($ip);
print $result;
which then prints 01.11.21.31
I do not want to reverse everything, just the full numbers.
Please can someone help?

Simply split the IP address on .s using split, reverse the resulting array, then rejoin it:
join(".", reverse(split(/\./, $ip)))
This will give you the "reversed" IP address, which you can then compare to the nslookup result.

So what we need to do is to just split the actual address, reorder them in reverse and then match the IP to the reversed IP.
use strict;
use warnings;
my $ipaddress = '10.11.12.13';
my #ip = split /\./,$ipaddress; #split the IP by .
my $sserddapi = "$ip[3].$ip[2].$ip[1].$ip[0]"; #reverse it
my #lookup = `nslookup $ipaddress`; #do the match
$lookup[3] =~ s/\s+//g; #remove all whitespace
my #device = split /=/, $lookup[3]; #get the hostname
if ($lookup[3] =~ /^$sserddapi/) { #see if it matches
$lookup[3] =~ s/$sserddapi.in-addr.arpaname=//g; #Remove the unwanted stuff
print "$ipaddress = $lookup[3]\n"; #print the result
}

Related

the first IP of the next block with Net::IP

i am trying to use Net::IP module from cpan
my $block = new Net::IP('194.158.1.0/24');
$block->ip() # get the first ip on the block
$block->last_ip() #get the last ip on the block
how can i increment the last ip , as i want to know the first ip on the next block
i tried something like :
++$block->last_ip()
but this is not working any idea?
You can't increment the last ip as mentioned in Net::IP document.
The ++ operator returns undef when the last address of the range is reached.
The most simple way is to convert the last ip into integer, increment, then create a brand new Net::IP object from it.
#!/usr/bin/perl
use strict;
use warnings;
use Net::IP qw(ip_inttobin ip_bintoip);
my $block = Net::IP->new('194.158.1.0/24');
my $next_bin = ip_inttobin($block->last_int + 1, $block->version);
my $next = Net::IP->new(ip_bintoip($next_bin, $block->version));
print $next->ip;

How to take IP address as input

How to take IP address as input and use it in the Xpath expressions?
When i am using it as a static input like $iptext = '10.109.25.1'; it is working fine but when i am getting the input from user i am getting error
#my $ip = <>;
#my $iptext=$ip.'';
my $query = "//nodeA/nodeB[nodeC/text() = '$iptext']/../NodeD/Name/text()";
I assume what you are forgetting is that input read with <> or <STDIN> come with newlines:
chomp(my $ip = <>); # newline removed
Documentation for chomp here.
If you do not remove the newline, your $query string will contain that newline, which I assume you pass to Xpath or some such:
xpath //nodeA/nodeB[nodeC/text() = '10.109.25.1
']/../NodeD/Name/text()
And of course that does not work.

Pattern matching an array with specific start and end words/characters

Have some output in an array which I am trying to pull details from where the output begins with a specific word and ends with a specific word/character. This output is then to be printed to the screen.
The output in the array which I am working with is:
router rip
version 2
redistribute bgp 45134 metric 3
passive-interface Serial1/3:1.333
passive-interface Serial3/1:3.333
passive-interface Serial3/1:5.333
passive-interface Serial3/2:1.333
passive-interface Serial3/4:1.333
passive-interface Serial3/4:17.333
passive-interface Serial6/1:1.333
no auto-summary
!
address-family ipv4 vrf TestVRF-0001
redistribute bgp 45134 metric 3
network 10.0.0.0
no auto-summary
version 2
exit-address-family
!
The perl code I have generated so far is below:
elsif ( $action eq "show_vrf1" ) {
my $cmd = "show run | begin router rip";
my #lines = $s->cmd(String => $cmd,
Prompt => "/$enableprompt/",
Timeout => 10);
foreach my $line (#lines) {
if(/address-family ipv4 vrf TestVRF-0001.*?!/){
$result=$1;
print $result;
}
}
}
Which I am wanting to only pull the below out of the array:
address-family ipv4 vrf TestVRF-0001
redistribute bgp 45134 metric 3
network 10.0.0.0
no auto-summary
version 2
exit-address-family
!
For some reason when I run the script, I just get a blank screen with no data pulled from the array.
if($line =~ /address-family ipv4 vrf TestVRF-0001.*?!/){
instead of
if(/address-family ipv4 vrf TestVRF-0001.*?!/){
?
You match $line against the regular expression. The output you expect is not on one line, so no line can match it.
You can store the whole multiline string in a scalar:
my $output = join q(), $s->cmd(...);
And then, you can retrieve the output, if you use parentheses to really capture a part of the string:
if ($output =~ /(address-family ipv4 vrf TestVRF-0001.*?!)/s) {
my $result = $1;
Note that the /s modifier is needed to make dot match a newline, which it normaly does not.

Getting all hostnames from IP address in Perl

I'm trying to find a way to get all hostnames that resolve to an IP address.
The gethostbyaddr function appears to only retrieve the first record from DNS (no matter if it's in scalar or list context).
Example:
my $hostname = gethostbyaddr(inet_aton($ip_to_check), AF_INET);
$print($hostname); //output: joe.example.com
my #hostnames = gethostbyaddr(inet_aton($ip_to_check), AF_INET);
foreach my $hostname (#hostnames){
print "(", join(',',#hostnames), ")"; //output: (joe.example.com,,2,4,?)
}
From the terminal:
$ host 192.168.1.5
5.1.168.192.in-addr.arpa domain name pointer joe.example.com.
5.1.168.192.in-addr.arpa domain name pointer john.example.com.
I've heard that Net::DNS is a little more robust, but I haven't had any luck getting that to pull all entries as well.
I used a combination of answers given here and elsewhere on stack overflow to find the answer I was looking for.
# create new Resolver Object
my $res = Net::DNS::Resolver->new;
# change IP from 192.168.1.15 to 15.1.168.192.in-addr.arpa for searching
my $target_IP = join('.', reverse split(/\./, $ip_to_check)).".in-addr.arpa";
# query DNS
my $query = $res->query("$target_IP", "PTR");
# if a result is found
if ($query){
print("Resolves to:\n");
# for every result, print the IP address
foreach my $rr ($query->answer){
# show all unless the type is PTR (pointer to a canonical name)
next unless $rr->type eq "PTR";
# remove the period at the end
printf(substr($rr->rdatastr, 0, -1));
}
}
The gethostby... interface is quite old and clunky, being defined back in primeval times before Perl got references and pretensions to OO. And it doesn't work the way you're trying to use it. When used in list context, it returns the primary name as the first element and a space-separated(!) list of aliases as the second:
my ($hostname, $aliases) = gethostbyaddr($addr, AF_INET);
my #hostname = ($hostname, split ' ', $aliases);
say join ' ', #hostname;
Now that's the theory; I didn't locate any IP addresses with multiple PTR records offhand, so I can't test if gethostbyaddr will actually return them -- it probably depends on your underlying C runtime as well -- but it does work if you use gethostbyname with a CNAMEd name, for instance.
Here's a small program I use to lookup all PTR records for a netmask (for example 192.0.2.0/28 ) when doing abuse tracking tasks. It sends up to 15 queries a second and when they are all sent then starts reading the responses (so it'd need a little work to function properly for bigger net blocks).
#!/usr/bin/env perl
use strict;
use warnings;
use Net::Netmask;
use Net::DNS;
#ARGV or die "$0 ip/cidr\n";
my $block = Net::Netmask->new(shift);
my $res = Net::DNS::Resolver->new;
my %sockets;
my $i = 0;
for my $i (1 .. $block->size - 1) {
my $ip = $block->nth($i);
my $reverse_ip = join ".", reverse split m/\./, $ip;
$reverse_ip .= ".in-addr.arpa";
#print "$ip\n";
my $bgsock = $res->bgsend($reverse_ip, 'PTR');
$sockets{$ip} = $bgsock;
sleep 1 unless $i % 15;
}
$i = 0;
for my $i (1 .. $block->size - 1) {
my $ip = $block->nth($i);
my $socket = $sockets{$ip};
my $wait = 0;
until ($res->bgisready($socket)) {
print "waiting for $ip\n" if $wait > 0;
sleep 1 + $wait;
$wait++;
}
my $packet = $res->bgread($socket);
my #rr = $packet->answer;
printf "%-15s %s\n", $ip, $res->errorstring
unless #rr;
for my $rr (#rr) {
printf "%-15s %s\n", $ip, $rr->string;
}
}
I don't think this is a well-formed problem statement. In the general case, there's a nearly infinite number of DNS names that could resolve to any IP address, even unknown to the party that holds the address. Reverse-lookups are fundamentally unreliable, and are not capable of answering the question the poster would like, since all names for an IP do not need to be in the visible reverse map.
The first answer, which enumerates the reverse map, is the best one can do, but it will miss any names that have not been entered in the map.
This is what I have used:
sub getauthoritivename
{
my ($printerdns)=#_;
my $res = Net::DNS::Resolver->new(searchlist=>$config->{searchlist});
my $query = $res->search($printerdns);
if ($query)
{
foreach my $rr ($query->answer)
{
next unless $rr->type eq "A";
print $rr->name;
}
}
else
{
warn "query failed: ", $res->errorstring, "\n";
return 0;
}
}
As long as $rr->name finds names, it keeps adding them.

Reverse DNS lookup in perl

How do I perform a reverse DNS lookup, that is how do I resolve an IP address to its DNS hostname in Perl?
If you need more detailed DNS info use the Net::DNS module, here is an example:
use Net::DNS;
my $res = Net::DNS::Resolver->new;
# create the reverse lookup DNS name (note that the octets in the IP address need to be reversed).
my $IP = "209.85.173.103";
my $target_IP = join('.', reverse split(/\./, $IP)).".in-addr.arpa";
my $query = $res->query("$target_IP", "PTR");
if ($query) {
foreach my $rr ($query->answer) {
next unless $rr->type eq "PTR";
print $rr->rdatastr, "\n";
}
} else {
warn "query failed: ", $res->errorstring, "\n";
}
Original Source EliteHackers.info, more details there as well.
gethostbyaddr and similar calls. See http://perldoc.perl.org/functions/gethostbyaddr.html
use Socket;
$iaddr = inet_aton("127.0.0.1"); # or whatever address
$name = gethostbyaddr($iaddr, AF_INET);
perl -MSocket -E 'say scalar gethostbyaddr(inet_aton("69.89.27.250"), AF_INET)'
Returns: Can't find string terminator "'" anywhere before EOF at -e line 1.
perl -MSocket -E "say scalar gethostbyaddr(inet_aton(\"69.89.27.250\"), AF_INET)"
Returns: box250.bluehost.com
I have to change the line to use double quotes and then escape out the quotes around the IP address
one-liner:
perl -MSocket -E 'say scalar gethostbyaddr(inet_aton("79.81.152.79"), AF_INET)'
There may be an easier way, but for IPv4, if you can perform ordinary DNS lookups, you can always construct the reverse query yourself. For the IPv4 address A.B.C.D, look up any PTR records at D.C.B.A.in-addr.arpa. For IPv6, you take the 128 hex nibbles and flip them around and append ipv6.arpa. and do the same thing.
If gethostbyaddr doesn't fit your needs, Net::DNS is more flexible.
This might be useful...
$ip = "XXX.XXX.XXX.XXX" # IPV4 address.
my #numbers = split (/\./, $ip);
if (scalar(#numbers) != 4)
{
print "$ip is not a valid IP address.\n";
next;
}
my $ip_addr = pack("C4", #numbers);
# First element of the array returned by gethostbyaddr is host name.
my ($name) = (gethostbyaddr($ip_addr, 2))[0];