File Coverage

blib/lib/Paranoid/Network/IPv4.pm
Criterion Covered Total %
statement 107 109 98.1
branch 28 34 82.3
condition 13 21 61.9
subroutine 21 21 100.0
pod 5 5 100.0
total 174 190 91.5


line stmt bran cond sub pod time code
1             # Paranoid::Network::IPv4 -- IPv4-specific network functions
2             #
3             # $Id: lib/Paranoid/Network/IPv4.pm, 2.10 2022/03/08 00:01:04 acorliss Exp $
4             #
5             # This software is free software. Similar to Perl, you can redistribute it
6             # and/or modify it under the terms of either:
7             #
8             # a) the GNU General Public License
9             # as published by the
10             # Free Software Foundation ; either version 1
11             # , or any later version
12             # , or
13             # b) the Artistic License 2.0
14             # ,
15             #
16             # subject to the following additional term: No trademark rights to
17             # "Paranoid" have been or are conveyed under any of the above licenses.
18             # However, "Paranoid" may be used fairly to describe this unmodified
19             # software, in good faith, but not as a trademark.
20             #
21             # (c) 2005 - 2020, Arthur Corliss (corliss@digitalmages.com)
22             # (tm) 2008 - 2020, Paranoid Inc. (www.paranoid.com)
23             #
24             #####################################################################
25              
26             #####################################################################
27             #
28             # Environment definitions
29             #
30             #####################################################################
31              
32             package Paranoid::Network::IPv4;
33              
34 3     3   491 use 5.008;
  3         8  
35              
36 3     3   13 use strict;
  3         4  
  3         47  
37 3     3   10 use warnings;
  3         5  
  3         91  
38 3     3   14 use vars qw($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS);
  3         4  
  3         211  
39 3     3   16 use base qw(Exporter);
  3         5  
  3         158  
40 3     3   13 use Paranoid;
  3         5  
  3         175  
41 3     3   24 use Paranoid::Debug qw(:all);
  3         5  
  3         470  
42 3     3   357 use Paranoid::Network::Socket;
  3         4  
  3         1511  
43              
44             my @base = qw(ipv4NetConvert ipv4NetIntersect);
45             my @constants = qw(MAXIPV4CIDR IPV4REGEX IPV4CIDRRGX IPV4BASE IPV4BRDCST
46             IPV4MASK);
47             my @ipv4sort = qw(ipv4NumSort ipv4StrSort ipv4PackedSort);
48              
49             ($VERSION) = ( q$Revision: 2.10 $ =~ /(\d+(?:\.\d+)+)/sm );
50             @EXPORT = @base;
51             @EXPORT_OK = ( @base, @constants, @ipv4sort );
52             %EXPORT_TAGS = (
53             all => [@EXPORT_OK],
54             base => [@base],
55             constants => [@constants],
56             ipv4Sort => [@ipv4sort],
57             );
58              
59 3     3   26 use constant MAXIPV4CIDR => 32;
  3         6  
  3         318  
60 3         223 use constant IPV4REGEX =>
61 3     3   18 qr/(?:(?:25[0-5]|2[0-4][0-9]|1?\d\d?)\.){3}(?:25[0-5]|2[0-4][0-9]|1?\d\d?)/s;
  3         6  
62 3         4 use constant IPV4CIDRRGX =>
63 3     3   34 qr#@{[ IPV4REGEX ]}/(?:(?:3[0-2]|[12]?\d)|@{[ IPV4REGEX ]})#s;
  3         6  
  3         7  
  3         362  
64 3     3   18 use constant FULLMASK => 0xffffffff;
  3         5  
  3         138  
65 3     3   17 use constant IPV4BASE => 0;
  3         5  
  3         109  
66 3     3   40 use constant IPV4BRDCST => 1;
  3         7  
  3         133  
67 3     3   17 use constant IPV4MASK => 2;
  3         4  
  3         1670  
68              
69             #####################################################################
70             #
71             # Module code follows
72             #
73             #####################################################################
74              
75             sub ipv4NetConvert {
76              
77             # Purpose: Takes a string representation of an IPv4 network
78             # address and returns a list containing the binary
79             # network address, broadcast address, and netmask.
80             # Also allows for a plain IP being passed, in which
81             # case it only returns the binary IP.
82             # Returns: Array, empty on errors
83             # Usage: @network = ipv4NetConvert($netAddr);
84              
85 41     41 1 5809 my $netAddr = shift;
86 41         61 my ( $bnet, $bmask, $t, @rv );
87              
88 41         89 subPreamble( PDLEVEL1, '$', $netAddr );
89              
90             # Extract net address, mask
91 41 100       69 if ( defined $netAddr ) {
92 40         50 ($t) = ( $netAddr =~ m#^(@{[ IPV4CIDRRGX ]}|@{[ IPV4REGEX ]})$#s )[0];
  40         61  
  40         527  
93 40 100       155 ( $bnet, $bmask ) = split m#/#s, $t if defined $t;
94             }
95              
96 41 100 66     117 if ( defined $bnet and length $bnet ) {
97              
98             # First, convert $bnet to see if we have a valid IP address
99 33         153 $bnet = unpack 'N', inet_aton($bnet);
100              
101 33 50 33     106 if ( defined $bnet and length $bnet ) {
102              
103             # Save our network address
104 33         56 push @rv, $bnet;
105              
106 33 100 66     79 if ( defined $bmask and length $bmask ) {
107              
108             # Convert netmask
109 24 50       116 $bmask =
    100          
110             $bmask !~ /^\d+$/s ? unpack 'N', inet_aton($bmask)
111             : $bmask <= MAXIPV4CIDR
112             ? FULLMASK - ( ( 2**( MAXIPV4CIDR - $bmask ) ) - 1 )
113             : undef;
114              
115 24 50 33     77 if ( defined $bmask and length $bmask ) {
116              
117             # Apply the mask to the base address
118 24         35 $rv[IPV4BASE] = $rv[IPV4BASE] & $bmask;
119              
120             # Calculate and save our broadcast address
121 24         39 push @rv, $bnet | ( $bmask ^ FULLMASK );
122              
123             # Save our mask
124 24         31 push @rv, $bmask;
125              
126             } else {
127 0         0 pdebug( 'invalid netmask passed', PDLEVEL1 );
128             }
129             }
130             } else {
131 0         0 pdebug( 'failed to convert IPv4 address', PDLEVEL1 );
132             }
133             } else {
134 8         17 pdebug( 'failed to extract an IPv4 address', PDLEVEL1 );
135             }
136              
137 41         103 subPostamble( PDLEVEL1, '@', @rv );
138              
139 41         82 return @rv;
140             }
141              
142             sub ipv4NetIntersect {
143              
144             # Purpose: Tests whether network address ranges intersect
145             # Returns: Integer, denoting whether an intersection exists, and what
146             # kind:
147             #
148             # -1: destination range encompasses target range
149             # 0: both ranges do not intersect at all
150             # 1: target range encompasses destination range
151             #
152             # Usage: $rv = ipv4NetIntersect($net1, $net2);
153              
154 18     18 1 443 my $tgt = shift;
155 18         25 my $dest = shift;
156 18         21 my $rv = 0;
157 18         22 my ( @tnet, @dnet );
158              
159 18         50 subPreamble( PDLEVEL1, '$$', $tgt, $dest );
160              
161             # Bypas if one or both isn't defined -- obviously no intersection
162 18 50 33     61 unless ( !defined $tgt or !defined $dest ) {
163              
164             # Convert addresses (also allows for raw IPs (32bit integers) to be
165             # passed)
166 18 50       64 @tnet = $tgt =~ /^\d+$/s ? ($tgt) : ipv4NetConvert($tgt);
167 18 50       59 @dnet = $dest =~ /^\d+$/s ? ($dest) : ipv4NetConvert($dest);
168              
169             # insert bogus numbers for non IP-address info
170 18 100       33 @tnet = (-1) unless scalar @tnet;
171 18 100       34 @dnet = (-2) unless scalar @dnet;
172              
173             # Dummy up broadcast address for those single IPs passed (in lieu of
174             # network ranges)
175 18 100       33 $tnet[IPV4BRDCST] = $tnet[IPV4BASE] if $#tnet == 0;
176 18 100       34 $dnet[IPV4BRDCST] = $dnet[IPV4BASE] if $#dnet == 0;
177              
178 18 100 100     73 if ( $tnet[IPV4BASE] <= $dnet[IPV4BASE]
    100 100        
179             and $tnet[IPV4BRDCST] >= $dnet[IPV4BRDCST] ) {
180              
181             # Target fully encapsulates dest
182 4         7 $rv = 1;
183              
184             } elsif ( $tnet[IPV4BASE] >= $dnet[IPV4BASE]
185             and $tnet[IPV4BRDCST] <= $dnet[IPV4BRDCST] ) {
186              
187             # Dest fully encapsulates target
188 7         18 $rv = -1;
189              
190             }
191             }
192              
193 18         44 subPostamble( PDLEVEL1, '$', $rv );
194              
195 18         56 return $rv;
196             }
197              
198             {
199              
200 3     3   20 no strict 'refs';
  3         4  
  3         807  
201              
202             sub ipv4NumSort {
203              
204             # Purpose: Sorts IPv4 addresses represented in numeric form
205             # Returns: -1, 0, 1
206             # Usage: @sorted = sort &ipv4NumSort @ipv4;
207              
208 2     2 1 1245 my ($pkg) = caller;
209              
210 2         3 return ${"${pkg}::a"} <=> ${"${pkg}::b"};
  2         5  
  2         6  
211             }
212              
213             sub ipv4PackedSort {
214              
215             # Purpose: Sorts IPv4 addresses represented in packed strings
216             # Returns: -1, 0, 1
217             # Usage: @sorted = sort &ipv4PackedSort @ipv4;
218              
219 2     2 1 1309 my ($pkg) = caller;
220              
221 2         3 my $a1 = unpack 'N', ${"${pkg}::a"};
  2         7  
222 2         3 my $b1 = unpack 'N', ${"${pkg}::b"};
  2         3  
223              
224 2         7 return $a1 <=> $b1;
225             }
226              
227             sub ipv4StrSort {
228              
229             # Purpose: Sorts IPv4 addresses represented in string form
230             # Returns: -1, 0, 1
231             # Usage: @sorted = sort &ipv4StrSort @ipv4;
232              
233 2     2 1 6 my ($pkg) = caller;
234              
235 2         4 my $a1 = ${"${pkg}::a"};
  2         5  
236 2         4 my $b1 = ${"${pkg}::b"};
  2         3  
237              
238 2         4 $a1 =~ s#/.+##s;
239 2         7 $a1 = unpack 'N', inet_aton($a1);
240 2         7 $b1 =~ s#/.+##s;
241 2         5 $b1 = unpack 'N', inet_aton($b1);
242              
243 2         6 return $a1 <=> $b1;
244             }
245             }
246              
247             1;
248              
249             __END__