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