File Coverage

blib/lib/Net/IPAddress/Util.pm
Criterion Covered Total %
statement 266 362 73.4
branch 58 116 50.0
condition 36 74 48.6
subroutine 38 51 74.5
pod 28 28 100.0
total 426 631 67.5


line stmt bran cond sub pod time code
1             package Net::IPAddress::Util;
2              
3 5     5   65091 use 5.012;
  5         35  
4              
5             use overload (
6 5         27 '=' => 'new',
7             '""' => 'str',
8             'cmp' => '_spaceship',
9             '<=>' => '_spaceship',
10             '+' => '_do_add',
11             '-' => '_do_subtract',
12             '<<' => '_shift_left',
13             '>>' => '_shift_right',
14             '&' => '_band',
15             '|' => '_bor',
16             '~' => '_neg',
17 5     5   5079 );
  5         4662  
18              
19 5     5   893 use Carp qw( carp cluck confess );
  5         9  
  5         413  
20 5     5   27 use Exporter qw( import );
  5         9  
  5         167  
21 5     5   2450 use List::MoreUtils qw( pairwise );
  5         58417  
  5         26  
22              
23             our %EXPORT_TAGS = (
24             constr => [qw( IP n32_to_ipv4 )],
25             manip => [qw( explode_ip implode_ip ip_pad_prefix common_prefix prefix_mask ipv4_mask ipv4_flag )],
26             sort => [qw( radix_sort )],
27             compat => [qw( ip2num num2ip validaddr mask fqdn )]
28             );
29              
30             my %EXPORT_OK;
31             for my $k (keys %EXPORT_TAGS) {
32             for my $v (@{$EXPORT_TAGS{ $k }}) {
33             undef $EXPORT_OK{ $v };
34             }
35             }
36              
37             our @EXPORT_OK = keys %EXPORT_OK;
38              
39             $EXPORT_TAGS{ all } = [ @EXPORT_OK ];
40              
41             our $DIE_ON_ERROR = 0;
42             our $PROMOTE_N32 = 1;
43             our $REPAIR_V3_FORMAT = 0;
44             our $WARN_ON_REPAIR = 1;
45              
46             our @SIIT = (
47             [ 0, 0, 0xff, 0xff ], # off
48             [ 0xff, 0xff, 0, 0 ], # on
49             );
50              
51             our $VERSION = '5.000';
52              
53             our $siit_fourish = qr/^(?:::ffff:0+:)?(\d+)\.(\d+)\.(\d+)\.(\d+)$/io;
54             our $fourish = qr/^(?:::ffff:)?(\d+)\.(\d+)\.(\d+)\.(\d+)$/io;
55             our $numberish = qr/^(\d+)$/o;
56             our $normalish = qr/^([0-9a-f]{32})$/io;
57             our $sixish = qr/^([0-9a-f:]+)(?:\%.*)?$/io;
58              
59             *__debug
60             = $ENV{ IP_UTIL_DEBUG }
61             ? sub { my ($p, $f, $l) = caller(); warn('@' . " $l: " . join(', ', @_) . "\n"); }
62       7451     : sub { }
63             ;
64              
65             *__dnorm
66             = $ENV{ IP_UTIL_DEBUG }
67             ? sub {
68             my ($p, $f, $l) = caller();
69             my $n = 0;
70             for my $x (@_) {
71             if (eval { @$x }) {
72             warn($n++ . ' @' . " $l: [" . join(', ', map { sprintf('%x', $_) } @$x) . "]\n");
73             }
74             else {
75             warn($n++ . ' @' . " $l: " . $x . "\n");
76             }
77             }
78             }
79       1562     : sub { }
80             ;
81              
82             sub SIIT {
83 16     16 1 28 my $self = shift;
84 16         29 my ($do) = @_;
85 16   50     55 $do //= 1;
86 16         31 $self = _set_SIIT($self, $do);
87 16         20 $self->{ SIIT } = $do;
88 16         35 return $self;
89             }
90              
91             sub _set_SIIT {
92 16     16   29 my ($old, $do) = @_;
93 16         151 my $normal = [ unpack('C16', $old->{ address }) ];
94 16 100 100     40 if (
95 128         174 !(grep { $_ } @$normal[ 0 .. 7 ])
96 44         126 && (grep { $normal->[ $_ ] == $SIIT[!!$do]->[ $_ ] } (8 .. 11)) == 4
97             ) {
98 1         7 $normal->[ $_ ] = $SIIT[!$do]->[ $_ ] for (8 .. 11);
99             }
100 16         51 $old->{ address } = pack('C16', @$normal);
101 16         47 return $old;
102             }
103              
104             sub IP {
105 545     545 1 11521 return __PACKAGE__->new(@_);
106             }
107              
108             sub new {
109 2107     2107 1 2926 my $self = shift;
110 2107   33     5115 my $class = ref($self) || $self;
111 2107         3115 my ($address, %opt) = @_;
112             __debug(
113 2107         6137 eval { @$address }
114 2107 100       2642 ? join(' ', map { sprintf($_ > 255 ? '0x%08x' : '%3s', $_ ) } @$address)
  17648 100       37277  
115             : $address
116             );
117 2107         3537 my @siit_prefix = @{$SIIT[$opt{ SIIT }
118             ||= (
119 2107   100     8978 !ref($address)
      66        
120             and
121             $address =~ $siit_fourish
122             and
123             $address !~ $fourish
124             )
125             ]};
126 2107 100       5959 __debug('is ' . ($opt{ SIIT } ? 'not ' : '') . 'SIIT looking');
127 2107   66     4593 my $promote = $opt{ promote } // $PROMOTE_N32;
128 2107         2876 my $normal = [ ];
129 2107 50       3266 if (!defined $address) {
130 0         0 $normal = [
131             0, 0, 0, 0,
132             0, 0, 0, 0,
133             0, 0, 0, 0,
134             0, 0, 0, 0,
135             ];
136 0         0 __dnorm($normal);
137             }
138 2107 100 100     7359 if (ref($address) eq 'ARRAY' && @$address == 16) {
    100 66        
    100 66        
    100 100        
    50 33        
    50 0        
    100 33        
    50 66        
    0 33        
139 950         1664 __debug('16 element array');
140 950         1169 $normal = $address;
141 950         1378 __dnorm($normal);
142             }
143             elsif (ref($address) eq 'ARRAY' && @$address == 4) {
144             # FIXME: Principal of least surprise here? Should feeding in 4 values make an IPv4?
145 612         1107 __debug('4 element array');
146 612         1905 $normal = [ unpack 'C16', pack 'N4', @$address ];
147 612         1097 __dnorm($normal);
148             }
149 494         1603 elsif (ref $address and eval { $address->isa(__PACKAGE__) }) {
150 494         1335 $normal = [ unpack 'C16', $address->{ address } ];
151             }
152             elsif ($address =~ $fourish || $address =~ $siit_fourish) {
153 31         134 my @addr = ($1, $2, $3, $4);
154             # FIXME: Why can't we do this all on one line?
155 31 100       51 @addr = map { $_ =~ /^0/ ? oct($_) : int($_) } @addr;
  124         315  
156 31         84 $normal = [
157             0, 0, 0, 0,
158             0, 0, 0, 0,
159             @siit_prefix,
160             @addr
161             ];
162             }
163             elsif (
164             $opt{ promote }
165             and $address =~ $numberish
166             and 0 <= $address && $address <= (2 ** 32) - 1
167             ) {
168 0         0 $normal = [
169             0, 0, 0, 0,
170             0, 0, 0, 0,
171             @siit_prefix,
172             unpack('C4', pack('N', $address))
173             ];
174             }
175             elsif ("$address" =~ $normalish) {
176 0         0 my $fresh = $1;
177 0 0       0 eval "require Math::BigInt" or return ERROR("Could not load Math::BigInt: $@");
178 0         0 my $raw = Math::BigInt->from_hex("$fresh");
179 0         0 while ($raw > 0) {
180 0         0 my $word = $raw->copy->band(0xffffffff);
181 0         0 unshift @$normal, unpack('C4', pack('N', $word));
182 0         0 $raw = $raw->copy->brsft(32);
183             }
184 0         0 while (@$normal < 16) {
185 0         0 unshift @$normal, 0;
186             }
187 0         0 eval "no Math::BigInt";
188             }
189             elsif ($address =~ $numberish) {
190 4 50       193 eval "require Math::BigInt" or return ERROR("Could not load Math::BigInt: $@");
191 4         46 my $raw = Math::BigInt->new("$address");
192 4         257 while ($raw > 0) {
193 6         1732 my $word = $raw->copy->band(0xffffffff);
194 6         2349 unshift @$normal, unpack('C4', pack('N', $word));
195 6         246 $raw = $raw->copy->brsft(32);
196             }
197 4         1670 while (@$normal < 16) {
198 40         66 unshift @$normal, 0;
199             }
200 4     2   271 eval "no Math::BigInt";
  2     2   12  
  2         4  
  2         38  
  2         12  
  2         4  
  2         26  
201             }
202             elsif (
203             $address =~ $sixish
204             and (
205             scalar(grep { /::/o } split(/[[:alnum:]]+/, $address)) == 1
206             or scalar(grep { /[[:alnum:]]+/ } split(/:/, $address)) == 8
207             )
208             ) {
209             # new() from IPv6 address, accepting and ignoring the Scope ID
210 16         38 $address = $1;
211 16         48 my ($lhs, $rhs) = split /::/, $address;
212 16 100       31 $rhs = '' unless defined $rhs;
213 16         42 my $hex = '0' x 32;
214 16         35 $lhs = join '', map { substr('0000' . $_, -4) } split /:/, $lhs;
  30         92  
215 16         42 $rhs = join '', map { substr('0000' . $_, -4) } split /:/, $rhs;
  19         51  
216 16         32 substr($hex, 0, length($lhs)) = $lhs;
217 16         24 substr($hex, - length($rhs), length($rhs)) = $rhs;
218 16         83 my @hex = split //, $hex;
219 16         37 while (@hex) {
220 256         533 push @$normal, hex(join('', splice(@hex, 0, 2)));
221             }
222             }
223             elsif (length($address) == 16) {
224 0         0 $normal = [ unpack('C16', $address) ];
225             }
226             else {
227 0   0     0 return ERROR("Invalid argument `$address', a(n) " . (ref($address) || 'bare scalar') . ' provided');
228             }
229             # warn(join(',', @$normal) . "\n");
230 2107         17204 return bless { address => pack('C16', @$normal), %opt } => $class;
231             }
232              
233             sub is_ipv4 {
234 1675     1675 1 2005 my $self = shift;
235 1675         3575 my @octets = unpack 'C16', $self->{ address };
236 1675         2472 __debug(join(' ', map { sprintf('%3s', $_) } @octets));
  26800         46870  
237             # my $is_siit = $self->{ SIIT } || 0;
238 1675 100       3836 return 0 if grep { $_ } @octets[ 0 .. 7 ];
  13400         17978  
239 1559         3739 return 1;
240             }
241              
242             sub ipv4 {
243 1055     1055 1 1228 my $self = shift;
244 1055         5106 return join '.', unpack 'C4', substr($self->{ address }, -4);
245             }
246              
247             sub as_n32 {
248 4     4 1 10 my $self = shift;
249 4         20 return unpack 'N', substr($self->{ address }, -4);
250             }
251              
252             sub as_n128 {
253 2     2 1 6 my $self = shift;
254 2         4 my ($keep) = @_;
255 2         4 my $rv;
256             {
257 2 50       4 eval "require Math::BigInt" or return ERROR("Could not load Math::BigInt: $@");
  2         128  
258 2         42317 my $accum = Math::BigInt->new(hex($self->normal_form));
259 2 50   2   38676 eval "no Math::BigInt" unless $keep;
  2         17  
  2         4  
  2         31  
260 2 50       13 $rv = $keep ? $accum : "$accum";
261             }
262 2         139 return $rv;
263             }
264              
265             sub normal_form {
266 50     50 1 97 my $self = shift;
267 50         207 my @addr = unpack('C16', $self->{ address });
268 50 100       91 splice(@addr, 8, 4, @{$SIIT[$self->{ SIIT }]}) if $self->is_ipv4;
  10         39  
269 50         79 my $hex = join('', map { sprintf('%02x', $_) } @addr);
  800         1201  
270 50         136 $hex = substr(('0' x 32) . $hex, -32);
271 50         143 return lc $hex;
272             }
273              
274             sub ipv6_expanded {
275 34     34 1 41 my $self = shift;
276 34         66 my $hex = $self->normal_form();
277 34         39 my $rv;
278 34         152 while ($hex =~ /(....)/g) {
279 272 100       376 $rv .= ':' if defined $rv;
280 272         561 $rv .= $1;
281             }
282 34         67 return $rv;
283             }
284              
285             sub ipv6 {
286 36     36 1 55 my $self = shift;
287 36 100       53 if ($self->is_ipv4()) {
288             return $self->{ SIIT }
289 2 100       10 ? '::ffff:0:' . $self->ipv4()
290             : '::ffff:' . $self->ipv4()
291             ;
292             }
293 34         64 my $iv = $self->ipv6_expanded();
294 34   100     96 my $rv = join(':', map { (my $x = $_) =~ s/^0+//; $x ||= '0'; $x } split ':', $iv);
  272         541  
  272         630  
  272         431  
295 34         141 $rv =~ s/[^[:xdigit:]]0(:0)+/::/;
296 34         89 $rv =~ s/::+/::/g;
297 34         45 $rv =~ s/^0::/::/;
298 34         139 return $rv;
299             }
300              
301 0     0 1 0 sub as_str { return str(@_); }
302              
303 0     0 1 0 sub as_string { return str(@_); }
304              
305             sub str {
306 1085     1085 1 1501 my $self = shift;
307 1085 100       1490 if ($self->is_ipv4()) {
308 1051         1630 return $self->ipv4();
309             }
310 34         74 return $self->ipv6();
311             }
312              
313             sub _spaceship {
314 432     432   612 my $self = shift;
315 432         673 my ($rhs, $swapped) = @_;
316 432         596 my $lhs = $self->{ address };
317 432         806 $lhs = [ unpack 'N4', $lhs ];
318 432   33     643 $rhs = eval { $rhs->{ address } } || pack('N4', (0, 0, 0, $rhs));
319 432         752 $rhs = [ unpack 'N4', $rhs ];
320 432 50       741 ($lhs, $rhs) = ($rhs, $lhs) if $swapped;
321 432   66     2816 return (1 - (2 * $swapped)) * (
322             $lhs->[ 0 ] <=> $rhs->[ 0 ]
323             || $lhs->[ 1 ] <=> $rhs->[ 1 ]
324             || $lhs->[ 2 ] <=> $rhs->[ 2 ]
325             || $lhs->[ 3 ] <=> $rhs->[ 3 ]
326             );
327             }
328              
329             sub _do_add {
330 32     32   49 my $self = shift;
331 32         53 my ($rhs, $swapped) = @_;
332 32         79 my ($pow, $mask) = $self->_pow_mask;
333 32         59 my $lhs = $self->{ address };
334 32         68 $lhs = [ unpack 'N4', $lhs ];
335 32   33     47 $rhs = eval { $rhs->{ address } } || pack('N4', (0, 0, 0, $rhs));
336 32         179 $rhs = [ unpack 'N4', $rhs ];
337 32 50       70 ($lhs, $rhs) = ($rhs, $lhs) if $swapped;
338 32         55 my @l = reverse @$lhs;
339 32         50 my @r = reverse @$rhs;
340 32         45 my @rv;
341 32         70 for my $digit (0 .. 3) {
342 128         160 my $answer = $l[$digit] + $r[$digit];
343 128 50       186 if ($answer > (2 ** 32) - 1) {
344 0 0       0 $r[$digit + 1] += int($answer / (2 ** 32)) if exists $r[$digit + 1];
345 0         0 $answer = $answer % (2 ** 32);
346             }
347 128         194 push @rv, $answer;
348             }
349 32         43 my %opt;
350 32         130 @opt{qw( SIIT promote )} = @$self{qw( SIIT promote )};
351 32         79 @rv = $self->_mask_out($pow, $mask, reverse @rv);
352 32         146 my $retval = Net::IPAddress::Util->new(\@rv, %opt);
353 32         200 return $retval;
354             }
355              
356             sub _do_subtract {
357 16     16   23 my $self = shift;
358 16         25 my ($rhs, $swapped) = @_;
359 16         29 my ($pow, $mask) = $self->_pow_mask;
360 16         26 my $lhs = $self->{ address };
361 16         32 $lhs = [ unpack 'N4', $lhs ];
362 16   33     24 $rhs = eval { $rhs->{ address } } || pack('N4', (0, 0, 0, $rhs));
363 16         39 $rhs = [ unpack 'N4', $rhs ];
364 16 50       34 ($lhs, $rhs) = ($rhs, $lhs) if $swapped;
365 16         27 my @l = reverse @$lhs;
366 16         22 my @r = reverse @$rhs;
367 16         20 my @rv;
368 16         29 for my $digit (0 .. 3) {
369 64         78 my $answer = $l[$digit] - $r[$digit];
370 64 50       87 if ($answer < 0) {
371 0         0 $answer += (2 ** 32) - 1;
372 0 0       0 $r[$digit + 1] -= 1 if exists $r[$digit + 1];
373             }
374 64         87 push @rv, $answer;
375             }
376 16         22 my %opt;
377 16         39 @opt{qw( SIIT promote )} = @$self{qw( SIIT promote )};
378 16         34 @rv = $self->_mask_out($pow, $mask, reverse @rv);
379 16         63 my $retval = Net::IPAddress::Util->new(\@rv, %opt);
380 16         72 return $retval;
381             }
382              
383             sub _shift_left {
384 0     0   0 my $self = shift;
385 0         0 my ($rhs, $swapped) = @_;
386 0         0 my ($pow, $mask) = $self->_pow_mask;
387 0         0 my @l = reverse unpack('C16', $self->{ address });
388 0         0 my @rv;
389 0         0 for my $octet (0 .. 15) {
390 0         0 $rv[$octet] += $l[$octet] << $rhs;
391 0 0       0 if ($rv[$octet] > 255) {
392 0         0 my $lsb = $rv[$octet] % 256;
393 0 0       0 $rv[$octet + 1] += ($rv[$octet] - $lsb) >> 8 if $octet < 15;
394 0         0 $rv[$octet] = $lsb;
395             }
396             }
397 0         0 @rv = $self->_mask_out($pow, $mask, @rv);
398 0         0 return Net::IPAddress::Util->new(\@rv);
399             }
400              
401             sub _shift_right {
402 0     0   0 my $self = shift;
403 0         0 my ($rhs, $swapped) = @_;
404 0         0 my ($pow, $mask) = $self->_pow_mask;
405 0         0 my @l = unpack('C16', $self->{ address });
406 0         0 my @rv;
407 0         0 for my $octet (0 .. 15) {
408 0         0 $rv[$octet] += $l[$octet] >> $rhs;
409 0 0       0 if (int($rv[$octet]) - $rv[$octet]) {
410 0         0 my $msb = int($rv[$octet]);
411 0         0 my $lsb = $rv[$octet] << $rhs;
412 0         0 $rv[$octet] = $msb;
413 0 0       0 $rv[$octet + 1] += $lsb if $octet < 15;
414             }
415             }
416 0         0 @rv = $self->_mask_out($pow, $mask, unpack('C16', pack('N4', @rv)));
417 0         0 return Net::IPAddress::Util->new(\@rv);
418             }
419              
420             sub _band {
421 282     282   441 my $self = shift;
422 282         493 my ($rhs, $swapped) = @_;
423 282 50       454 ($self, $rhs) = ($rhs, $self) if $swapped;
424 282         418 my $lhs = $self->{ address };
425 282         652 $lhs = [ unpack 'N4', $lhs ];
426 282   33     460 $rhs = eval { $rhs->{ address } } || pack('N4', (0, 0, 0, $rhs));
427 282         538 $rhs = [ unpack 'N4', $rhs ];
428 282 50       477 ($lhs, $rhs) = ($rhs, $lhs) if $swapped;
429 282         427 my @l = @$lhs;
430 282         346 my @r = @$rhs;
431 282         309 my @rv;
432 282         470 for my $hextet (0 .. 3) {
433 1128         1589 $rv[$hextet] = $l[$hextet] & $r[$hextet];
434             }
435 282         520 return Net::IPAddress::Util->new(\@rv);
436             }
437              
438             sub _bor {
439 282     282   393 my $self = shift;
440 282         404 my ($rhs, $swapped) = @_;
441 282 50       468 ($self, $rhs) = ($rhs, $self) if $swapped;
442 282         404 my $lhs = $self->{ address };
443 282         534 $lhs = [ unpack 'N4', $lhs ];
444 282   33     415 $rhs = eval { $rhs->{ address } } || pack('N4', (0, 0, 0, $rhs));
445 282         479 $rhs = [ unpack 'N4', $rhs ];
446 282 50       507 ($lhs, $rhs) = ($rhs, $lhs) if $swapped;
447 282         397 my @l = @$lhs;
448 282         378 my @r = @$rhs;
449 282         323 my @rv;
450 282         470 for my $hextet (0 .. 3) {
451 1128         1554 $rv[$hextet] = $l[$hextet] | $r[$hextet];
452             }
453 282         496 return Net::IPAddress::Util->new(\@rv);
454             }
455              
456             sub _neg {
457 282     282   375 my $self = shift;
458 282         720 my @n = unpack('C16', $self->{ address });
459 282         432 my @rv = map { 255 - $_ } @n;
  4512         5277  
460 282         555 return Net::IPAddress::Util->new(\@rv);
461             }
462              
463             sub _pow_mask {
464 48     48   61 my $self = shift;
465 48         54 my $pow = 128;
466 48         67 my $mask = pack('N4', 0, 0, 0, 0);
467 48 50       86 if ($self->is_ipv4) {
468 48         67 $pow = 32;
469             $mask = pack('C16',
470             0, 0, 0, 0,
471             0, 0, 0, 0,
472 48         67 @{$SIIT[ $self->{ SIIT } ]},
  48         130  
473             0, 0, 0, 0,
474             );
475             }
476 48         114 return ($pow, $mask);
477             }
478              
479             sub _mask_out {
480 48     48   67 my $self = shift;
481 48         88 my ($pow, $mask, @rv) = @_;
482 48         74 my @and = (0, 0, 0, 0);
483 48         83 map { $and[ 4 - $_ ] = 0xffffffff } grep { $pow / $_ >= 32 } (1 .. 4);
  48         98  
  192         366  
484 48         106 my @or = unpack('N4', $mask);
485 48     192   420 @rv = pairwise { $a & $b } @rv, @and;
  192         321  
486 48     192   252 @rv = pairwise { $a | $b } @rv, @or;
  192         270  
487 48         145 return @rv;
488             }
489              
490             sub ipv4_mask {
491 174     174 1 332 return implode_ip(('0' x 64) . ('1' x 16) . ('0' x 16) . ('1' x 32));
492             }
493              
494             sub ipv4_flag {
495 0     0 1 0 return implode_ip(('0' x 64) . ('1' x 16) . ('0' x 48));
496             }
497              
498             sub common_prefix (\@\@) {
499 174     174 1 286 my ($x, $y) = @_;
500 174 50       339 return ERROR("Something isn't right there") unless @$x == @$y;
501 174         196 my @rv;
502 174         360 for my $i (0 .. $#$x) {
503 21712 100       28091 if($x->[$i] eq $y->[$i]) {
504 21550         27926 push @rv, $x->[$i];
505             }
506             else {
507 162         254 last;
508             }
509             }
510 174         3255 return @rv;
511             }
512              
513             sub prefix_mask (\@\@) {
514 174     174 1 273 my ($x, $y) = @_;
515 174 50       310 return ERROR("Something isn't right there") unless @$x == @$y;
516 174         191 my @rv;
517 174         347 for my $i (0 .. $#$x) {
518 21712 100       29917 if($x->[$i] == $y->[$i]) {
519 21550         24560 push @rv, 1;
520             }
521             else {
522 162         284 last;
523             }
524             }
525 174         1014 return @rv;
526             }
527              
528             sub ip_pad_prefix (\@) {
529 348     348 1 389 my @array = @{$_[0]};
  348         2756  
530 348         609 for my $i (scalar(@array) .. 127) {
531 1444         1763 push @array, 0;
532             }
533 348         4018 return @array;
534             }
535              
536             sub explode_ip {
537 348     348 1 432 my $ip = shift;
538 348         4178 return map { ~~$_ } split //, unpack 'B128', $ip->{ address };
  44544         57478  
539             }
540              
541             sub implode_ip {
542 628     628 1 1124 return Net::IPAddress::Util->new([ unpack 'C16', pack 'B128', join '', map { ~~$_ } map { split // } @_ ]);
  80384         105688  
  44824         60981  
543             }
544              
545 0     0 1 0 sub n32_to_ipv4 { local $PROMOTE_N32 = 1; return IP(@_) }
  0         0  
546              
547             sub ERROR {
548 0 0   0 1 0 my $msg = @_ ? shift() : 'An error has occured';
549 0 0       0 if ($DIE_ON_ERROR) {
550 0         0 confess($msg);
551             }
552             else {
553 0 0       0 cluck($msg) if $^W;
554             }
555 0         0 return;
556             }
557              
558             sub radix_sort (\@) {
559             # In theory, a radix sort is O(N), which beats Perl's O(N log N) by
560             # a fair margin. However, it _does_ discard duplicates, so ymmv.
561 0 0   0 1 0 shift if $_[0] eq __PACKAGE__;
562 0         0 my $array = shift;
563 0         0 my $from = [ map { [ unpack 'C16', $_->{ address } ] } @$array ];
  0         0  
564 0         0 my $to;
565 0         0 for (my $i = 15; $i >= 0; $i--) {
566 0         0 $to = [];
567 0         0 for my $card (@$from) {
568 0         0 push @{$to->[ $card->[ $i ] ]}, $card;
  0         0  
569             }
570 0   0     0 $from = [ map { @{$_ // []} } @$to ];
  0         0  
  0         0  
571             }
572 0         0 my @rv = map { IP(pack 'C16', @$_) } @$from;
  0         0  
573 0         0 return @rv;
574             }
575              
576             sub ip2num {
577 0 0   0 1 0 carp('Compatibility function ip2num() is deprecated') if $^W;
578 0         0 my $ip = shift;
579 0         0 my $self = IP($ip);
580 0         0 $self &= ((2 ** 32) - 1);
581 0         0 return $self->as_n32();
582             }
583              
584             sub num2ip {
585 0 0   0 1 0 carp('Compatibility function num2ip() is deprecated') if $^W;
586 0         0 my $num = shift;
587 0         0 my $self = n32_to_ipv4($num);
588 0         0 return $self->str();
589             }
590              
591             sub validaddr {
592 0 0   0 1 0 carp('Compatibility function validaddr() is deprecated') if $^W;
593 0         0 my $ip = shift;
594 0         0 my @octets = split(/\./, $ip);
595 0 0       0 return unless scalar @octets == 4;
596 0         0 for (@octets) {
597 0 0 0     0 return unless defined $_ && $_ >= 0 && $_ <= 255;
      0        
598             }
599 0         0 return 1;
600             }
601              
602             sub mask {
603 0 0   0 1 0 carp('Compatibility function mask() is deprecated') if $^W;
604 0         0 my ($ip, $mask) = @_;
605 0         0 my $self = IP($ip);
606 0         0 my $nm = IP($mask);
607 0         0 return $self & $nm;
608             }
609              
610             sub fqdn {
611 0 0   0 1 0 carp('Compatibility function fqdn() is deprecated') if $^W;
612 0         0 my $dn = shift;
613 0         0 return split(/\./, $dn, 2);
614             }
615              
616             1;
617              
618             __END__