diff --git a/IP.pm b/IP.pm index 8cadd3b..759de67 100644 --- a/IP.pm +++ b/IP.pm @@ -1,14 +1,51 @@ #!/usr/bin/perl -w +# $Id: IP.pm,v 1.2 2002/10/31 04:30:22 lem Exp $ + package NetAddr::IP; +=pod + +=head1 NAME + +NetAddr::IP - Manages IPv4 addresses and subnets + +=head1 SYNOPSIS + + use NetAddr::IP; + + my $ip = new NetAddr::IP 'loopback'; + + print "The address is ", $ip->addr, " with mask ", $ip->mask, "\n" ; + + if ($ip->within(new NetAddr::IP "127.0.0.0", "255.0.0.0")) { + print "Is a loopback address\n"; + } + + # This prints 127.0.0.1/32 + print "You can also say $ip...\n"; + +=head1 DESCRIPTION + +This module provides an object-oriented abstraction on top of IP +addresses or IP subnets, that allows for easy manipulations. Many +operations are supported, as described below: + +=head2 Overloaded Operators + +Many operators have been overloaded, as described below: + +=over + +=cut + require 5.005_62; use Carp; use Socket; use strict; use warnings; -our $VERSION = '3.11'; +our $VERSION = '3.12'; ############################################# # These are the overload methods, placed here @@ -29,7 +66,7 @@ return _fnew NetAddr::IP [ $_[0]->{addr}, $_[0]->{mask}, $_[0]->{bits} ]; }, - + '""' => sub { $_[0]->cidr(); }, @@ -49,117 +86,198 @@ # 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 undef unless $_[0]->{bits} == $_[1]->{bits}; return scalar($_[0]->numeric()) > scalar($_[1]->numeric()); - -# 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 unless $_[0]->{bits} == $_[1]->{bits}; return scalar($_[0]->numeric()) < scalar($_[1]->numeric()); - -# 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 unless $_[0]->{bits} == $_[1]->{bits}; return scalar($_[0]->numeric()) >= scalar($_[1]->numeric()); - -# 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 unless $_[0]->{bits} == $_[1]->{bits}; return scalar($_[0]->numeric()) <= scalar($_[1]->numeric()); - -# 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 unless $_[0]->{bits} == $_[1]->{bits}; return scalar($_[0]->numeric()) <=> scalar($_[1]->numeric()); - -# 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 unless $_[0]->{bits} == $_[1]->{bits}; return scalar($_[0]->numeric()) <=> scalar($_[1]->numeric()); - -# 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 ]; }; +=pod + +=item B)> + +Has been optimized to copy one NetAddr::IP object to another very quickly. + +=item B + +An object can be used just as a string. For instance, the following code + + my $ip = new NetAddr::IP 'loopback'; + print "$ip\n"; + +Will print the string 127.0.0.1/8. + +=item B + +You can test for equality with either C or C<==>. C allows the +comparison with arbitrary strings as well as NetAddr::IP objects. The +following example: + + if (NetAddr::IP->new('loopback') eq '127.0.0.1/8') + { print "Yes\n"; } + +Will print out "Yes". + +Comparison with C<==> requires both operands to be NetAddr::IP objects. + +In both cases, a true value is returned if the CIDR representation of +the operands is equal. + +=item B, E, E=, E=, E=E and C> + +Those are numeric comparisons. All will return undef if you attempt to +compare a V4 subnet with a V6 subnet, when V6 becomes supported some +day. + +In case the version matches, the numeric representation of the network +is compared through the corresponding operation. The netmask is +ignored for these comparisons, as there is no standard criteria to say +wether 10/8 is larger than 10/10 or not. + +=item B + +You can do something along the lines of + + my $net = new NetAddr::IP $cidr_spec; + for my $ip (@$net) { + print "Host $ip is in $net\n"; + } + +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, as it +is very easy to consume huge amounts of resources. See below for +smarter ways to do loops and other constructions that are much more +conservative. + +=item B + +Adding a constant to a NetAddr::IP object changes its address part to +point to the one so many hosts above the start address. For instance, +this code: + + print NetAddr::IP->new('loopback') + 5; + +will output 127.0.0.6/8. The address will wrap around at the broadcast +back to the network address. This code: + + print NetAddr::IP->new('10.0.0.1/24') + 255; + +outputs 10.0.0.0/24. + +=cut + +sub plus { + my $ip = shift; + my $const = shift; + + return $ip unless $const; + + my $a = $ip->{addr}; + my $m = $ip->{mask}; + my $b = $ip->{bits}; + + my $hp = "$a" & ~"$m"; + my $np = "$a" & "$m"; + + vec($hp, 0, $b) += $const; + + return _fnew NetAddr::IP [ "$np" | ("$hp" & ~"$m"), $m, $b]; +} + +=item B + +The complement of the addition of a constant. + +=cut + +sub minus { + my $ip = shift; + my $const = shift; + + return plus($ip, -$const, @_); +} + + # Auto-increment an object +=pod + +=item B + +Auto-incrementing a NetAddr::IP object causes the address part to be +adjusted to the next host address within the subnet. It will wrap at +the broadcast address and start again from the network address. + +=cut + +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; +} + +=item B + +Auto-decrementing a NetAddr::IP object performs exactly the opposite +of auto-incrementing it, as you would expect. + +=cut + +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; +} + ############################################# # End of the overload methods. ############################################# @@ -191,91 +309,55 @@ 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 $b = $ip->{bits}; - - my $hp = "$a" & ~"$m"; - my $np = "$a" & "$m"; - - vec($hp, 0, $b) += $const; - - return _fnew NetAddr::IP [ "$np" | ("$hp" & ~"$m"), $m, $b]; +sub _to_quad ($) { + my $vec = shift; + return vec($vec, 0, 8) . '.' . + vec($vec, 1, 8) . '.' . + vec($vec, 2, 8) . '.' . + vec($vec, 3, 8); } -sub minus { - my $ip = shift; - my $const = shift; +sub do_prefix ($$$) { + my $mask = shift; + my $faddr = shift; + my $laddr = 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; - - for (my $i = 0; - $i < $self->{bits}; - $i ++) - { - $bits += vec($self->{mask}, $i, 1); + if ($mask > 24) { + return "$faddr->[0].$faddr->[1].$faddr->[2].$faddr->[3]-$laddr->[3]"; } - - return $bits; + elsif ($mask == 24) { + return "$faddr->[0].$faddr->[1].$faddr->[2]."; + } + elsif ($mask > 16) { + return "$faddr->[0].$faddr->[1].$faddr->[2]-$laddr->[2]."; + } + elsif ($mask == 16) { + return "$faddr->[0].$faddr->[1]."; + } + elsif ($mask > 8) { + return "$faddr->[0].$faddr->[1]-$laddr->[1]."; + } + elsif ($mask == 8) { + return "$faddr->[0]."; + } + else { + return "$faddr->[0]-$laddr->[0]"; + } } sub _parse_mask ($$) { - my $mask = shift; + my $mask = lc shift; my $bits = shift; my $bmask = ''; - if ($mask =~ m/^default|any$/i) { + if ($mask eq 'default' or $mask eq 'any') { vec($bmask, 0, $bits) = 0x0; } - elsif ($mask =~ m/^broadcast|host$/i) { + elsif ($mask eq 'broadcast' or $mask eq 'host') { vec($bmask, 0, $bits) = _ones $bits; } - elsif ($mask =~ m/^loopback$/i) { + elsif ($mask eq 'loopback') { vec($bmask, 0, 8) = 255; vec($bmask, 1, 8) = 0; vec($bmask, 2, 8) = 0; @@ -324,19 +406,19 @@ } sub _v4 ($$$) { - my $ip = shift; + my $ip = lc shift; my $mask = shift; my $present = shift; my $addr = ''; - if ($ip =~ m!^default|any$!i) { + if ($ip eq 'default' or $ip eq 'any') { vec($addr, 0, 32) = 0x0; } - elsif ($ip =~ m!^broadcast$!i) { + elsif ($ip eq 'broadcast') { vec($addr, 0, 32) = _ones 32; } - elsif ($ip =~ m!^loopback$!i) { + elsif ($ip eq 'loopback') { vec($addr, 0, 8) = 127; vec($addr, 3, 8) = 1; } @@ -493,6 +575,13 @@ vec($addr, 3, 8) = $4; } } + elsif (!$present and length($ip) == 4) { + + my @o = unpack("c4", $ip); + + vec($addr, $_, 8) = $o[$_] for 0 .. 3; + vec($mask, 0, 32) = 0xFFFFFFFF; + } else { # croak "Cannot obtain an IP address out of $ip"; return undef; @@ -501,6 +590,39 @@ return { addr => $addr, mask => $mask, bits => 32 }; } +sub new4 ($$;$) { + new($_[0], $_[1], $_[2]); +} + +=pod + +=back + +=head2 Methods + +=over + +=item C<-Enew([$addr, [ $mask ]])> + +This method creates a new IPv4 address with the supplied address in +C<$addr> and an optional netmask C<$mask>, which can be omitted to get +a /32 mask. + +C<$addr> can be almost anything that can be resolved to an IP address +in all the notations I have seen over time. It can optionally contain +the mask in CIDR notation. + +B notation is understood, with the limitation that the range +speficied by the prefix must match with a valid subnet. + +Addresses in the same format returned by C or +C are also understood, although no mask can be +specified for them. + +If called with no arguments, 'default' is assumed. + +=cut + sub new ($$;$) { my $type = $_[0]; my $class = ref($type) || $type || "NetAddr::IP"; @@ -541,32 +663,22 @@ return bless $self, $class; } -sub new4 ($$;$) { - new($_[0], $_[1], $_[2]); -} +=pod - # Output a vec() as a dotted-quad +=item C<-Ebroadcast()> -sub _to_quad ($) { - my $vec = shift; - return vec($vec, 0, 8) . '.' . - vec($vec, 1, 8) . '.' . - vec($vec, 2, 8) . '.' . - vec($vec, 3, 8); -} +Returns a new object refering to the broadcast address of a given +subnet. The broadcast address has all ones in all the bit positions +where the netmask has zero bits. This is normally used to address all +the hosts in a given subnet. - # Get the network address +=cut -sub _network ($) { +sub broadcast ($) { my $self = shift; - my $a = $self->{addr}; - my $m = $self->{mask}; - - return [ "$a" & "$m", $self->{mask}, $self->{bits} ]; + return $self->_fnew($self->_broadcast); } - # Should be obvious - sub _broadcast ($) { my $self = shift; my $a = $self->{addr}; @@ -579,66 +691,137 @@ return [ "$a" | ~ "$m" | $c, $self->{mask}, $self->{bits} ]; } - # This will become an lvalue later +=pod -sub mask ($) { +=item C<-Enetwork()> + +Returns a new object refering to the network address of a given +subnet. A network address has all zero bits where the bits of the +netmask are zero. Normally this is used to refer to a subnet. + +=cut + +sub network ($) { my $self = shift; - _to_quad $self->{mask}; + return $self->_fnew($self->_network); } - # idem +sub _network ($) { + my $self = shift; + my $a = $self->{addr}; + my $m = $self->{mask}; + + return [ "$a" & "$m", $self->{mask}, $self->{bits} ]; +} + +=pod + +=item C<-Eaddr()> + +Returns a scalar with the address part of the object as a +dotted-quad. This is useful for printing or for passing the address +part of the NetAddr::IP object to other components that expect an IP +address. + +=cut sub addr ($) { my $self = shift; _to_quad $self->{addr}; } +=pod + +=item C<-Emask()> + +Returns a scalar with the mask as a dotted-quad. + +=cut + +sub mask ($) { + my $self = shift; + _to_quad $self->{mask}; +} + +=pod + +=item C<-Emasklen()> + +Returns a scalar the number of one bits in the mask. + +=cut + +sub masklen ($) { + my $self = shift; + my $bits = 0; + + for (my $i = 0; + $i < $self->{bits}; + $i ++) + { + $bits += vec($self->{mask}, $i, 1); + } + + return $bits; +} + +=pod + +=item C<-Ecidr()> + +Returns a scalar with the address and mask in CIDR notation. A +NetAddr::IP object I to the result of this function. + +=cut + sub cidr ($) { my $self = shift; return $self->addr . '/' . $self->masklen; } -sub do_prefix ($$$) { - my $mask = shift; - my $faddr = shift; - my $laddr = shift; +=pod - if ($mask > 24) { - return "$faddr->[0].$faddr->[1].$faddr->[2].$faddr->[3]-$laddr->[3]"; - } - elsif ($mask == 24) { - return "$faddr->[0].$faddr->[1].$faddr->[2]."; - } - elsif ($mask > 16) { - return "$faddr->[0].$faddr->[1].$faddr->[2]-$laddr->[2]."; - } - elsif ($mask == 16) { - return "$faddr->[0].$faddr->[1]."; - } - elsif ($mask > 8) { - return "$faddr->[0].$faddr->[1]-$laddr->[1]."; - } - elsif ($mask == 8) { - return "$faddr->[0]."; - } - else { - return "$faddr->[0]-$laddr->[0]"; - } +=item C<-Eaton()> + +Returns the address part of the NetAddr::IP object in the same format +as the C function. This should ease a bit the code +required to deal with "old-style" sockets. + +=cut + +sub aton { + my $self = shift; + return pack "c4", split /\./, $self->addr; } -sub nprefix ($) { +=pod + +=item C<-Erange()> + +Returns a scalar with the base address and the broadcast address +separated by a dash and spaces. This is called range notation. + +=cut + +sub range ($) { my $self = shift; my $mask = $self->masklen; return undef if $self->{bits} > 32; - return $self->addr if $mask == 32; - - my @faddr = split (/\./, $self->first->addr); - my @laddr = split (/\./, $self->last->addr); - - return do_prefix $mask, \@faddr, \@laddr; + return $self->network->addr . ' - ' . $self->broadcast->addr; } +=pod + +=item C<-Eprefix()> + +Returns a scalar with the address and mask in prefix +representation. This is useful for some programs, which expect its +input to be in this format. This method will include the broadcast +address in the encoding. + +=cut + sub prefix ($) { my $self = shift; my $mask = $self->masklen; @@ -652,30 +835,41 @@ return do_prefix $mask, \@faddr, \@laddr; } -sub range ($) { +=pod + +=item C<-Enprefix()> + +Just as C<-Eprefix()>, but does not include the broadcast address. + +=cut + +sub nprefix ($) { my $self = shift; my $mask = $self->masklen; return undef if $self->{bits} > 32; - return $self->network->addr . ' - ' . $self->broadcast->addr; + return $self->addr if $mask == 32; + + my @faddr = split (/\./, $self->first->addr); + my @laddr = split (/\./, $self->last->addr); + + return do_prefix $mask, \@faddr, \@laddr; } -sub broadcast ($) { - my $self = shift; - return $self->_fnew($self->_broadcast); -} +=pod -sub network ($) { - my $self = shift; - return $self->_fnew($self->_network); -} +=item C<-Enumeric()> -sub wildcard ($) { - my $self = shift; - return wantarray() ? ($self->addr, _to_quad ~$self->{mask}) : - _to_quad ~$self->{mask}; - -} +When called in a scalar context, will return a numeric representation +of the address part of the IP address. When called in an array +contest, it returns a list of two elements. The first element is as +described, the second element is the numeric representation of the +netmask. + +This method is essential for serializing the representation of a +subnet. + +=cut sub numeric ($) { my $self = shift; @@ -685,89 +879,101 @@ vec($self->{addr}, 0, 32); } - # Return the shortest possible subnet - # list that completely contains all - # the given addresses or subnets. +=pod -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 []; +=item C<-Ewildcard()> - my $bits = $addr[0]->{bits}; - my $changed; +When called in a scalar context, returns the wildcard bits +corresponding to the mask, in dotted-quad format. - do { - $changed = 0; - for (my $i = 0; - $i <= $#addr - 1; - $i ++) - { - my $lip = $addr[$i]; - my $hip = $addr[$i + 1]; +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. - if ($lip->contains($hip)) { - splice(@addr, $i + 1, 1); - ++ $changed; - -- $i; - } - elsif (vec($lip->{mask}, 0, $bits) - == vec($hip->{mask}, 0, $bits)) - { - my $la = $lip->{addr}; - my $ha = $hip->{addr}; - my $nb = ''; - my $na = ''; - my $nm = ''; +=cut - vec($nb, 0, $bits) = - vec($na, 0, $bits) = - vec($la, 0, $bits); - vec($nb, 0, $bits) ^= vec($ha, 0, $bits); - vec($na, 0, $bits) ^= vec($nb, 0, $bits); - vec($nm, 0, $bits) = vec($lip->{mask}, 0, $bits); - vec($nm, 0, $bits) <<= 1; - - -# if ((vec($la, 0, $bits) & vec($nm, 0, $bits)) -# == (vec($ha, 0, $bits) & vec($nm, 0, $bits))) - - if (("$la" & "$nm") eq ("$ha" & "$nm")) - { - if ("$la" eq "$ha") { - splice(@addr, $i + 1, 1); - } - else { - $addr[$i] = ($lip->_fnew([ "$na" & "$nm", - $nm, $bits ])); - splice(@addr, $i + 1, 1); - } - -# print $lip->addr, "/", $lip->mask, " + ", $hip->addr, -# "/", $hip->mask, " = ", $addr[$i]->addr, "/", -# $addr[$i]->mask, "\n"; - - -- $i; - ++ $changed; - } - } - } - } while ($changed); - - return \@addr; +sub wildcard ($) { + my $self = shift; + return wantarray() ? ($self->addr, _to_quad ~$self->{mask}) : + _to_quad ~$self->{mask}; + } -sub compact { - return @{compactref(\@_)}; +=pod + +=item C<$me-Econtains($other)> + +Returns true when C<$me> completely contains C<$other>. False is +returned otherwise and C is returned if C<$me> and C<$other> +are of different versions. + +=cut + +sub contains ($$) { + my $a = shift; + my $b = shift; + + my $bits = $a->{bits}; + + my $mask; + + # Both must be of the same length... + return undef + unless $bits == $b->{bits}; + + # $a must be less specific than $b... + return 0 + unless ($mask = vec($a->{mask}, 0, $bits)) + <= vec($b->{mask}, 0, $bits); + + # A default address always contains + return 1 if ($mask == 0x0); + + return + ((vec($a->{addr}, 0, $bits) & $mask) + == (vec($b->{addr}, 0, $bits) & $mask)); } - # Splits the current object in - # smaller subnets, of $bits bits - # netmask. +=pod + +=item C<$me-Ewithin($other)> + +The complement of C<-Econtains()>. Returns true when C<$me> is +completely con tained within C<$other>. + +=cut + +sub within ($$) { + return contains($_[1], $_[0]); +} + +=pod + +=item C<-Esplit($bits)> + +Returns a list of objects, representing subnets of C<$bits> mask +produced by splitting the original object, which is left +unchanged. Note that C<$bits> must be longer than the original +mask in order for it to be splittable. + +Note that C<$bits> can be given as an integer (the length of the mask) +or as a dotted-quad. If omitted, a host mask is assumed. + +=cut + +sub split ($;$) { + return @{$_[0]->splitref($_[1])}; +} + +=pod + +=item C<-Esplitref($bits)> + +A (faster) version of C<-Esplit()> that returns a reference to a +list of objects instead of a real list. This is useful when large +numbers of objects are expected. + +=cut sub splitref ($;$) { my $self = shift; @@ -816,10 +1022,26 @@ return \@ret; } -sub split ($;$) { - return @{$_[0]->splitref($_[1])}; +=pod + +=item C<-Ehostenum()> + +Returns the list of hosts within a subnet. + +=cut + +sub hostenum ($) { + return @{$_[0]->hostenumref}; } +=pod + +=item C<-Ehostenumref()> + +Faster version of C<-Ehostenum()>, returning a reference to a list. + +=cut + sub hostenumref ($) { my $r = $_[0]->splitref(32); if ($_[0]->mask ne '255.255.255.255') { @@ -829,42 +1051,104 @@ return $r; } -sub hostenum ($) { - return @{$_[0]->hostenumref}; +=pod + +=item C<$me-Ecompact($addr1, $addr2, ...)> + +Given a list of objects (including C<$me>), this method will compact +all the addresses and subnets into the largest (ie, least specific) +subnets possible that contain exactly all of the given objects. + +Note that in versions prior to 3.02, if fed with the same IP subnets +multiple times, these subnets would be returned. From 3.02 on, a more +"correct" approach has been adopted and only one address would be +returned. + +=cut + +sub compact { + return @{compactref(\@_)}; } +=pod - # Returns TRUE if $a completely - # contains $b and both are of the - # same length (ie, V4 or V6). -sub contains ($$) { - my $a = shift; - my $b = shift; +=item C<$me-Ecompactref(\@list)> - my $bits = $a->{bits}; +As usual, a faster version of =item C<-Ecompact()> that returns a +reference to a list. Note that this method takes a reference to a list +instead. - my $mask; - - # Both must be of the same length... - return undef - unless $bits == $b->{bits}; +=cut - # $a must be less specific than $b... - return 0 - unless ($mask = vec($a->{mask}, 0, $bits)) - <= vec($b->{mask}, 0, $bits); +sub compactref ($) { + my @addr = sort - # A default address always contains - return 1 if ($mask == 0x0); + @{$_[0]} or + return []; - return - ((vec($a->{addr}, 0, $bits) & $mask) - == (vec($b->{addr}, 0, $bits) & $mask)); + my $bits = $addr[0]->{bits}; + my $changed; + + do { + $changed = 0; + for (my $i = 0; + $i <= $#addr - 1; + $i ++) + { + my $lip = $addr[$i]; + my $hip = $addr[$i + 1]; + + if ($lip->contains($hip)) { + splice(@addr, $i + 1, 1); + ++ $changed; + -- $i; + } + elsif (vec($lip->{mask}, 0, $bits) + == vec($hip->{mask}, 0, $bits)) + { + my $la = $lip->{addr}; + my $ha = $hip->{addr}; + my $nb = ''; + my $na = ''; + my $nm = ''; + + vec($nb, 0, $bits) = + vec($na, 0, $bits) = + vec($la, 0, $bits); + vec($nb, 0, $bits) ^= vec($ha, 0, $bits); + vec($na, 0, $bits) ^= vec($nb, 0, $bits); + vec($nm, 0, $bits) = vec($lip->{mask}, 0, $bits); + vec($nm, 0, $bits) <<= 1; + + if (("$la" & "$nm") eq ("$ha" & "$nm")) + { + if ("$la" eq "$ha") { + splice(@addr, $i + 1, 1); + } + else { + $addr[$i] = ($lip->_fnew([ "$na" & "$nm", + $nm, $bits ])); + splice(@addr, $i + 1, 1); + } + + -- $i; + ++ $changed; + } + } + } + } while ($changed); + + return \@addr; } -sub within ($$) { - return contains($_[1], $_[0]); -} +=pod + +=item C<-Efirst()> + +Returns a new object representing the first useable IP address within +the subnet (ie, the first host address). + +=cut sub first ($) { my $self = shift; @@ -872,15 +1156,14 @@ return $self->network + 1; } -sub nth ($$) { - my $self = shift; - my $count = shift; +=pod - return undef if ($count < 1 or $count > $self->num ()); - return $self->network + $count; -} +=item C<-Elast()> - +Returns a new object representing the last useable IP address within +the subnet (ie, one less than the broadcast address). + +=cut sub last ($) { my $self = shift; @@ -890,183 +1173,7 @@ return $self->broadcast - 1; } - # XXX - The constant below should be - # constructed dinamically depending on - # the address size in order to work with - # V6. -sub num ($) { - my $self = shift; - return ~vec($self->{mask}, 0, $self->{bits}) & 0xFFFFFFFF; -} - -1; - -__END__ - -=head1 NAME - -NetAddr::IP - Manages IPv4 addresses and subnets - -=head1 SYNOPSIS - - use NetAddr::IP; - - my $ip = new NetAddr::IP 'loopback'; - - print "The address is ", $ip->addr, " with mask ", $ip->mask, "\n" ; - - if ($ip->within(new NetAddr::IP "127.0.0.0", "255.0.0.0")) { - print "Is a loopback address\n"; - } - - # This prints 127.0.0.1/32 - print "You can also say $ip...\n"; - -=head1 DESCRIPTION - -This module provides a number of methods useful for handling IPv4 -addresses ans subnets. Hopefully, its methods are also usable for IPv6 -addresses. - -Methods so far include: - -=over - -=item C<-Enew([$addr, [ $mask ]])> - -This method creates a new IPv4 address with the supplied address in -C<$addr> and an optional netmask C<$mask>, which can be omitted to get -a /32 mask. - -C<$addr> can be almost anything that can be resolved to an IP address -in all the notations I have seen over time. It can optionally contain -the mask in CIDR notation. - -B notation is understood, with the limitation that the range -speficied by the prefix must match with a valid subnet. - -If called with no arguments, 'default' is assumed. - -=item C<-Ebroadcast()> - -Returns a new object refering to the broadcast address of a given -subnet. - -=item C<-Enetwork()> - -Returns a new object refering to the network address of a given -subnet. - -=item C<-Eaddr()> - -Returns a scalar with the address part of the object as a dotted-quad. - -=item C<-Emask()> - -Returns a scalar with the mask as a dotted-quad. - -=item C<-Emasklen()> - -Returns a scalar the number of one bits in the mask. - -=item C<-Ecidr()> - -Returns a scalar with the address and mask in CIDR notation. - -=item C<-Erange()> - -Returns a scalar with the base address and the broadcast address -separated by a dash and spaces. This is called range notation. - -=item C<-Eprefix()> - -Returns a scalar with the address and mask in prefix -representation. This is useful for some programs, which expect its -input to be in this format. This method will include the broadcast -address in the encoding. - -=item C<-Enprefix()> - -Just as C<-Eprefix()>, but does not include the broadcast address. - -=item C<-Enumeric()> - -When called in a scalar context, will return a numeric representation -of the address part of the IP address. When called in an array -contest, it returns a list of two elements. The first element is as -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 -returned otherwise and C is returned if C<$me> and C<$other> -are of different versions. - -=item C<$me-Ewithin($other)> - -The complement of C<-Econtains()>. Returns true when C<$me> is -completely con tained within C<$other>. - -=item C<-Esplit($bits)> - -Returns a list of objects, representing subnets of C<$bits> mask -produced by splitting the original object, which is left -unchanged. Note that C<$bits> must be longer than the original -object's mask in order for it to be splittable. - -Note that C<$bits> can be given as an integer (the length of the mask) -or as a dotted-quad. If omitted, a host mask is assumed. - -=item C<-Esplitref($bits)> - -A (faster) version of C<-Esplit()> that returns a reference to a -list of objects instead of a real list. This is useful when large -numbers of objects are expected. - -=item C<-Ehostenum()> - -Returns the list of hosts within a subnet. - -=item C<-Ehostenumref()> - -Faster version of C<-Ehostenum()>, returning a reference to a list. - -=item C<$me-Ecompact($addr1, $addr2, ...)> - -Given a list of objects (including C<$me>), this method will compact -all the addresses and subnets into the largest (ie, least specific) -subnets possible that contain exactly all of the given objects. - -Note that in versions prior to 3.02, if fed with the same IP subnets -multiple times, these subnets would be returned. From 3.02 on, a more -"correct" approach has been adopted and only one address would be -returned. - -=item C<$me-Ecompactref(\@list)> - -As usual, a faster version of =item C<-Ecompact()> that returns a -reference to a list. Note that this method takes a reference to a list -instead. - -=item C<-Efirst()> - -Returns a new object representing the first useable IP address within -the subnet (ie, the first host address). - -=item C<-Elast()> - -Returns a new object representing the last useable IP address within -the subnet (ie, one less than the broadcast address). +=pod =item C<-Enth($index)> @@ -1075,53 +1182,35 @@ (for example, when the network is too small for C<$index> hosts), C is returned. +=cut + +sub nth ($$) { + my $self = shift; + my $count = shift; + + return undef if ($count < 1 or $count > $self->num ()); + return $self->network + $count; +} + +=pod + =item C<-Enum()> Returns the number of useable addresses IP addresses within the subnet, not counting the broadcast address. -=back +=cut -In addition to the methods, some functions are overloaded to ease -manipulation of the objects. The available operations are: +sub num ($) { + my $self = shift; + return ~vec($self->{mask}, 0, $self->{bits}) & 0xFFFFFFFF; +} -=over + # Output a vec() as a dotted-quad -=item B +1; -An object can be used just as a string. For instance, the following code - - my $ip = new NetAddr::IP 'loopback'; - print "$ip\n"; - -Will print the string 127.0.0.1/8. - -=item B - -You can test for equality with either C or C<==>. - -=item B - -You can do something along the lines of - - my $net = new NetAddr::IP $cidr_spec; - for my $ip (@$net) { - print "Host $ip is in $net\n"; - } - -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. +__END__ =back @@ -1132,6 +1221,8 @@ =head1 HISTORY +$Id: IP.pm,v 1.2 2002/10/31 04:30:22 lem Exp $ + =over =item 0.01 @@ -1361,8 +1452,6 @@ =item 3.00 -=over - This is a major rewrite, supposed to fix a number of issues pointed out in earlier versions. @@ -1379,8 +1468,6 @@ HP-UX11 on PA-RISC (5.6.0), RedHat Linux 6.2 (5.6.0), Digital Unix on Alpha (5.6.0), Solaris on Sparc (5.6.0) and possibly others. -=back - =item 3.01 =over @@ -1546,6 +1633,37 @@ =back +=item 3.12 + +=over + +=item * + +Added CVS control files, though this is of no relevance to the community. + +=item * + +Thanks to Steve Snodgrass for pointing out a bug in the processing of +the special names such as default, any, etc. A fix was produced and +adequate tests were added to the code. + +=item * + +First steps towards "regexp free" parsing. + +=item * + +Documentation revisited and reorganized within the file, so that it +helps document the code. + +=item * + +Added C<-Eaton()> and support for this format in +C<-Enew()>. This makes the code helpful to interface with +old-style socket code. + +=back + =back =head1 AUTHOR @@ -1569,3 +1687,4 @@ perl(1). =cut + diff --git a/MANIFEST b/MANIFEST index 44e8ed2..5234d43 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,4 +1,5 @@ IP.pm +TODO README MANIFEST Makefile.PL @@ -8,9 +9,11 @@ t/v4-new.t t/v4-num.t t/relops.t +t/v4-aton.t t/v4-snew.t t/v4-cnew.t t/v4-last.t +t/v4-wnew.t t/over-qq.t t/masklen.t t/v4-base.t diff --git a/Makefile.PL b/Makefile.PL index 27cc5df..1b47bff 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -1,8 +1,13 @@ use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. + +# $Id: Makefile.PL,v 1.2 2002/10/31 04:30:22 lem Exp $ + WriteMakefile( 'NAME' => 'NetAddr::IP', 'VERSION_FROM' => 'IP.pm', # finds $VERSION - 'PREREQ_PM' => {}, # e.g., Module::Name => 1.1 + 'PREREQ_PM' => { + Test::More => 0, + }, # e.g., Module::Name => 1.1 ); diff --git a/README b/README index 8fefa86..1681899 100644 --- a/README +++ b/README @@ -60,6 +60,14 @@ fast. If you're upgrading from 2.xx, please review your code as some methods no longer exist or have changed. +********************************************************************** +* VERSIONS EARLIER THAN 3.00 WON'T BE SUPPORTED AT ALL. PLEASE DON'T * +* EVEN ASK. I have very limited time and this module has grown quite * +* popular. Please help me help you by staying up to date with it. If * +* you find a bug, upgrade to the latest version on CPAN prior to * +* contacting me. * +********************************************************************** + To install, follow the standard CPAN recipe of: $ perl Makefile.PL @@ -70,6 +78,11 @@ $ make install +The test suite includes a lot of cases. Note that currently, some +tests require Test::More. Eventually all tests will require it, so you +should really consider installing it if your Perl did not include this +module. + Tests related to address compaction could be too resource-intensive in some environments. If this is your case, you can skip those tests by setting an environment variable before make'ing test. In a bash-like @@ -100,3 +113,5 @@ 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. + +$Id: README,v 1.2 2002/10/31 04:30:22 lem Exp $ diff --git a/TODO b/TODO new file mode 100644 index 0000000..f5f98b8 --- /dev/null +++ b/TODO @@ -0,0 +1,4 @@ + +$Id: TODO,v 1.1 2002/10/31 04:30:23 lem Exp $ + +o Definitely, support for IPv6 addresses. diff --git a/t/00-load.t b/t/00-load.t index 432f894..4b9ed63 100644 --- a/t/00-load.t +++ b/t/00-load.t @@ -1,10 +1,7 @@ -# Before `make install' is performed this script should be runnable with -# `make test'. After `make install' it should work as `perl test.pl' -######################### We start with some black magic to print on failure. +# Test if the module will load correctly -# Change 1..1 below to 1..last_test_to_print . -# (It may become useful if the test is moved to ./t subdirectory.) +# $Id: 00-load.t,v 1.2 2002/10/31 04:30:35 lem Exp $ BEGIN { $| = 1; print "1..1\n"; } END {print "not ok 1\n" unless $loaded;} @@ -12,9 +9,3 @@ $loaded = 1; print "ok 1\n"; -######################### End of black magic. - -# Insert your test code below (better if it prints "ok 13" -# (correspondingly "not ok 13") depending on the success of chunk 13 -# of the test code): - diff --git a/t/loops.t b/t/loops.t index 31f9555..5cbef31 100644 --- a/t/loops.t +++ b/t/loops.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: loops.t,v 1.2 2002/10/31 04:30:35 lem Exp $ + $| = 1; my @deltas = (0, 1, 2, 3, 255); diff --git a/t/masklen.t b/t/masklen.t index 175e013..bd28ee2 100644 --- a/t/masklen.t +++ b/t/masklen.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: masklen.t,v 1.2 2002/10/31 04:30:35 lem Exp $ + my @masks = 0 .. 32; $| = 1; diff --git a/t/over-arr.t b/t/over-arr.t index b9c839d..092442a 100644 --- a/t/over-arr.t +++ b/t/over-arr.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: over-arr.t,v 1.2 2002/10/31 04:30:35 lem Exp $ + my @addr = ( [ '10.0.0.0/24', '10.0.0.1/32' ], [ '192.168.0.0/24', '192.168.0.1/32' ], [ '127.0.0.1/32', '127.0.0.1/32' ] ); diff --git a/t/over-qq.t b/t/over-qq.t index 5f8426b..1fca4b3 100644 --- a/t/over-qq.t +++ b/t/over-qq.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: over-qq.t,v 1.2 2002/10/31 04:30:35 lem Exp $ + my @addr = ('10.0.0.0/8', '192.168.0.0/16', '127.0.0.1/32'); $| = 1; diff --git a/t/relops.t b/t/relops.t index be7fcf0..ed7a2a8 100644 --- a/t/relops.t +++ b/t/relops.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: relops.t,v 1.2 2002/10/31 04:30:35 lem Exp $ + my @gt = ( [ '255.255.255.255/32', '0.0.0.0/0' ], [ '10.0.1.0/16', '10.0.0.1/24' ], diff --git a/t/v4-aton.t b/t/v4-aton.t new file mode 100644 index 0000000..d48c065 --- /dev/null +++ b/t/v4-aton.t @@ -0,0 +1,22 @@ +use Test::More tests => 12; +use Socket; +use NetAddr::IP; + +# $Id$ + +my @addr = ( + [ 'localhost', '127.0.0.1' ], + [ 'broadcast', '255.255.255.255' ], + [ 'default', '0.0.0.0' ], + [ '10.0.0.1', '10.0.0.1' ], + +); + +is(NetAddr::IP->new($_->[0])->aton, inet_aton($_->[1]), "->aton($_->[0])") + for @addr; + +ok(defined NetAddr::IP->new(inet_aton($_->[1])), "->new aton($_->[1])") + for @addr; + +is(NetAddr::IP->new(inet_aton($_->[1]))->addr, $_->[1], "->new aton($_->[1])") + for @addr; diff --git a/t/v4-badnm.t b/t/v4-badnm.t index a69da61..096a371 100644 --- a/t/v4-badnm.t +++ b/t/v4-badnm.t @@ -1,5 +1,7 @@ # I know this does not look like -*- perl -*-, but I swear it is... +# $Id: v4-badnm.t,v 1.2 2002/10/31 04:30:35 lem Exp $ + use NetAddr::IP; use strict; diff --git a/t/v4-base.t b/t/v4-base.t index e1facd5..5358c1e 100644 --- a/t/v4-base.t +++ b/t/v4-base.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-base.t,v 1.2 2002/10/31 04:30:35 lem Exp $ + my @addr = (qw( 127.0.0.1 10.0.0.1 )); my @mask = (qw( 255.0.0.0 255.255.0.0 255.255.255.0 255.255.255.255 )); diff --git a/t/v4-basem.t b/t/v4-basem.t index 3346a39..7f4b36a 100644 --- a/t/v4-basem.t +++ b/t/v4-basem.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-basem.t,v 1.2 2002/10/31 04:30:35 lem Exp $ + my $nets = { '10.0.0.16' => [ 24, '10.0.0.255', '10.0.0.0' ], '127.0.0.1' => [ 8, '127.255.255.255', '127.0.0.0' ], diff --git a/t/v4-cidr.t b/t/v4-cidr.t index 7ebd20e..efa4316 100644 --- a/t/v4-cidr.t +++ b/t/v4-cidr.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-cidr.t,v 1.2 2002/10/31 04:30:35 lem Exp $ + $| = 1; my @addr = (qw( diff --git a/t/v4-cnew.t b/t/v4-cnew.t index dbf66d0..0407182 100644 --- a/t/v4-cnew.t +++ b/t/v4-cnew.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-cnew.t,v 1.2 2002/10/31 04:30:35 lem Exp $ + my @subnets = ( [ '127.1', '127.0.0.1/32' ], [ '127.1/16', '127.1.0.0/16' ], diff --git a/t/v4-compact.t b/t/v4-compact.t index 16404a6..207db76 100644 --- a/t/v4-compact.t +++ b/t/v4-compact.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-compact.t,v 1.2 2002/10/31 04:30:36 lem Exp $ + my @r = ( [ '10.0.0.0', '255.255.255.0'], [ '11.0.0.0', '255.255.255.0'], diff --git a/t/v4-compplus.t b/t/v4-compplus.t index f572bf6..6293079 100644 --- a/t/v4-compplus.t +++ b/t/v4-compplus.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-compplus.t,v 1.2 2002/10/31 04:30:36 lem Exp $ + $| = 1; print "1..50\n"; diff --git a/t/v4-contains.t b/t/v4-contains.t index 57816bb..f0d27f5 100644 --- a/t/v4-contains.t +++ b/t/v4-contains.t @@ -1,4 +1,7 @@ use NetAddr::IP; + +# $Id: v4-contains.t,v 1.2 2002/10/31 04:30:36 lem Exp $ + #require "IP.pm"; my @yes_pairs = ( diff --git a/t/v4-first.t b/t/v4-first.t index 6f0a7a5..270dce3 100644 --- a/t/v4-first.t +++ b/t/v4-first.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-first.t,v 1.2 2002/10/31 04:30:36 lem Exp $ + my $nets = { '10.0.0.16' => [ 24, '10.0.0.1', '10.0.0.254', '10.0.0.10'], '10.0.0.5' => [ 30, '10.0.0.5', '10.0.0.6', 'undef' ], diff --git a/t/v4-hostenum.t b/t/v4-hostenum.t index a050f4b..5fb432b 100644 --- a/t/v4-hostenum.t +++ b/t/v4-hostenum.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-hostenum.t,v 1.2 2002/10/31 04:30:36 lem Exp $ + my %addr = ( '10.0.0.0' => [ '255.255.255.252', [ diff --git a/t/v4-last.t b/t/v4-last.t index 55fb627..76ee806 100644 --- a/t/v4-last.t +++ b/t/v4-last.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-last.t,v 1.2 2002/10/31 04:30:36 lem Exp $ + my %w = ('default' => [ '255.255.255.254', '0.0.0.0' ], 'loopback' => [ '127.255.255.254', '255.0.0.0' ], '127.0.0.1/8' => [ '127.255.255.254', '255.0.0.0' ], diff --git a/t/v4-new.t b/t/v4-new.t index c31066f..d802523 100644 --- a/t/v4-new.t +++ b/t/v4-new.t @@ -1,6 +1,8 @@ use NetAddr::IP; #require "IP.pm"; +# $Id: v4-new.t,v 1.2 2002/10/31 04:30:36 lem Exp $ + my @a = ( [ 'localhost', '127.0.0.1' ], [ 0x01010101, '1.1.1.1' ], diff --git a/t/v4-num.t b/t/v4-num.t index 868040a..ee58d7d 100644 --- a/t/v4-num.t +++ b/t/v4-num.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-num.t,v 1.2 2002/10/31 04:30:36 lem Exp $ + my $nets = { '10.0.0.16' => [ 24, 255 ], '10.128.0.1' => [ 8, 2 ** 24 - 1 ], diff --git a/t/v4-numeric.t b/t/v4-numeric.t index 0f14d75..d7e8a6f 100644 --- a/t/v4-numeric.t +++ b/t/v4-numeric.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-numeric.t,v 1.2 2002/10/31 04:30:36 lem Exp $ + my $nets = { '10.0.0.0/20' => [ 167772160, 4294963200 ], '10.0.15.0/24' => [ 167776000, 4294967040 ], diff --git a/t/v4-range.t b/t/v4-range.t index d4da8a8..9531367 100644 --- a/t/v4-range.t +++ b/t/v4-range.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-range.t,v 1.2 2002/10/31 04:30:36 lem Exp $ + my @ranges = ( [ '10.0.0.0/8', '10.0.0.0', '10.255.255.255' ], [ '192.168.0.0/16', '192.168.0.0', '192.168.255.255' ], diff --git a/t/v4-snew.t b/t/v4-snew.t index 92f9094..4c23b93 100644 --- a/t/v4-snew.t +++ b/t/v4-snew.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-snew.t,v 1.2 2002/10/31 04:30:36 lem Exp $ + my %w = ('broadcast' => [ '255.255.255.255', '255.255.255.255' ], 'default' => [ '0.0.0.0', '0.0.0.0' ], 'loopback' => [ '127.0.0.1', '255.0.0.0' ], diff --git a/t/v4-split-bulk.t b/t/v4-split-bulk.t index 33d8a7c..46695f3 100644 --- a/t/v4-split-bulk.t +++ b/t/v4-split-bulk.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-split-bulk.t,v 1.2 2002/10/31 04:30:36 lem Exp $ + my @addr = ( [ '10.0.0.0', 20, 32, 4096 ], [ '10.0.0.0', 22, 32, 1024 ], [ '10.0.0.0', 24, 32, 256 ], diff --git a/t/v4-split-list.t b/t/v4-split-list.t index 1113244..64817a2 100644 --- a/t/v4-split-list.t +++ b/t/v4-split-list.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-split-list.t,v 1.2 2002/10/31 04:30:37 lem Exp $ + my %addr = ( '10.0.0.10' => [ '255.255.252.0', 24, [ diff --git a/t/v4-sprefix.t b/t/v4-sprefix.t index 88d9e58..eabdf6a 100644 --- a/t/v4-sprefix.t +++ b/t/v4-sprefix.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-sprefix.t,v 1.2 2002/10/31 04:30:37 lem Exp $ + my @addr = ( [ '10.', '10.0.0.0/8' ], [ '11.11.', '11.11.0.0/16' ], diff --git a/t/v4-wnew.t b/t/v4-wnew.t new file mode 100644 index 0000000..b366d82 --- /dev/null +++ b/t/v4-wnew.t @@ -0,0 +1,15 @@ +use Test::More tests => 12; +use NetAddr::IP; + +# $Id: v4-wnew.t,v 1.1 2002/10/31 04:30:37 lem Exp $ + +my @good = (qw(default any broadcast loopback)); +my @bad = map { ("$_.neveranydomainlikethis", + "nohostlikethis.$_") } @good; + +ok(defined NetAddr::IP->new($_), "defined ->new($_)") + for @good; + +ok(! defined NetAddr::IP->new($_), "not defined ->new($_)") + for @bad; + diff --git a/t/v4-xprefix.t b/t/v4-xprefix.t index b37620d..f1ac9ed 100644 --- a/t/v4-xprefix.t +++ b/t/v4-xprefix.t @@ -1,5 +1,7 @@ use NetAddr::IP; +# $Id: v4-xprefix.t,v 1.2 2002/10/31 04:30:37 lem Exp $ + my @addr = ( [ '0.0.0.0/1', '0-127' ], [ '128.0.0.0/1', '128-255' ], diff --git a/tutorial.htm b/tutorial.htm index 7d3540e..9b252d7 100644 --- a/tutorial.htm +++ b/tutorial.htm @@ -1,3 +1,9 @@ + + +A tutorial for NetAddr::IP + + +$Id: tutorial.htm,v 1.2 2002/10/31 04:30:23 lem Exp $

What this tutorial is all about...

@@ -535,3 +541,4 @@ subnetting, but we'll leave that discussion to other tutorials.
+ \ No newline at end of file