diff --git a/IP.pm b/IP.pm index cf898d5..4909d4d 100644 --- a/IP.pm +++ b/IP.pm @@ -7,24 +7,139 @@ use Socket; use strict; use warnings; + + ############################################# + # These are the overload methods, placed here + # for convenience. + ############################################# + use overload - '""' => sub { $_[0]->cidr(); }, - 'eq' => sub { my $a = ref $_[0] eq 'NetAddr::IP' ? - $_[0]->cidr : $_[0]; - my $b = ref $_[1] eq 'NetAddr::IP' ? - $_[1]->cidr : $_[1]; - $a eq $b; - }, + '+' => \&plus, - '==' => sub { return 0 unless ref $_[0] eq 'NetAddr::IP'; - return 0 unless ref $_[1] eq 'NetAddr::IP'; - $_[0]->cidr eq $_[1]->cidr; - }, + '-' => \&minus, - '@{}' => sub { return [ $_[0]->hostenum ]; }; + '++' => \&plusplus, -our $VERSION = '3.02'; + '--' => \&minusminus, + + "=" => sub { + return _fnew NetAddr::IP [ $_[0]->{addr}, $_[0]->{mask}, + $_[0]->{bits} ]; + }, + + '""' => sub { + $_[0]->cidr(); + }, + + 'eq' => sub { + my $a = ref $_[0] eq 'NetAddr::IP' ? $_[0]->cidr : $_[0]; + my $b = ref $_[1] eq 'NetAddr::IP' ? $_[1]->cidr : $_[1]; + $a eq $b; + }, + + '==' => sub { + return 0 unless ref $_[0] eq 'NetAddr::IP'; + return 0 unless ref $_[1] eq 'NetAddr::IP'; + $_[0]->cidr eq $_[1]->cidr; + }, + # The comparisons below are not portable + # when attempted with the full bit vector. + # This is why we break them down and do it + # one octet at a time. String comparison + # is not portable because of endianness. + '>' => sub { + return 0 if ($_[0]->{bits} != $_[1]->{bits}); + for my $b (0 .. $_[0]->{bits}/8 - 1) { + return 1 if vec($_[0]->{addr}, $b, 8) + > vec($_[1]->{addr}, $b, 8); + } + for my $b (0 .. $_[0]->{bits}/8 - 1) { + return 1 if vec($_[0]->{mask}, $b, 8) + > vec($_[1]->{mask}, $b, 8); + } + return 0; + }, + + '<' => sub { + return 0 if ($_[0]->{bits} != $_[1]->{bits}); + for my $b (0 .. $_[0]->{bits}/8 - 1) { + return 1 if vec($_[0]->{addr}, $b, 8) + < vec($_[1]->{addr}, $b, 8); + } + for my $b (0 .. $_[0]->{bits}/8 - 1) { + return 1 if vec($_[0]->{mask}, $b, 8) + < vec($_[1]->{mask}, $b, 8); + } + return 0; + }, + + '>=' => sub { + return 0 if ($_[0]->{bits} != $_[1]->{bits}); + for my $b (0 .. $_[0]->{bits}/8 - 1) { + return 1 if vec($_[0]->{addr}, $b, 8) + >= vec($_[1]->{addr}, $b, 8); + } + for my $b (0 .. $_[0]->{bits}/8 - 1) { + return 1 if vec($_[0]->{mask}, $b, 8) + >= vec($_[1]->{mask}, $b, 8); + } + return 0; + }, + + '<=' => sub { + return 0 if ($_[0]->{bits} != $_[1]->{bits}); + for my $b (0 .. $_[0]->{bits}/8 - 1) { + return 1 if vec($_[0]->{addr}, $b, 8) + <= vec($_[1]->{addr}, $b, 8); + } + for my $b (0 .. $_[0]->{bits}/8 - 1) { + return 1 if vec($_[0]->{mask}, $b, 8) + <= vec($_[1]->{mask}, $b, 8); + } + return 0; + }, + + '<=>' => sub { + return undef if ($_[0]->{bits} != $_[1]->{bits}); + for my $b (0 .. $_[0]->{bits}/8 - 1) { + my $r = vec($_[0]->{addr}, $b, 8) + <=> vec($_[1]->{addr}, $b, 8); + return $r if $r; + } + for my $b (0 .. $_[0]->{bits}/8 - 1) { + my $r = vec($_[0]->{mask}, $b, 8) + <=> vec($_[1]->{mask}, $b, 8); + return $r if $r; + } + return 0; + }, + + 'cmp' => sub { + return undef if ($_[0]->{bits} != $_[1]->{bits}); + for my $b (0 .. $_[0]->{bits}/8 - 1) { + my $r = vec($_[0]->{addr}, $b, 8) + <=> vec($_[1]->{addr}, $b, 8); + return $r if $r; + } + for my $b (0 .. $_[0]->{bits}/8 - 1) { + my $r = vec($_[0]->{mask}, $b, 8) + <=> vec($_[1]->{mask}, $b, 8); + return $r if $r; + } + return 0; + }, + + '@{}' => sub { + return [ $_[0]->hostenum ]; + }; + + ############################################# + # End of the overload methods. + ############################################# + + +our $VERSION = '3.03'; # Preloaded methods go here. @@ -52,6 +167,64 @@ return ~vec('', 0, $bits); } + # Addition of a constant to an + # object +sub plus { + my $ip = shift; + my $const = shift; + + return $ip unless $const; + + my $a = $ip->{addr}; + my $m = $ip->{mask}; + + my $hp = "$a" & ~"$m"; + my $np = "$a" & "$m"; + + vec($hp, 0, 32) += $const; + + return _fnew NetAddr::IP [ "$np" | ("$hp" & ~"$m"), + $ip->{mask}, $ip->{bits}]; +} + +sub minus { + my $ip = shift; + my $const = shift; + + return plus($ip, -$const, @_); +} + + # Auto-increment an object +sub plusplus { + my $ip = shift; + + my $a = $ip->{addr}; + my $m = $ip->{mask}; + + my $hp = "$a" & ~"$m"; + my $np = "$a" & "$m"; + + vec($hp, 0, 32) ++; + + $ip->{addr} = "$np" | ("$hp" & ~"$m"); + return $ip; +} + +sub minusminus { + my $ip = shift; + + my $a = $ip->{addr}; + my $m = $ip->{mask}; + + my $hp = "$a" & ~"$m"; + my $np = "$a" & "$m"; + + vec($hp, 0, 32) --; + + $ip->{addr} = "$np" | ("$hp" & ~"$m"); + return $ip; +} + sub masklen ($) { my $self = shift; my $bits = 0; @@ -248,6 +421,13 @@ return $self->_fnew($self->_network); } +sub wildcard ($) { + my $self = shift; + return wantarray() ? ($self->addr, _to_quad ~$self->{mask}) : + _to_quad ~$self->{mask}; + +} + sub numeric ($) { my $self = shift; return @@ -262,11 +442,12 @@ sub compactref ($) { my @addr = sort - { (vec($a->{addr}, 0, $a->{bits}) <=> vec($b->{addr}, 0, $a->{bits})) - || (vec($a->{mask}, 0, $a->{bits}) - <=> vec($b->{mask}, 0, $a->{bits})) - } @{$_[0]} or - return []; +# { (vec($a->{addr}, 0, $a->{bits}) <=> vec($b->{addr}, 0, $a->{bits})) +# || (vec($a->{mask}, 0, $a->{bits}) +# <=> vec($b->{mask}, 0, $a->{bits})) +# } + @{$_[0]} or + return []; my $bits = $addr[0]->{bits}; my $changed; @@ -553,6 +734,15 @@ described, the second element is the numeric representation of the netmask. +=item C<-Ewildcard()> + +When called in a scalar context, returns the wildcard bits +corresponding to the mask, in dotted-quad format. + +When called in an array context, returns a two-element array. The +first element, is the address part. The second element, is the +wildcard translation of the mask. + =item C<$me-Econtains($other)> Returns true when C<$me> completely contains C<$other>. False is @@ -653,6 +843,17 @@ However, note that this might generate a very large amount of items in the list. You must be careful when doing this kind of expansion. +=item B + +You can add a constant to an object. This will return a new object +referring to the host address obtained by incrementing (or +decrementing) the given address. YOu can do this with the operators +B<+>, B<->, B<+=> and B<-=>. + +The auto-increment or auto-decrement operators will return a new +object pointing to the next or previous host address in the +subnet. These are the B<++> and B<--> operators. + =back =head2 EXPORT @@ -950,6 +1151,23 @@ =back +=item 3.03 + +=over + +=item * + +Added more comparison operators. + +=item * + +As per Peter Wirdemo's suggestion, added C<-Ewildcard()> for +producing subnets in wildcard format. + +=item * + +Added C<++> and C<+> to provide for efficient iteration operations +over all the hosts of a subnet without C<-Eexpand()>ing it. =back diff --git a/MANIFEST b/MANIFEST index d17657b..a57648d 100644 --- a/MANIFEST +++ b/MANIFEST @@ -3,8 +3,10 @@ Makefile.PL README t/00-load.t +t/loops.t t/v4-new.t t/v4-num.t +t/relops.t t/v4-snew.t t/over-qq.t t/masklen.t @@ -13,6 +15,7 @@ t/over-arr.t t/v4-basem.t t/v4-first.t +t/wildcard.t t/v4-compact.t t/v4-numeric.t t/v4-compplus.t diff --git a/README b/README index 54cedb5..6e58a9e 100644 --- a/README +++ b/README @@ -61,6 +61,14 @@ element is as described, the second element is the numeric representation of the netmask. + `->wildcard()' + When called in a scalar context, returns the wildcard bits + corresponding to the mask, in dotted-quad format. + + When called in an array context, returns a two-element array. The + first element, is the address part. The second element, is the + wildcard translation of the mask. + `$me->contains($other)' Returns true when `$me' completely contains `$other'. False is returned otherwise and `undef' is returned if `$me' and `$other' are @@ -143,6 +151,16 @@ However, note that this might generate a very large amount of items in the list. You must be careful when doing this kind of expansion. + Sum and auto-increment + You can add a constant to an object. This will return a new object + referring to the host address obtained by incrementing (or + decrementing) the given address. YOu can do this with the operators + +, -, += and -=. + + The auto-increment or auto-decrement operators will return a new + object pointing to the next or previous host address in the subnet. + These are the ++ and -- operators. + EXPORT None by default. @@ -269,19 +287,28 @@ * Fixed compatibility issue with C<-Enum()> on 64-bit processors. + 3.03 + * Added more comparison operators. + + * As per Peter Wirdemo's suggestion, added `->wildcard()' for + producing subnets in wildcard format. + + * Added `++' and `+' to provide for efficient iteration operations + over all the hosts of a subnet without `->expand()'ing it. + AUTHOR - Luis E. Munoz + Luis E. Munoz WARRANTY - This software comes with the same warranty as perl itself (ie, none), so - by using it you accept any and all the liability. + This software comes with the same warranty as perl itself (ie, + none), so by using it you accept any and all the liability. LICENSE - This software is (c) Luis E. Munoz. It can be used under the terms of - the perl artistic license provided that proper credit for the work of - the author is preserved in the form of this copyright notice and license - for this module. + This software is (c) Luis E. Munoz. It can be used under the terms + of the perl artistic license provided that proper credit for the + work of the author is preserved in the form of this copyright notice + and license for this module. SEE ALSO - perl(1). + perl(1). diff --git a/t/loops.t b/t/loops.t new file mode 100644 index 0000000..31f9555 --- /dev/null +++ b/t/loops.t @@ -0,0 +1,51 @@ +use NetAddr::IP; + +$| = 1; + +my @deltas = (0, 1, 2, 3, 255); + +print "1..", 15 + @deltas, "\n"; + +my $count = 1; + +for (my $ip = new NetAddr::IP '10.0.0.1/28'; + $ip < $ip->broadcast; + $ip ++) +{ + my $o = $ip->addr; + + $o =~ s/^.+\.(\d+)$/$1/; + + if ($o == $count) { + print "ok $count\n"; + } + else { + print "not ok $count\n"; + } + + ++ $count; +} + +my $ip = new NetAddr::IP '10.0.0.255/24'; +$ip ++; + +if ($ip eq '10.0.0.0/24') { + print "ok $count\n"; +} +else { + print "not ok $count\n"; +} + +++$count; + +$ip = new NetAddr::IP '10.0.0.0/24'; + +for my $v (@deltas) { + if ($ip + $v eq '10.0.0.' . $v . '/24') { + print "ok $count\n"; + } + else { + print "not ok $count\n"; + } + ++ $count; +} diff --git a/t/relops.t b/t/relops.t new file mode 100644 index 0000000..364c47b --- /dev/null +++ b/t/relops.t @@ -0,0 +1,81 @@ +use NetAddr::IP; + +my @gt = ( + [ '255.255.255.255/32', '0.0.0.0/0' ], + [ '10.0.0.0/16', '10.0.0.0/8' ], + [ '10.0.0.0/24', '10.0.0.0/8' ], + ); + +my @ngt = ( + [ '0.0.0.0/0', '255.255.255.255/32' ], + [ '10.0.0.0/24', '10.0.0.0/24' ], + ); + +my @cmp = ( + [ '0.0.0.0/0', '255.255.255.255/32', -1 ], + [ '10.0.0.0/16', '10.0.0.0/8', 1 ], + [ '10.0.0.0/24', '10.0.0.0/8', 1 ], + [ '255.255.255.255/32', '0.0.0.0/0', 1 ], + [ '10.0.0.0/24', '10.0.0.0/24', 0 ], + [ 'default', 'default', 0 ], + [ 'broadcast', 'broadcast', 0], + [ 'loopback', 'loopback', 0], + ); + +$| = 1; + +print "1..", @gt + @ngt + (2 * @cmp), "\n"; + +my $count = 1; + +for my $a (@gt) { + my $a_ip = new NetAddr::IP $a->[0]; + my $b_ip = new NetAddr::IP $a->[1]; + + if ($a_ip > $b_ip) { + print "ok $count\n"; + } + else { + print "not ok $count\n"; + } + ++$count; +} + +for my $a (@ngt) { + my $a_ip = new NetAddr::IP $a->[0]; + my $b_ip = new NetAddr::IP $a->[1]; + + if (not ($a_ip > $b_ip)) { + print "ok $count\n"; + } + else { + print "not ok $count\n"; + } + ++$count; +} + +for my $a (@cmp) { + my $a_ip = new NetAddr::IP $a->[0]; + my $b_ip = new NetAddr::IP $a->[1]; + + if (($a_ip <=> $b_ip) == $a->[2]) { + print "ok $count\n"; + } + else { + print "not ok $count\n"; + } + ++$count; +} + +for my $a (@cmp) { + my $a_ip = new NetAddr::IP $a->[0]; + my $b_ip = new NetAddr::IP $a->[1]; + + if (($a_ip cmp $b_ip) == $a->[2]) { + print "ok $count\n"; + } + else { + print "not ok $count\n"; + } + ++$count; +} diff --git a/t/v4-compact.t b/t/v4-compact.t index a8a7191..b0dd00e 100644 --- a/t/v4-compact.t +++ b/t/v4-compact.t @@ -11,7 +11,7 @@ $| = 1; -print "1..2\n"; +print "1..4\n"; my @ips; @@ -52,3 +52,23 @@ print "not ok 2\n"; } +@c = NetAddr::IP::compact( + NetAddr::IP->new('broadcast'), + NetAddr::IP->new('default') + ); + +if (@c == 1) { + print "ok 3\n"; +} +else { + print "not ok 3\n"; +} + +if ($c[0]->cidr eq '0.0.0.0/0') { + print "ok 4\n"; +} +else { + print "not ok 4\n"; +} + + diff --git a/t/wildcard.t b/t/wildcard.t new file mode 100644 index 0000000..249b8bb --- /dev/null +++ b/t/wildcard.t @@ -0,0 +1,37 @@ +use NetAddr::IP; + +my @addr = ( + [ 'localhost', '0.0.0.0' ], + [ '10.0.0.0/24', '0.0.0.255' ], + [ '192.168.0.0/16', '0.0.255.255' ], + [ '10.128.0.1/17', '0.0.127.255' ] +); + +$| = 1; + +print "1..", 2 * scalar @addr, "\n"; + +my $count = 1; + +for my $a (@addr) { + my $ip = new NetAddr::IP $a->[0]; + + if ($ip->wildcard eq $a->[1]) { + print "ok $count\n"; + } + else { + print "not ok $count\n"; + } + + ++$count; + + + if (($ip->wildcard)[1] eq $a->[1]) { + print "ok $count\n"; + } + else { + print "not ok $count\n"; + } + + ++$count; +}