How can I generate a range of IP addresses in Perl? - perl

I need to generate a list of IP-addresses (IPv4) in Perl. I have start and end addresses, for example 1.1.1.1 and 1.10.20.30. How can I print all the addresses inbetween?

Use Net::IP. From the CPAN documentation:
my $ip = new Net::IP ('195.45.6.7 - 195.45.6.19') || die;
# Loop
do {
print $ip->ip(), "\n";
} while (++$ip);
This approach is more flexible because Net::IP accepts CIDR notation e.g. 193.0.1/24 and also supports IPv6.
Edit: if you are working with netblocks specifically, you might investigate Net::Netmask.

Use Net::IP's looping feature:
The + operator is overloaded in order to allow looping though a whole range of IP addresses:

It's all in how you code it. This is the fastest way I know.
my $start = 0x010101; # 1.1.1
my $end = 0x0a141e; # 10.20.30
for my $ip ( $start..$end ) {
my #ip = ( $ip >> 16 & 0xff
, $ip >> 8 & 0xff
, $ip & 0xff
);
print join( '.', 1, #ip ), "\n";
}

TMTOWTDI:
sub inc_ip { $_[0] = pack "N", 1 + unpack "N", $_[0] }
my $start = 1.1.1.1;
my $end = 1.10.20.30;
for ( $ip = $start; $ip le $end; inc_ip($ip) ) {
printf "%vd\n", $ip;
}

# We can use below code to generate IP range
use warnings;
use strict;
my $startIp = $ARGV[0];
my $endIp = $ARGV[1];
sub range {
my (#ip,#newIp,$i,$newIp,$j,$k,$l,$fh);
my ($j1,$k1,$l1);
open($fh,">","ip.txt") or die "could not open the file $!";
#ip = split(/\./,$startIp);
for($i=$ip[0];$i<=255;$i++) {
for($j=$ip[1];$j<=255;$j++) {
$ip[1]=0 if($j == 255);
for($k=$ip[2];$k<=255;$k++) {
$ip[2]=0 if($k == 255);
for($l=$ip[3];$l<=255;$l++) {
$ip[3]=0 if($l == 255);
#newIp = $newIp = join('.',$i,$j,$k,$l);
print $fh "$newIp \n";
exit if ($newIp eq $endIp);
}
}
}
}
}
range ($startIp, $endIp);

Related

I need to increment an IP address within a given range using Perl

For example
my $starting_ip = '1.2.3.4';
my $ending_ip = '1.3.0.5';
my output will be
1.2.3
1.2.4
1.2.5
..
..
1.2.255
1.3.0
I tried so far:
use Socket;
my $startIp = $ARGV[0];
my $endIp = $ARGV[1];
my $start_address = unpack 'N', inet_aton( $startIp );
my $finish_address = unpack 'N', inet_aton( $endIp );
my #f = split( '\.', $endIp );
my $last_ip = join( '.', $f[0], $f[1], $f[2] );
for ( my $address = $start_address;
$address <= $finish_address;
$address = $‌​address + 255 ) {
my $new_ip = inet_ntoa( pack 'N', $address );
my #a = split( '\.', $new_ip );
$new_ip = join( '.', $a[0], $a[1], $a[2] );
print "$new_ip\n";
}
print "$last_ip\n";
There is no need to consider the last portion of the IP address.
Like so?
sub ip_addr { $_[0] = pack "N", 1 + unpack "N", $_[0] }
my $starting_ip = 1.2.4.4;
my $ending_ip = 1.3.0.5;
for ( $addresses = $starting_ip; $addresses le $ending_ip; ip_addr($addresses) ) {
printf "%vd\n", $addresses;
}
Though there are many other ways, I think an even better option would be to use Net::IP

Strawberry Perl: "out of memory!"

trying to run the following code:
$combs = combinations(\#set,$k);
while (my $c = $combs->next)
{
$nrc=1;
}
Gives me "out of memory!" when I hit Ctrl+C (because its taking too long and it should not) if I pass a set from, for example, (0..450) and numbers to combine ($k) of 6. This issue does not occur with, lets say, a set of 0..45 and $k=6.
Note that the while loop seems to do nothing, in the original script it printed out the combination and incremented a counter that will hold the total number of combinations. But since I was not sure what the problem was, I decided to eliminate that.
I've read the Algorithm:Combinatorics on CPAN and it states that memory usage is minimal, so I don't know what's happening.
I am using Strawberry Perl 32bit on a Windows 10 machine.
Thanks.
--------------------- COMPLETE CODE
#!/usr/bin/perl
use List::MoreUtils "uniq";
use Algorithm::Combinatorics "combinations";
my $argc = $#ARGV+1;
my #set;
if ($argc == 0)
{
print STDERR "Valor minimo de rango: "; # range min
my $minrange = int <STDIN>;
print STDERR "Valor maximo de rango: "; #range max
my $maxrange = int <STDIN>;
#set = uniq sort { $a <=> $b }($minrange...$maxrange);
}
elsif ($argc == 1)
{
open(SETFROMFILE,"<$ARGV[0]") or die "No se puedo abrir el fichero, $!";
chomp(#set = <SETFROMFILE>);
close(SETFROMFILE);
#set = uniq sort { $a <=> $b } #set;
}
else
{
print STDERR "Uso: $0 [file]\n";
exit;
}
my $nrc = 0;
print STDERR "\n";
print STDERR "Numeros a combinar: "; # get subset
my $k = <STDIN>;
if ($k == 0) { exit; }
$combs = combinations(\#set,$k);
print STDERR "\n";
while (my $c = $combs->next)
{
print join(";",#$c) . "\n";
$nrc++;
}
print STDERR "\n";
print STDERR "numero total de combinaciones: $nrc\n";
It works for me.
use strict;
use warnings;
use Algorithm::Combinatorics qw( combinations );
sub show_mem { system('ps', '--no-heading', '-o', 'rss', $$); }
my #set = (0..450);
my $k = 6;
my $count = 0;
#show_mem();
my $combs = combinations(\#set, $k);
#show_mem();
while (my $c = $combs->next) {
++$count;
if (($count % 100_000) == 0) {
print("$count\n");
#show_mem();
}
}
Output:
784
784
100000
776
200000
784
300000
788
400000
776
500000
780
600000
784
700000
768
800000
784
900000
784
1000000
776
...
Of course, it will take forever to go through all C(451, 6) = 11,303,769,578,640 combinations! (We're talking about 251 days on my machine[1].)
(Note that 11,303,769,578,640 is too large for a 32-bit integer. Fortunately, Perl will switching to using a double-precision floating-point number, and those are large enough to hold that all numbers up to and including that one.)
By the way, if you just need the number of combinations, you can use
my $count = 1; $count *= ( #set - $_ + 1 ) / $_ for 1..$k;
How I timed it:
use Algorithm::Combinatorics qw( combinations );
use Time::HiRes qw( time );
my #set = (0..450);
my $k = 6;
my $count = 0;
my $combs = combinations(\#set, $k);
my $s = time;
while (my $c = $combs->next) {
++$count;
last if $count == 1_000_000;
}
my $e = time;
print($e-$s, "\n");
There are 11.1 trillion combinations of six items out of 450. I'm not surprised it ran out of memory!

Exclude Range of Numbers from a given Range (Port Range)

I am trying to make the port range of 1-65535 allow the user to exclude certain ranges of ports from it
The input will be a comma separated single or range of port numbers, such as:
1-2,3-4,4-5,5-6,7-8,9-10,1-100
The output should be:
101-65535
I have written code to cover many cases, but for some reason the code I currently have doesn't handle the last exclusion 1-100 because 9 is the current minimum port number
Here is my code:
my #ranges;
push #ranges, '1-65535';
my $bFound = 0;
do {
$bFound = 0;
foreach my $ptrExclusion (#exclusions) {
my %exclusion = %{$ptrExclusion};
print STDERR "handling exclusion # 7964: ".Dumper(\%exclusion);
my $currentPos = 0;
foreach my $range (#ranges) {
$currentPos++;
if ($range =~ /([0-9]+)-([0-9]+)/) {
my $firstPortInRange = $1;
my $secondPortInRange = $2;
if ($secondPortInRange == $exclusion{first} and
$exclusion{second} == $exclusion{first}) {
$bFound = 1;
my #newranges;
if ($exclusion{first} - 1 > 0) {
push #newranges, "$firstPortInRange-".(sprintf("%d", $exclusion{first} - 1));
} else { # Handle port "1"
### Don't put anything, we are excluded from adding this
}
if ($currentPos > 1) {
unshift #newranges, $ranges[1..($currentPos-1)];
}
if ($currentPos + 1 < scalar(#ranges)) {
push #newranges, $ranges[($currentPos+1) .. scalar(#ranges)];
}
print STDERR "newranges # 7985: ".Dumper(\#newranges);
#ranges = #newranges;
last;
}
if ($firstPortInRange == $exclusion{first} and
$exclusion{second} == $exclusion{first}) {
$bFound = 1;
my #newranges;
if ($exclusion{second} + 1 <= 65535) {
push #newranges, (sprintf("%d", $exclusion{second} + 1))."-$secondPortInRange";
} else { # Handle port 65535
#### Don't put anything, we are excluded from adding this
}
if ($currentPos > 1) {
unshift #newranges, $ranges[1..($currentPos-1)];
}
if ($currentPos + 1 < scalar(#ranges)) {
push #newranges, $ranges[($currentPos+1) .. scalar(#ranges)];
}
print STDERR "newranges # 8005: ".Dumper(\#newranges);
#ranges = #newranges;
last;
}
if ($firstPortInRange < $exclusion{first} and
$secondPortInRange > $exclusion{second} # An exclusion is between the ranges we currently have, this doesn't include "hits" on the exact port number, i.e. excluding port 1 and port 65535
) {
print STDERR "exclusion matched # 8022\n";
$bFound = 1;
#printf (STDERR "currentPos # 7973: %d\n", $currentPos);
my #newranges;
push #newranges, "$firstPortInRange-".(sprintf("%d", $exclusion{first} - 1));
push #newranges, (sprintf("%d", $exclusion{second} + 1))."-$secondPortInRange";
if ($currentPos > 1) {
unshift #newranges, $ranges[1..($currentPos-1)];
}
if ($currentPos + 1 < scalar(#ranges)) {
push #newranges, $ranges[($currentPos+1) .. scalar(#ranges)];
}
print STDERR "newranges # 8026: ".Dumper(\#newranges);
#ranges = #newranges;
last;
}
if ($firstPortInRange >= $exclusion{first} and
$firstPortInRange < $exclusion{second} and
$secondPortInRange <= $exclusion{second} and
$secondPortInRange > $exclusion{first} # An exclusion is holding our range inside it
) {
print STDERR "exclusion matched # 8045\n";
$bFound = 1;
splice(#ranges, $currentPos-1, 1); # -1 as our index starts from 1, while #ranges index starts at 0
print STDERR "ranges # 8051: ".Dumper(\#ranges);
last;
}
}
}
if ($bFound) {
last;
}
}
} while ($bFound);
print STDERR "ranges # 7980: ".join(", ", #ranges). "\n";
#exclusions has hash elements under it with a first and second values to them which designate port A and port B (lower range and top range) their value can match if the exclusion is for 1 port.
There are a number of modules for working with sets that will make your life easier. I would recommend Set::IntSpan::Fast:
use strict;
use warnings;
use 5.010;
use Set::IntSpan::Fast;
my $ports = Set::IntSpan::Fast->new('1-65535');
my $exclude = Set::IntSpan::Fast->new('1-2,3-4,4-5,5-6,7-8,9-10,1-100');
say $ports->diff($exclude)->as_string;
Output
101-65535
The Range::Object set of modules is very comprehensive, and Range::Object::Serial does exactly what you're looking for out of the box
This short program demonstrates
use strict;
use warnings;
use v5.10;
use Range::Object::Serial;
my $range = Range::Object::Serial->new('1-65535');
say scalar $range->collapsed;
$range->remove('1-2,3-4,4-5,5-6,7-8,9-10,1-100');
say scalar $range->collapsed;
output
1-65535
101-65535
Here is an attempt, without using a library, instead just using an array (since it is not very large).
#!/usr/bin/env perl
use warnings;
use strict;
my #port_range = ( 1, 65535 );
my #ports = map { 1 } ( 0 .. $port_range[1] );
my $exclusions = '1-2,3-4,4-5,5-6,7-8,9-10,1-100';
for my $exclusion ( split /,/, $exclusions ) {
if ($exclusion =~ m|\-|) {
# Range
my ($start, $stop) = split /-/, $exclusion;
$ports[$_] = 0 for ($start..$stop);
} else {
# Single port
$ports[$exclusion] = 0;
}
}
my #good_ports = grep { $ports[$_] > 0 } ( $port_range[0] .. $port_range[1] );
my $last_good = 0;
for my $i ( 1 .. $#good_ports ) {
if ($good_ports[$i] - $good_ports[$i-1] > 1) {
# gap
print join '-', $good_ports[$last_good], $good_ports[$i-1] . "\n";
$last_good = $i;
}
}
print join '-', $good_ports[$last_good], $good_ports[$#good_ports] . "\n";
Output
101-65535
With a range as small as that you could just keep a list of every port and set flags for which are open & closed, and print out the results.
#!/usr/bin/perl
use warnings;
use strict;
# print a list of open ports, given a string of closed ports
my #ports = map { 1 } 1..65535; # start with all ports flagged as open
while (<DATA>) { # get the string of closed port numbers & ranges
chomp;
/\d/ or next; # ensure we have at least one number to work with
my #exclusions = split /,/;
for (#exclusions) {
# each exclusion is a number, optionally followed
# by a dash and another number
/^(\d+)(?:-(\d+))?$/ or next;
# set the flag to 0 for a single port or a range of ports
if ($1 and ! $2) {
$ports[$1-1] = 0; # single port
}
elsif ($1 and $2) {
#ports[$1-1..$2-1] = map {0} $1..$2; # range of ports
}
}
}
# get a list of all ports which are open
my #open_ports = map {$_ + 1} grep {$ports[$_] == 1} 0..$#ports;
# the final list of open port ranges, to be displayed
my #ranges = ();
# build up the list of open ranges
for (#open_ports) {
my $one_less = $_ - 1;
# either add this open port to the previous range,
# or start a new range with this port
#
(#ranges and $ranges[-1] =~ s/-$one_less$/-$_/)
or push #ranges, "$_-$_";
}
# fix single-number ranges for display
for (#ranges) {
s/^(\d+)-\1$/$1/;
}
# display the result
print join ',', #ranges;
__DATA__
1-2,3-4,4-5,5-6,7-8,9-10,1-100
Output:
101-65535

Find multiple substrings in strings and record location

The following is the script for finding consecutive substrings in strings.
use strict;
use warnings;
my $file="Sample.txt";
open(DAT, $file) || die("Could not open file!");
#worry about these later
#my $regexp1 = "motif1";
#my $regexp2 = "motif2";
#my $regexp3 = "motif3";
#my $regexp4 = "motif4";
my $sequence;
while (my $line = <DAT>) {
if ($line=~ /(HDWFLSFKD)/g){
{
print "its found index location: ",
pos($line), "-", pos($line)+length($1), "\n";
}
if ($line=~ /(HD)/g){
print "motif found and its locations is: \n";
pos($line), "-", pos($line)+length($1), "\n\n";
}
if ($line=~ /(K)/g){
print "motif found and its location is: \n";
pos($line), "-",pos($line)+length($1), "\n\n";
}
if ($line=~ /(DD)/g){
print "motif found and its location is: \n";
pos($line), "-", pos($line)+length($1), "\n\n";
}
}else {
$sequence .= $line;
print "came in else\n";
}
}
It matches substring1 with string and prints out position where substring1 matched. The problem lies in finding the rest of the substrings. For substrings2 it starts again from the beginning of the string (instead of starting from the position where substring1 was found). The problem is that every time it calculates position it starts from the beginning of string instead of starting from the position of the previously found substring. Since substrings are consecutive substring1, substring2, substring3, substring4, their positions have to occur after the previous respectively.
Try this perl program
use strict;
use warnings;
use feature qw'say';
my $file="Sample.txt";
open( my $dat, '<', $file) || die("Could not open file!");
my #regex = qw(
HDWFLSFKD
HD
K
DD
);
my $sequence;
while( my $line = <$dat> ){
chomp $line;
say 'Line: ', $.;
# reset the position of variable $line
# pos is an lvalue subroutine
pos $line = 0;
for my $regex ( #regex ){
$regex = quotemeta $regex;
if( scalar $line =~ / \G (.*?) ($regex) /xg ){
say $regex, ' found at location (', $-[2], '-', $+[2], ')';
if( $1 ){
say " but skipped: \"$1\" at location ($-[1]-$+[1])";
}
}else{
say 'Unable to find ', $regex;
# end loop
last;
}
}
}
I'm not a perl expert but you can use $- and $+ to track index location for last regex match found.
Below is code built on top of your code that explains this.
use strict;
use warnings;
my $file="sample.txt";
open(DAT, $file) || die("Could not open file!");
open (OUTPUTFILE, '>data.txt');
my $sequence;
my $someVar = 0;
my $sequenceNums = 1;
my $motif1 = "(HDWFLSFKD)";
my $motif2 = "(HD)";
my $motif3 = "(K)";
my $motif4 = "(DD)";
while (my $line = <DAT>)
{
$someVar = 0;
print "\nSequence $sequenceNums: $line\n";
print OUTPUTFILE "\nSequence $sequenceNums: $line\n";
if ($line=~ /$motif1/g)
{
&printStuff($sequenceNums, "motif1", $motif1, "$-[0]-$+[0]");
$someVar = 1;
}
if ($line=~ /$motif2/g and $someVar == 1)
{
&printStuff($sequenceNums, "motif2", $motif2, "$-[0]-$+[0]");
$someVar = 2;
}
if ($line=~ /$motif3/g and $someVar == 2)
{
&printStuff($sequenceNums, "motif3", $motif4, "$-[0]-$+[0]");
$someVar = 3;
}
if ($line=~ /$motif4/g and $someVar == 3)
{
&printStuff($sequenceNums, "motif4", $motif4, "$-[0]-$+[0]");
}
else
{
$sequence .= $line;
if ($someVar == 0)
{
&printWrongStuff($sequenceNums, "motif1", $motif1);
}
elsif ($someVar == 1)
{
&printWrongStuff($sequenceNums, "motif2", $motif2);
}
elsif ($someVar == 2)
{
&printWrongStuff($sequenceNums, "motif3", $motif3);
}
elsif ($someVar == 3)
{
&printWrongStuff($sequenceNums, "motif4", $motif4);
}
}
$sequenceNums++;
}
sub printStuff
{
print "Sequence: $_[0] $_[1]: $_[2] index location: $_[3] \n";
print OUTPUTFILE "Sequence: $_[0] $_[1]: $_[2] index location: $_[3]\n";
}
sub printWrongStuff
{
print "Sequence: $_[0] $_[1]: $_[2] was not found\n";
print OUTPUTFILE "Sequence: $_[0] $_[1]: $_[2] was not found\n";
}
close (OUTPUTFILE);
close (DAT);
Sample input:
MLTSHQKKFHDWFLSFKDSNNYNHDSKQNHSIKDDIFNRFNHYIYNDLGIRTIA
MLTSHQKKFSNNYNSKQNHSIKDIFNRFNHYIYNDLGIRTIA
MLTSHQKKFSNNYNSKHDWFLSFKDQNHSIKDIFNRFNHYIYNDL
You really should read
perldoc perlre
perldoc perlreref
perldoc perlretut
You need the special variables #- and #+ if you need the positions. No need to try to compute them yourself.
#!/usr/bin/perl
use strict;
use warnings;
use List::MoreUtils qw( each_array );
my $source = 'AAAA BBCCC DD E FFFFF';
my $pattern = join '\s*', map { "($_+)" } qw( A B C D E F );
if ( $source =~ /$pattern/ ) {
my $it = each_array #-, #+;
$it->(); # discard overall match information;
while ( my ($start, $end) = $it->() ) {
printf "Start: %d - Length: %d\n", $start, $end - $start;
}
}
Start: 0 - Length: 4
Start: 7 - Length: 2
Start: 9 - Length: 3
Start: 15 - Length: 2
Start: 19 - Length: 1
Start: 26 - Length: 5
The result of a construct like
$line=~ /(HD)/g
is a list. Use while to step through the hits.
To match where the last match left off, use \G. perldoc perlre says (but consult your own installation's version's manual first):
The "\G" assertion can be used to
chain global matches (using "m//g"),
as described in "Regexp Quote-Like
Operators" in perlop. It is also
useful when writing "lex"-like
scanners, when you have several
patterns that you want to match
against consequent substrings of your
string, see the previous reference.
The actual location where "\G" will
match can also be influenced by using
"pos()" as an lvalue: see "pos" in
perlfunc. Note that the rule for
zero-length matches is modified
somewhat, in that contents to the left
of "\G" is not counted when
determining the length of the match.
Thus the following will not match
forever:
$str = 'ABC';
pos($str) = 1;
while (/.\G/g) {
print $&;
}

How can I save email to a file with Perl?

I learning Perl and I want to create a simple application that gets all my emails and save they to a file, but how I can do this? Thanks.
I used to use the following script to filter SpamAssassin flagged email before switching ISPs:
#!/usr/bin/perl
use strict;
use warnings;
$| = 1;
use constant SEVERITY => 5;
use Mail::POP3Client;
use Term::ReadKey;
my $user = shift;
my $pop = Mail::POP3Client->new(
HOST => '127.0.0.1',
PORT => 9999
);
my $pass = prompt_password();
print "\n";
$pop->User($user);
$pop->Pass($pass);
$pop->Connect or die $pop->Message;
my $count = $pop->Count;
$count >= 0 or die "Failed to get message count.\n";
$count > 0 or die "No messages in mailbox.\n";
my #to_delete;
print "Scanning messages: ";
my $to_delete = 0;
for my $msg_num (1 .. $count) {
my #headers = $pop->Head($msg_num);
for my $h (#headers) {
if($h =~ /^X-Spam-Level: (\*+)/) {
if(SEVERITY <= length $1) {
$to_delete += 1;
$pop->Delete($msg_num);
print "\b*>";
} else {
print "\b->";
}
}
}
}
print "\b ... done\n";
use Lingua::EN::Inflect qw( PL );
if( $to_delete ) {
printf "%d %s will be deleted. Commit: [Y/N]?\n",
$to_delete, PL('message', $to_delete);
$pop->Reset unless yes();
}
$pop->Close;
print "OK\n";
sub yes {
while(my $r = <STDIN>) {
$r = lc substr $r, 0, 1;
return 1 if $r eq 'y';
next unless $r eq 'n';
last;
}
0;
}
sub prompt_password {
print 'Password: ';
ReadMode 2;
my $pass = ReadLine 0;
ReadMode 0;
chomp $pass;
return $pass;
}
It is trivial to change this so it saves messages. See Mail::POP3Client.
POP3 example in Perl
The answer to almost any such question is "Find the right module on CPAN Search".
Most modules come with examples in the documentation and tests.
Good luck, :)