How do I create valid IP ranges given an IP address and Subnet mask in Perl? - perl

How do I create valid IP ranges given an IP address and Subnet mask in Perl? I understand the concept of generating the IP ranges but need help writing in Perl. For example, if I AND the IP address and subnet mask I get the subnet number. Adding 1 to this number should give me the first valid IP. If I invert the subnet mask and OR with the subnet number, I should get the broadcast address. Subtracting 1 from it should give the last valid IP address.

See perldoc perlop for information about the bitwise operators (they are the same as in most other C-like languages):
& is bitwise AND
| is bitwise OR
^ is bitwise XOR
>> is right-shift
<< is left-shift
However, if you really want to do some work with network blocks and IP addresses (as opposed to simply answering a homework assignment - although I'm curious what course you'd be taking that used Perl), you can avoid reinventing the wheel by turning to CPAN:
Net::Netmask
Network::IPv4Addr

If you want to play with bitwise operators yourself, it becomes this:
#!/usr/bin/perl
use strict;
use warnings;
use Socket;
my $ip_address = '192.168.0.15';
my $netmask = 28;
my $ip_address_binary = inet_aton( $ip_address );
my $netmask_binary = ~pack("N", (2**(32-$netmask))-1);
my $network_address = inet_ntoa( $ip_address_binary & $netmask_binary );
my $first_valid = inet_ntoa( pack( 'N', unpack('N', $ip_address_binary & $netmask_binary ) + 1 ));
my $last_valid = inet_ntoa( pack( 'N', unpack('N', $ip_address_binary | ~$netmask_binary ) - 1 ));
my $broadcast_address = inet_ntoa( $ip_address_binary | ~$netmask_binary );
print $network_address, "\n";
print $first_valid, "\n";
print $last_valid, "\n";
print $broadcast_address, "\n";
exit;
With Net::Netmask it's easier to understand:
#!/usr/bin/perl
use strict;
use warnings;
use Net::Netmask;
my $ip_address = '192.168.0.15';
my $netmask = 28;
my $block = Net::Netmask->new( "$ip_address/$netmask" );
my $network_address = $block->base();
my $first_valid = $block->nth(1);
my $last_valid = $block->nth( $block->size - 2 );
my $broadcast_address = $block->broadcast();
print $network_address, "\n";
print $first_valid, "\n";
print $last_valid, "\n";
print $broadcast_address, "\n";
exit;

Quick & dirty way to find the subnet mask:
use Socket;
my $subnet_mask = inet_ntoa(inet_aton($ip_str) & inet_aton($mask_str)):

Following Snippet can help you to find ip address and subnet mask on rhel7
my $ip=`hostname -i`;
my $subnet = `ip addr show|grep -i ".*${ip}/.*"|tail -1|cut -d "/" -f2|cut -d " " -f1`;

Related

How to effectively use the BitVector Module In Perl to find the XOR of two numbers?

I am having trouble figuring out how to effectively use the BitVector module in Perl to find the Exclusive Or (XOR) of two numbers in hexadecimal form.
This is my whole code:
use Bit::Vector;
$bits = Bit::Vector->Word_Bits(); # bits in a machine word
print "This program will take two numbers and will return the XOR of the two numbers.\n";
print "Enter the first number in hexadecimal form:\n";
$firstHexNumber = <STDIN>;
$vector = Bit::Vector->new($bits, $firstHexNumber); # bit vector constructor
print "Enter the second number in hexadecimal form:\n";
$secondHexNumber = <STDIN>;
$vector2 = Bit::Vector->new($bits, $secondHexNumber); # bit vector constructor
$vector3 = Bit::Vector->new($bits); # bit vector constructor
$vector3->Xor($vector,$vector2);
print $vector3;
I am not sure if I am doing the syntax right for the BitVector module.
If I try to run it, I get an output like this.
Output
When I input 1 and 16 as my arguments, the output is supposed to be 17.
Please help me see what's wrong with my code to get the output correct.
Thank you.
No need for a module.
# Make sure the bitwise feature wasn't activated (e.g. by `use 5.022;`)
no if $] >= 5.022, feature => qw( bitwise );
my $hex1 = '012345';
my $hex2 = '000AAA';
my $hex_xor = unpack('H*', pack('H*', $hex1) ^ pack('H*', $hex2) );
say $hex_xor; # 0129ef
or (5.22+)
# Safe. Feature accepted without change in 5.28.
use experimental qw( bitwise );
my $hex1 = '012345';
my $hex2 = '000AAA';
my $hex_xor = unpack('H*', pack('H*', $hex1) ^. pack('H*', $hex2) );
say $hex_xor; # 0129ef
or (5.28+)
use feature qw( bitwise ); # Or: use 5.028; # Or: use v5.28;
my $hex1 = '012345';
my $hex2 = '000AAA';
my $hex_xor = unpack('H*', pack('H*', $hex1) ^. pack('H*', $hex2) );
say $hex_xor; # 0129ef
These solutions work with numbers of arbitrary length, which is why I assume Bit::Vector was selected for use. (Just pad the numbers so that both have the same length if necessary, or Perl will effectively right-pad with zeroes.)
You can use new_Hex() and to_Hex():
use strict;
use warnings;
use Bit::Vector;
my $bits = Bit::Vector->Word_Bits(); # bits in a machine word
my $firstHexNumber = "1";
my $vector = Bit::Vector->new_Hex($bits, $firstHexNumber);
my $secondHexNumber = "17";
my $vector2 = Bit::Vector->new_Hex($bits, $secondHexNumber);
my $vector3 = Bit::Vector->new($bits); # bit vector constructor
$vector3->Xor($vector,$vector2);
print $vector3->to_Hex;
Output:
0000000000000016

Random generation of ipv6 address in perl

I have to randomly generate IPV6 address using Perl.
Please help me.
It's unclear whether you want just any 128-bit pattern, or if you need to skip reserved IP addresses
This program solves the most basic interpretation
use strict;
use warnings 'all';
use feature 'say';
my $ipv6 = join ':', map { sprintf '%04X', rand 0x10000 } 1 .. 8;
say $ipv6;
output example
FDFE:5E91:137C:8482:DCB2:03D9:2C1D:8A75
If you need a string that contains some ipv6-address, you can use this code:
my $addr_str = '';
$addr_str .= (($_) ? ':' : '') . sprintf "%04x", rand 0xFFFF + 1 for 0..7;
# $addr_str =~ s/0000//g; # to reduce when zero-block appears

calculate network address with perl

How can I get the class c network address from an ipv4 address in perl?
I tried already this:
my $ipObject = new Net::IP("$ip/24");
return $ipObject->prefix();
leads to
Can't call method "prefix" on an undefined value at /usr/share/awstats/plugins/geoipfree.pm line 101, line 1.
Net::IP->new returns undef on error. The error message can be obtained by calling Net::IP::Error().
my $ipObject = Net::IP->new("$ip/24")
or die(Net::IP::Error());
It probably failed because you specified a prefix without using the base address of the subnet. For example, it will fail for 10.1.2.3/24, but not for 10.1.2.0/24.
$ perl -E'
use Net::IP;
Net::IP->new("10.1.2.3/24")
or die(Net::IP::Error());
'
Invalid prefix 00001010000000010000001000000011/24 at -e line 4.
Since you are trying to find the base address of the subnet, it means you'll need to omit the /24 and apply it as a mask to the address. However, I don't see any way of applying a mask to an Net::IP object. In fact, I don't see any way of using Net::IP to obtain the base address of a subnet at all.
On the other hand, it's trivial using NetAddr::IP.
$ perl -E'
use NetAddr::IP;
say NetAddr::IP->new("10.1.2.3/24")->network->addr;
'
10.1.2.0
Of course, you could also use
( my $network = $ip ) =~ s/\.\d+\z/.0/;
ikegami has given you a solution, but I'll point out something. IP addresses are simply base 256 numbers. CIDR involves 'bit masks' which are applied to an IP to differentiate the network address from the host address.
So given an IP of 10.11.12.13 - that's a representation of:
10 * 256 * 256 * 256
+ 11 * 256 * 256
+ 12 * 256
+ 13
Or 168496141
When working with bitmasks, you logically 'and' the mask to get the 'network part'. A /24 is 24 bits (out of 32):
11111111111111111111111100000000
So if you take the IP above, bitwise and to get your network address. A bit like this:
use strict;
use warnings;
my #IP = qw ( 10 11 12 13 );
print join (".", #IP),"\n";
my $DWORD = 0;
foreach my $octet ( #IP ) {
$DWORD *= 256;
$DWORD += $octet;
}
print "Decimal of IP:",$DWORD,"\n";
my $host_mask = 2 ** 8;
my $netmask = 2 ** 32;
$netmask -= $host_mask;
$host_mask -= 1;
printf ( "%032b\n", $netmask);
printf ( "%032b\n", $host_mask);
my $network = $DWORD & $netmask;
my $host = $DWORD & $host_mask;
#and convert back to IP:
my #net;
while ( $network > 0 ) {
my $octet = $network % 256;
$network -= $octet;
$network /= 256;
unshift ( #net, $octet );
}
print "Network part:", join ( ".", #net ),"\n";
my #host_ip;
while ( $host > 0 ) {
my $octet = $host % 256;
$host -= $octet;
$host /= 256;
unshift ( #host_ip, $octet );
}
print "Host Part:", join ( ".", #host_ip ),"\n";
Not particularly complicated, but hopefully illustrates why the 'right' answer is 'use a module' - the conversions back and forth can be error prone.

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];