File Coverage

blib/lib/Data/Validate/IP.pm
Criterion Covered Total %
statement 553 553 100.0
branch 356 396 89.9
condition 16 20 80.0
subroutine 35 35 100.0
pod 1 2 50.0
total 961 1006 95.5


line stmt bran cond sub pod time code
1             package Data::Validate::IP;
2              
3 2     2   4635 use strict;
  2         14  
  2         55  
4 2     2   10 use warnings;
  2         6  
  2         44  
5              
6 2     2   48 use 5.008;
  2         8  
7              
8             our $VERSION = '0.31';
9              
10 2     2   1109 use NetAddr::IP 4;
  2         63216  
  2         13  
11 2     2   253 use Scalar::Util qw( blessed );
  2         6  
  2         237  
12              
13 2     2   14 use base 'Exporter';
  2         4  
  2         760  
14              
15             ## no critic (Modules::ProhibitAutomaticExportation)
16             our @EXPORT = qw(
17             is_ip
18             is_ipv4
19             is_ipv6
20             is_innet_ipv4
21             );
22             ## use critic
23              
24             our $HAS_SOCKET;
25              
26             BEGIN {
27 2     2   9 local $@ = undef;
28             $HAS_SOCKET = ( !$ENV{DVI_NO_SOCKET} )
29 2   66     13 && eval {
30             require Socket;
31             Socket->import(qw( AF_INET AF_INET6 inet_pton ));
32              
33             # On some platforms, Socket.pm exports an inet_pton that just dies
34             # when it is called. On others, inet_pton accepts various forms of
35             # invalid input.
36             defined &Socket::inet_pton
37             && !defined inet_pton( Socket::AF_INET(), '016.17.184.1' )
38             && !defined inet_pton( Socket::AF_INET6(), '2067::1:' )
39              
40             # Some old versions of Socket are hopelessly broken
41             && length( inet_pton( Socket::AF_INET(), '1.1.1.1' ) ) == 4;
42             };
43              
44 2 100       17 if ($HAS_SOCKET) {
45 1         5 *is_ipv4 = \&_fast_is_ipv4;
46 1         3 *is_ipv6 = \&_fast_is_ipv6;
47 1         2 *is_ip = \&_fast_is_ip;
48 1         2527 *_build_is_X_ip_subs = \&_build_fast_is_X_ip_subs;
49             }
50             else {
51 1         6 *is_ipv4 = \&_slow_is_ipv4;
52 1         3 *is_ipv6 = \&_slow_is_ipv6;
53 1         3 *is_ip = \&_slow_is_ip;
54 1         2543 *_build_is_X_ip_subs = \&_build_slow_is_X_ip_subs;
55             }
56             }
57              
58             sub new {
59 2     2 0 136780 my $class = shift;
60              
61 2         10 return bless {}, $class;
62             }
63              
64             sub _fast_is_ip {
65 14 100   14   3139 shift if ref $_[0];
66 14         26 my $value = shift;
67              
68 14 50       44 return undef unless defined $value;
69 14 100       62 return $value =~ /:/ ? _fast_is_ipv6($value) : _fast_is_ipv4($value);
70             }
71              
72             sub _fast_is_ipv4 {
73 46 100   46   5603 shift if ref $_[0];
74 46         86 my $value = shift;
75              
76 46 100       118 return undef unless _fast_is_ipv4_packed($value);
77              
78             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
79 23         95 $value =~ /(.+)/;
80 23         107 return $1;
81             }
82              
83             sub _fast_is_ipv4_packed {
84 796     796   1245 my $value = shift;
85              
86 796 50       1770 return undef unless defined $value;
87 796 100       2198 return undef if $value =~ /\0/;
88 778         15379 return inet_pton( Socket::AF_INET(), $value );
89             }
90              
91             sub _slow_is_ip {
92 14 100   14   3052 shift if ref $_[0];
93 14         28 my $value = shift;
94              
95 14   100     30 return _slow_is_ipv4($value) || _slow_is_ipv6($value);
96             }
97              
98             sub _slow_is_ipv4 {
99 806 100   806   7125 shift if ref $_[0];
100 806         1146 my $value = shift;
101              
102 806 50       1603 return undef unless defined($value);
103              
104 806         3298 my (@octets) = $value =~ /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
105 806 100       6937 return undef unless ( @octets == 4 );
106 487         958 foreach (@octets) {
107 1804 100 66     5835 return undef if $_ < 0 || $_ > 255;
108 1786 100       4037 return undef if $_ =~ /^0\d{1,2}$/;
109             }
110              
111 433         8810 return join( '.', @octets );
112             }
113              
114             sub _fast_is_ipv6 {
115 66 100   66   15349 shift if ref $_[0];
116 66         126 my $value = shift;
117              
118 66 100       126 return undef unless _fast_is_ipv6_packed($value);
119              
120             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
121 36         155 $value =~ /(.+)/;
122 36         190 return $1;
123             }
124              
125             sub _fast_is_ipv6_packed {
126 1538     1538   2435 my $value = shift;
127              
128 1538 50       3333 return undef unless defined $value;
129 1538 100       4341 return undef if $value =~ /\0/;
130 1510 100       4441 return undef if $value =~ /0[[:xdigit:]]{4}/;
131 1484         29859 return inet_pton( Socket::AF_INET6(), $value );
132             }
133              
134             {
135             # This comes from Regexp::IPv6
136             ## no critic (RegularExpressions::ProhibitComplexRegexes)
137             my $ipv6_re
138             = qr/(?-xism::(?::[0-9a-fA-F]{1,4}){0,5}(?:(?::[0-9a-fA-F]{1,4}){1,2}|:(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})))|[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}:(?:[0-9a-fA-F]{1,4}|:)|(?::(?:[0-9a-fA-F]{1,4})?|(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))))|:(?:(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))|[0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4})?|))|(?::(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))|:[0-9a-fA-F]{1,4}(?::(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))|(?::[0-9a-fA-F]{1,4}){0,2})|:))|(?:(?::[0-9a-fA-F]{1,4}){0,2}(?::(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))|(?::[0-9a-fA-F]{1,4}){1,2})|:))|(?:(?::[0-9a-fA-F]{1,4}){0,3}(?::(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))|(?::[0-9a-fA-F]{1,4}){1,2})|:))|(?:(?::[0-9a-fA-F]{1,4}){0,4}(?::(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))|(?::[0-9a-fA-F]{1,4}){1,2})|:)))/;
139              
140             sub _slow_is_ipv6 {
141 1540 100   1540   17845 shift if ref $_[0];
142 1540         2256 my $value = shift;
143              
144 1540 50       2915 return undef unless defined($value);
145              
146 1540 100       3674 return '::' if $value eq '::';
147 1514 100       22492 return undef unless $value =~ /^$ipv6_re$/;
148              
149             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
150 1122         2671 $value =~ /(.+)/;
151 1122         22591 return $1;
152             }
153             }
154              
155             # This is just a quick test - we'll let NetAddr::IP decide if the address is
156             # valid.
157             my $ip_re = qr/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
158             my $partial_ip_re = qr/\d{1,3}(?:\.\d{1,3}){0,2}/;
159              
160             ## no critic (Subroutines::ProhibitExcessComplexity, ControlStructures::ProhibitCascadingIfElse)
161             sub is_innet_ipv4 {
162 28 50   28 1 16818 shift if ref $_[0];
163 28         54 my $value = shift;
164 28         44 my $network = shift;
165              
166 28 50       64 return undef unless defined($value);
167              
168 28         60 my $ip = is_ipv4($value);
169 28 100       70 return undef unless defined $ip;
170              
171             # Backwards compatibility hacks to make it accept things that Net::Netmask
172             # accepts.
173 26 100 100     534 if ( $network eq 'default'
    50 100        
      33        
174             || $network =~ /^$ip_re$/
175             || $network =~ m{^$ip_re/\d\d?$} ) {
176              
177 10 50       62 $network = NetAddr::IP->new($network) or return undef;
178             }
179             elsif ( !( blessed $network && $network->isa('NetAddr::IP') ) ) {
180 16         38 my $orig = $network;
181 16 100       346 if ( $network =~ /^($ip_re)[:\-]($ip_re)$/ ) {
    100          
    100          
    50          
182 4         17 my ( $net, $netmask ) = ( $1, $2 );
183              
184 4 50       13 my $bits = _netmask_to_bits($netmask)
185             or return undef;
186              
187 4         15 $network = "$net/$bits";
188             }
189             elsif ( $network =~ /^($ip_re)\#($ip_re)$/ ) {
190 2         11 my ( $net, $hostmask ) = ( $1, $2 );
191              
192 2 50       10 my $bits = _hostmask_to_bits($hostmask)
193             or return undef;
194              
195 2         9 $network = "$net/$bits";
196             }
197             elsif ( $network =~ m{^($partial_ip_re)/(\d\d?)$} ) {
198 4         19 my ( $net, $bits ) = ( $1, $2 );
199              
200             # This is a hack to avoid a deprecation warning (Use of implicit
201             # split to @_ is deprecated) that shows up on 5.10.1 but not on
202             # newer Perls.
203             #
204             ## no critic(Variables::ProhibitUnusedVarsStricter)
205 4         20 my $octets = scalar( my @tmp = split /\./, $net );
206 4         10 $network = $net;
207 4         15 $network .= '.0' x ( 4 - $octets );
208 4         13 $network .= "/$bits";
209             }
210             elsif ( $network =~ /^$partial_ip_re$/ ) {
211              
212             ## no critic(Variables::ProhibitUnusedVarsStricter)
213 6         24 my $octets = scalar( my @tmp = split /\./, $network );
214 6 50       21 if ( $octets < 4 ) {
215 6         20 $network .= '.0' x ( 4 - $octets );
216 6         21 $network .= '/' . $octets * 8;
217             }
218             }
219              
220 16 50       42 if ( $orig ne $network ) {
221 16         38 _deprecation_warn(
222             'Use of non-CIDR notation for networks with is_innet_ipv4() is deprecated'
223             );
224             }
225              
226 16 50       71 $network = NetAddr::IP->new($network) or return undef;
227             }
228              
229 26 50       10809 my $netaddr_ip = NetAddr::IP->new($ip) or return undef;
230              
231 26 100       9477 return $ip if $network->contains($netaddr_ip);
232 2         48 return undef;
233             }
234             ## use critic;
235              
236             {
237             my %netmasks = (
238             '128.0.0.0' => '1',
239             '192.0.0.0' => '2',
240             '224.0.0.0' => '3',
241             '240.0.0.0' => '4',
242             '248.0.0.0' => '5',
243             '252.0.0.0' => '6',
244             '254.0.0.0' => '7',
245             '255.0.0.0' => '8',
246             '255.128.0.0' => '9',
247             '255.192.0.0' => '10',
248             '255.224.0.0' => '11',
249             '255.240.0.0' => '12',
250             '255.248.0.0' => '13',
251             '255.252.0.0' => '14',
252             '255.254.0.0' => '15',
253             '255.255.0.0' => '16',
254             '255.255.128.0' => '17',
255             '255.255.192.0' => '18',
256             '255.255.224.0' => '19',
257             '255.255.240.0' => '20',
258             '255.255.248.0' => '21',
259             '255.255.252.0' => '22',
260             '255.255.254.0' => '23',
261             '255.255.255.0' => '24',
262             '255.255.255.128' => '25',
263             '255.255.255.192' => '26',
264             '255.255.255.224' => '27',
265             '255.255.255.240' => '28',
266             '255.255.255.248' => '29',
267             '255.255.255.252' => '30',
268             '255.255.255.254' => '31',
269             '255.255.255.255' => '32',
270             );
271              
272             sub _netmask_to_bits {
273 4     4   22 return $netmasks{ $_[0] };
274             }
275             }
276              
277             {
278             my %hostmasks = (
279             '255.255.255.255' => 0,
280             '127.255.255.255' => 1,
281             '63.255.255.255' => 2,
282             '31.255.255.255' => 3,
283             '15.255.255.255' => 4,
284             '7.255.255.255' => 5,
285             '3.255.255.255' => 6,
286             '1.255.255.255' => 7,
287             '0.255.255.255' => 8,
288             '0.127.255.255' => 9,
289             '0.63.255.255' => 10,
290             '0.31.255.255' => 11,
291             '0.15.255.255' => 12,
292             '0.7.255.255' => 13,
293             '0.3.255.255' => 14,
294             '0.1.255.255' => 15,
295             '0.0.255.255' => 16,
296             '0.0.127.255' => 17,
297             '0.0.63.255' => 18,
298             '0.0.31.255' => 19,
299             '0.0.15.255' => 20,
300             '0.0.7.255' => 21,
301             '0.0.3.255' => 22,
302             '0.0.1.255' => 23,
303             '0.0.0.255' => 24,
304             '0.0.0.127' => 25,
305             '0.0.0.63' => 26,
306             '0.0.0.31' => 27,
307             '0.0.0.15' => 28,
308             '0.0.0.7' => 29,
309             '0.0.0.3' => 30,
310             '0.0.0.1' => 31,
311             '0.0.0.0' => 32,
312             );
313              
314             sub _hostmask_to_bits {
315 2     2   12 return $hostmasks{ $_[0] };
316             }
317             }
318              
319             {
320             my %warned_at;
321              
322             sub _deprecation_warn {
323 16     16   27 my $warning = shift;
324 16         127 my @caller = caller(2);
325              
326 16         60 my $caller_info
327             = "at line $caller[2] of $caller[0] in sub $caller[3]";
328              
329 16 100       75 return undef if $warned_at{$warning}{$caller_info}++;
330              
331 2         27 warn "$warning $caller_info\n";
332             }
333             }
334              
335             {
336             my %ipv4_networks = (
337             loopback => { networks => '127.0.0.0/8' },
338             private => {
339             networks => [
340             qw(
341             10.0.0.0/8
342             172.16.0.0/12
343             192.168.0.0/16
344             )
345             ],
346             },
347             testnet => {
348             networks => [
349             qw(
350             192.0.2.0/24
351             198.51.100.0/24
352             203.0.113.0/24
353             )
354             ],
355             },
356             anycast => { networks => '192.88.99.0/24' },
357             multicast => { networks => '224.0.0.0/4' },
358             linklocal => { networks => '169.254.0.0/16' },
359             unroutable => {
360             networks => [
361             qw(
362             0.0.0.0/8
363             100.64.0.0/10
364             192.0.0.0/29
365             198.18.0.0/15
366             240.0.0.0/4
367             )
368             ],
369             },
370             );
371              
372             _build_is_X_ip_subs( \%ipv4_networks, 4 );
373             }
374              
375             {
376             my %ipv6_networks = (
377             loopback => { networks => '::1/128' },
378             ipv4_mapped => { networks => '::ffff:0:0/96' },
379             discard => { networks => '100::/64' },
380             special => { networks => '2001::/23' },
381             teredo => {
382             networks => '2001::/32',
383             subnet_of => 'special',
384             },
385             orchid => {
386             networks => '2001:10::/28',
387             subnet_of => 'special',
388             },
389             documentation => { networks => '2001:db8::/32' },
390             private => { networks => 'fc00::/7' },
391             linklocal => { networks => 'fe80::/10' },
392             multicast => { networks => 'ff00::/8' },
393             unspecified => { networks => '::/128' },
394             );
395              
396             _build_is_X_ip_subs( \%ipv6_networks, 6 );
397              
398             # This exists for the benefit of the test code.
399             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
400             sub _network_is_subnet_of {
401 1350     1350   457756 my $network = shift;
402 1350         2137 my $other = shift;
403              
404 1350   100     6646 return ( $ipv6_networks{$network}{subnet_of} || q{} ) eq $other;
405             }
406             }
407              
408             ## no critic (TestingAndDebugging::ProhibitNoStrict, BuiltinFunctions::ProhibitStringyEval)
409             sub _build_slow_is_X_ip_subs {
410 2     2   4 my $networks = shift;
411 2         3 my $ip_number = shift;
412              
413 2 100       6 my $is_ip_sub = $ip_number == 4 ? 'is_ipv4' : 'is_ipv6';
414 2 100       5 my $netaddr_new = $ip_number == 4 ? 'new' : 'new6';
415              
416 2         2 my @all_nets;
417              
418 2         4 local $@ = undef;
419 2         3 for my $type ( keys %{$networks} ) {
  2         16  
420             my @nets
421 26         999 = map { NetAddr::IP->$netaddr_new($_) }
422             ref $networks->{$type}{networks}
423 3         13 ? @{ $networks->{$type}{networks} }
424 18 100       59 : $networks->{$type}{networks};
425              
426             # Some IPv6 networks (like TEREDO) are a subset of the special block
427             # so there's no point in checking for them in the is_public_ipv6()
428             # sub.
429 18 100       2667 unless ( $networks->{$type}{subnet_of} ) {
430 16         36 push @all_nets, @nets;
431             }
432              
433             # We're using code gen rather than just making an anon sub outright so
434             # we don't have to pay the cost of derefencing the $is_ip_sub and the
435             # dynamic dispatch cost for $netaddr_new
436 18 100   154   2744 my $sub = eval sprintf( <<'EOF', $is_ip_sub, $netaddr_new );
  154 50       6880  
  154 100       279  
  154 100       403  
  154 100       479  
  154 50       595  
  114 100       388  
  114 100       29484  
  114 100       365  
  98 50       4000  
  98 100       6273  
  98 100       227  
  98 100       308  
  98 50       284  
  98 100       372  
  74 100       254  
  74 100       19044  
  74 50       261  
  68 100       2752  
  60 100       5268  
  60 100       115  
  60 50       141  
  60 100       164  
  60 100       215  
  40 100       145  
  40 50       5315  
  114 100       2644  
  34 100       1212  
  150 100       6853  
  150 50       304  
  150 100       341  
  150 100       412  
  150 100       544  
  114 50       383  
  114 100       29575  
  114 100       350  
  98 100       4217  
  60 50       6898  
  60 100       100  
  60 100       135  
  60 100       150  
  60 50       211  
  40 100       153  
  40 100       5300  
  180 100       4672  
  26 50       900  
  102 100       5959  
  102 100       211  
  102 100       319  
  102 50       303  
  102 100       408  
  78 100       279  
  78 100       20028  
  78 50       242  
  74 100       3019  
  102 100       6798  
  102 100       211  
  102 50       249  
  102 100       316  
  102 100       390  
  78 100       294  
  78 50       20117  
  78 100       250  
  70 100       2916  
  154 100       5646  
  154 50       295  
  154 100       406  
  154 100       426  
  154 100       595  
  114 50       396  
  114 100       29596  
  114 100       410  
  110         4603  
  102         6369  
  102         210  
  102         288  
  102         329  
  102         411  
  78         271  
  78         20185  
  78         249  
  60         2542  
  102         6781  
  102         213  
  102         307  
  102         293  
  102         406  
  78         291  
  78         20105  
  78         254  
  72         2981  
  114         4584  
  114         218  
  114         265  
  114         287  
  114         1569  
  58         207  
  58         7778  
  58         184  
  54         2703  
  102         6342  
  102         205  
  102         307  
  102         293  
  102         399  
  78         263  
  78         20174  
  78         275  
  72         2908  
  154         6794  
  154         288  
  154         428  
  154         388  
  154         613  
  114         391  
  114         29394  
  114         385  
  102         4214  
  114         4680  
  114         210  
  114         260  
  114         295  
  114         1594  
  58         203  
  58         7881  
  58         193  
  54         2735  
  102         1659  
  102         211  
  102         280  
  102         288  
  102         408  
  78         294  
  78         20304  
  78         256  
  72         2969  
  60         636  
  60         138  
  60         142  
  60         161  
  60         234  
  40         187  
  40         5493  
  40         136  
  38         1559  
  114         4645  
  114         202  
  114         259  
  114         327  
  114         1559  
  58         211  
  58         7759  
  58         224  
  54         2714  
  114         5462  
  114         216  
  114         256  
  114         271  
  114         1530  
  58         209  
  58         7877  
  162         3773  
  46         2054  
437             sub {
438             shift if ref $_[0];
439             my $value = shift;
440              
441             return undef unless defined $value;
442              
443             my $ip = %s($value);
444             return undef unless defined $ip;
445              
446             my $netaddr_ip = NetAddr::IP->%s($ip);
447             for my $net (@nets) {
448             return $ip if $net->contains($netaddr_ip);
449             }
450             return undef;
451             }
452             EOF
453 18 50       59 die $@ if $@;
454              
455 18         47 my $sub_name = 'is_' . $type . '_ipv' . $ip_number;
456             {
457 2     2   19 no strict 'refs';
  2         4  
  2         233  
  18         32  
458 18         21 *{$sub_name} = $sub;
  18         94  
459             }
460 18         61 push @EXPORT, $sub_name;
461             }
462              
463 2 100   150   272 my $sub = eval sprintf( <<'EOF', $is_ip_sub, $netaddr_new );
  150 50       8447  
  150 100       315  
  150 100       423  
  150 100       373  
  150 50       553  
  114 100       419  
  114 100       29515  
  720         20263  
  24         909  
  114         5386  
  114         208  
  114         241  
  114         292  
  114         1527  
  58         244  
  58         7793  
  560         16513  
  12         535  
464             sub {
465             shift if ref $_[0];
466             my $value = shift;
467              
468             return undef unless defined($value);
469              
470             my $ip = %s($value);
471             return undef unless defined $ip;
472              
473             my $netaddr_ip = NetAddr::IP->%s($ip);
474             for my $net (@all_nets) {
475             return undef if $net->contains($netaddr_ip);
476             }
477              
478             return $ip;
479             }
480             EOF
481 2 50       8 die $@ if $@;
482              
483 2         5 my $sub_name = 'is_public_ipv' . $ip_number;
484             {
485 2     2   14 no strict 'refs';
  2         6  
  2         507  
  2         4  
486 2         3 *{$sub_name} = $sub;
  2         11  
487             }
488 2         7 push @EXPORT, $sub_name;
489             }
490              
491             sub _build_fast_is_X_ip_subs {
492 2     2   5 my $networks = shift;
493 2         2 my $ip_number = shift;
494              
495 2 100       7 my $family = $ip_number == 4 ? Socket::AF_INET() : Socket::AF_INET6();
496              
497 2         4 my @all_nets;
498              
499 2         12 local $@ = undef;
500 2         4 for my $type ( keys %{$networks} ) {
  2         13  
501             my @nets
502 26         52 = map { _packed_network_and_netmask( $family, $_ ) }
503             ref $networks->{$type}{networks}
504 3         8 ? @{ $networks->{$type}{networks} }
505 18 100       63 : $networks->{$type}{networks};
506              
507             # Some IPv6 networks (like TEREDO) are a subset of the special block
508             # so there's no point in checking for them in the is_public_ipv6()
509             # sub.
510 18 100       52 unless ( $networks->{$type}{subnet_of} ) {
511 16         31 push @all_nets, @nets;
512             }
513              
514             # We're using code gen rather than just making an anon sub outright so
515             # we don't have to pay the cost of derefencing the $is_ip_sub and the
516             # dynamic dispatch cost for $netaddr_new
517 18 100   102   2890 my $sub = eval sprintf( <<'EOF', $ip_number );
  102 100       6573  
  102 100       217  
  102 100       268  
  102 100       399  
  78 100       187  
  78 100       310  
  6 100       20  
  6 100       38  
  72 100       329  
  114 100       5609  
  114 100       210  
  114 100       291  
  114 100       1619  
  58 100       131  
  162 100       430  
  12 100       34  
  12 100       212  
  46 100       578  
  102 100       1751  
  102 100       206  
  102 100       291  
  102 100       450  
  78 100       226  
  78 100       331  
  6 100       20  
  6 100       48  
  72 100       362  
  60 100       656  
  60 100       112  
  60 100       216  
  60 100       232  
  40 100       92  
  40 100       149  
  2 100       8  
  2 100       18  
  38 100       164  
  102 100       6644  
  102 100       201  
  102 100       272  
  102 100       411  
  78 100       198  
  78 100       339  
  6 100       20  
  6 100       48  
  72 100       341  
  114 100       4776  
  114 100       211  
  114 100       308  
  114 100       1653  
  58 100       127  
  58 100       219  
  4 100       13  
  4 100       63  
  54         781  
  60         7388  
  60         105  
  60         168  
  60         229  
  40         88  
  180         430  
  14         39  
  14         84  
  26         106  
  98         6433  
  98         208  
  98         265  
  98         407  
  74         217  
  74         297  
  6         20  
  6         38  
  68         299  
  154         7120  
  154         285  
  154         381  
  154         615  
  114         287  
  114         478  
  16         48  
  16         126  
  98         494  
  154         5940  
  154         327  
  154         403  
  154         583  
  114         270  
  114         459  
  4         15  
  4         30  
  110         531  
  102         6554  
  102         241  
  102         296  
  102         407  
  78         179  
  78         331  
  18         53  
  18         129  
  60         265  
  154         6612  
  154         323  
  154         470  
  154         655  
  114         285  
  114         1057  
  12         35  
  12         114  
  102         491  
  102         6128  
  102         208  
  102         286  
  102         386  
  78         193  
  78         289  
  4         15  
  4         28  
  74         322  
  114         4890  
  114         208  
  114         292  
  114         1632  
  58         135  
  58         217  
  4         17  
  4         62  
  54         741  
  114         4780  
  114         220  
  114         296  
  114         1643  
  58         124  
  58         214  
  4         14  
  4         63  
  54         750  
  102         7038  
  102         197  
  102         274  
  102         420  
  78         215  
  78         317  
  8         23  
  8         66  
  70         317  
  150         7174  
  150         296  
  150         417  
  150         554  
  114         264  
  114         426  
  16         49  
  16         118  
  98         490  
  60         5413  
  60         105  
  60         158  
  60         225  
  40         98  
  114         312  
  6         18  
  6         37  
  34         136  
518             sub {
519             shift if ref $_[0];
520             my $value = shift;
521              
522             my $ip = _fast_is_ipv%u_packed($value);
523              
524             return undef unless defined $ip;
525              
526             for my $net (@nets) {
527             if (($net->[1] & $ip) eq $net->[0]) {
528             $value =~ /(.+)/;
529             return $1;
530             }
531             }
532             return undef;
533             }
534             EOF
535 18 50       58 die $@ if $@;
536              
537 18         51 my $sub_name = 'is_' . $type . '_ipv' . $ip_number;
538             {
539 2     2   15 no strict 'refs';
  2         12  
  2         209  
  18         26  
540 18         29 *{$sub_name} = $sub;
  18         87  
541             }
542 18         61 push @EXPORT, $sub_name;
543             }
544              
545 2 100   150   301 my $sub = eval sprintf( <<'EOF', $ip_number );
  150 100       7901  
  150 100       338  
  150 100       382  
  150 100       591  
  114 100       271  
  650         1899  
  24         70  
  24         160  
  114         5629  
  114         209  
  114         324  
  114         1679  
  58         144  
  552         1793  
  12         36  
  12         183  
546             sub {
547             shift if ref $_[0];
548             my $value = shift;
549              
550             my $ip = _fast_is_ipv%u_packed($value);
551              
552             return undef unless defined $ip;
553              
554             for my $net (@all_nets) {
555             return undef if ($net->[1] & $ip) eq $net->[0];
556             }
557              
558             $value =~ /(.+)/;
559             return $1;
560             }
561             EOF
562 2 50       8 die $@ if $@;
563              
564 2         5 my $sub_name = 'is_public_ipv' . $ip_number;
565             {
566 2     2   14 no strict 'refs';
  2         4  
  2         485  
  2         5  
567 2         4 *{$sub_name} = $sub;
  2         11  
568             }
569 2         8 push @EXPORT, $sub_name;
570             }
571              
572             sub _packed_network_and_netmask {
573 26     26   39 my $family = shift;
574 26         37 my $network = shift;
575              
576 26         136 my ( $ip, $bits ) = split qr{/}, $network, 2;
577              
578             return [
579 26         98 inet_pton( $family, $ip ),
580             _packed_netmask( $family, $bits )
581             ];
582             }
583              
584             sub _packed_netmask {
585 26     26   47 my $family = shift;
586 26         36 my $bits = shift;
587              
588 26 100       54 my $bit_length = $family == Socket::AF_INET() ? 32 : 128;
589              
590 26         129 my $bit_string
591             = join( q{}, (1) x $bits, (0) x ( $bit_length - $bits ) );
592 26         156 return pack( 'B' . $bit_length, $bit_string );
593             }
594              
595             for my $sub (qw( linklocal loopback multicast private public )) {
596             my $sub_name = "is_${sub}_ip";
597              
598             {
599 2     2   14 no strict 'refs';
  2         6  
  2         232  
600 108 100   108   8733 *{$sub_name}
  108 100       3726  
  108 100       6360  
  108 100       8602  
  108 100       5539  
601             = eval "sub { ${sub_name}v4(\@_) || ${sub_name}v6(\@_) }";
602             die $@ if $@;
603             }
604              
605             push @EXPORT, $sub_name;
606             }
607             ## use critic
608              
609             1;
610              
611             # ABSTRACT: IPv4 and IPv6 validation methods
612              
613             __END__