File Coverage

blib/lib/IPTables/Rule.pm
Criterion Covered Total %
statement 323 368 87.7
branch 170 272 62.5
condition 25 42 59.5
subroutine 41 41 100.0
pod 20 23 86.9
total 579 746 77.6


line stmt bran cond sub pod time code
1             package IPTables::Rule;
2              
3 1     1   25274 use 5.000000;
  1         4  
  1         49  
4 1     1   5 use strict;
  1         2  
  1         39  
5 1     1   22 use warnings;
  1         13  
  1         6862  
6              
7             our $VERSION = '0.01';
8              
9             ###############################################################################
10             ### PRECOMPILED REGEX
11             my $qr_fqdn = qr/(([A-Z0-9]|[A-Z0-9][A-Z0-9\-]*[A-Z0-9])\.)*([A-Z]|[A-Z][A-Z0-9\-]*[A-Z0-9])/io;
12             my $qr_mac_addr = qr/(([A-F0-9]{2}[:.-]?){6})/io;
13              
14             my $qr_ip4_addr = qr/(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/o;
15             my $qr_ip6_addr;
16             {
17             # This block courtesy of Regexp::IPv6 0.03 by Salvador FandiƱo
18             # http://search.cpan.org/~salva/Regexp-IPv6/
19             # http://cpansearch.perl.org/src/SALVA/Regexp-IPv6-0.03/lib/Regexp/IPv6.pm
20             my $IPv4 = "((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}))";
21             my $G = "[0-9a-fA-F]{1,4}";
22             my @tail = ( ":",
23             "(:($G)?|$IPv4)",
24             ":($IPv4|$G(:$G)?|)",
25             "(:$IPv4|:$G(:$IPv4|(:$G){0,2})|:)",
26             "((:$G){0,2}(:$IPv4|(:$G){1,2})|:)",
27             "((:$G){0,3}(:$IPv4|(:$G){1,2})|:)",
28             "((:$G){0,4}(:$IPv4|(:$G){1,2})|:)" );
29             my $IPv6_re = $G;
30             $IPv6_re = "$G:($IPv6_re|$_)" for @tail;
31             $IPv6_re = qq/:(:$G){0,5}((:$G){1,2}|:$IPv4)|$IPv6_re/;
32             $IPv6_re =~ s/\(/(?:/g;
33             $qr_ip6_addr = qr/$IPv6_re/;
34             }
35             # and the CIDR versions of the above
36             my $qr_ip4_cidr = qr/$qr_ip4_addr\/[0-9]{1,2}/o;
37             my $qr_ip6_cidr = qr/$qr_ip6_addr\/[0-9]{1,3}/io;
38              
39             ###############################################################################
40             ### METHODS
41              
42             sub new {
43 26     26 1 10740 my $self = {
44             iptbinary => 'iptables',
45             iptaction => '-A',
46             ipver => '4', # IPv4 by default
47             table => undef,
48             chain => undef,
49             target => undef,
50             in => undef,
51             out => undef,
52             src => undef,
53             dst => undef,
54             proto => undef,
55             dpt => undef,
56             spt => undef,
57             mac => undef,
58             state => undef,
59             comment => undef,
60             logprefix => undef,
61             icmp_type => undef,
62             };
63              
64 26         82 bless $self;
65             }
66              
67             sub dump {
68 1     1 1 5 my $self = shift;
69 1         1 my %dump_hash;
70              
71 1         6 foreach my $key ( keys %$self ) {
72 18 100       32 $dump_hash{$key} = $self->{$key} if ( defined($self->{$key}) );
73             }
74              
75 1         4 return \%dump_hash;
76             }
77              
78             sub errstr {
79 1     1 0 6 my $self = shift;
80 1         6 return $self->{errstr};
81             }
82              
83             sub iptbinary {
84 3     3 1 493 my $self = shift;
85 3         6 my ($arg) = @_;
86              
87 3 50       10 if ( $arg ) {
88 3 100       13 unless ( $arg =~ m|\A/.+\z| ) {
89 1         6 __errstr($self, 'invalid path: '.$arg);
90 1         7 return;
91             }
92 2         9 $self->{iptbinary} = $arg;
93             }
94              
95 2         13 return $self->{iptbinary};
96             }
97              
98             sub iptaction {
99 14     14 1 341 my $self = shift;
100 14         22 my ($arg) = @_;
101              
102 14 50       36 if ( $arg ) {
103 14 100       51 unless ( $arg =~ m/\A-[ADIRLSFZNXPE]\z/ ) {
104 2         8 __errstr($self, 'invalid action: '.$arg);
105 2         89 return;
106             }
107 12         23 $self->{iptaction} = $arg;
108             }
109              
110 12         58 return $self->{iptaction};
111             }
112              
113             sub ipversion {
114 12     12 1 1054 my $self = shift;
115 12         20 my ($arg) = @_;
116              
117 12 50       31 if ( $arg ) {
118             # Valid arguments are 4 and 6
119 12 100       38 unless ( $arg =~ m/\A[46]\z/ ) {
120 4         14 __errstr($self, 'invalid ip version: '.$arg);
121 4         16 return;
122             }
123              
124 8         20 $self->{ipver} = $arg;
125             }
126              
127 8         23 return $self->{ipver};
128             }
129              
130             sub table {
131 8     8 1 385 my $self = shift;
132 8         12 my ($arg) = @_;
133              
134 8 50       23 if ( $arg ) {
135 8         10 my $need_to_barf;
136 8 50 66     79 $need_to_barf = 1 if ( $self->{ipver} eq '4' and $arg !~ m/\A(filter|nat|mangle|raw)\z/i );
137 8 50 66     44 $need_to_barf = 1 if ( $self->{ipver} eq '6' and $arg !~ m/\A(filter|mangle|raw)\z/i );
138 8 50       19 if ( $need_to_barf ) {
139 0         0 __errstr($self, sprintf('invalid table "%s" for ip version: %s', $arg, $self->{ipver}));
140 0         0 return;
141             }
142              
143 8         17 $self->{table} = $arg;
144             }
145              
146 8         41 return $self->{table};
147             }
148              
149             sub chain {
150 8     8 1 1634 my $self = shift;
151 8         15 my ($arg) = @_;
152              
153 8 50       21 if ( $arg ) {
154 8         17 $self->{chain} = $arg;
155             }
156              
157 8         24 return $self->{chain};
158             }
159              
160             sub target {
161 10     10 1 385 my $self = shift;
162 10         17 my ($arg) = @_;
163              
164 10 50       23 if ( $arg ) {
165 10         19 $self->{target} = $arg;
166             }
167              
168 10         31 return $self->{target};
169             }
170              
171             *protocol = \&proto;
172             sub proto {
173 11     11 1 602 my $self = shift;
174 11         20 my ($arg) = @_;
175              
176 11 50       26 if ( $arg ) {
177 11 50       60 unless ( $arg =~ m/\A[a-z0-9]+\z/ ) {
178 0         0 __errstr($self, 'invalid protocol: '.$arg);
179 0         0 return;
180             }
181 11 100 100     47 if ( $self->{ipver} eq '6' and $arg eq 'icmp' ) {
182 1         4 __errstr($self, 'icmp not valid protocol for IPv6. Perhaps you meant "icmpv6"?');
183 1         4 return;
184             }
185 10 100 100     61 if ( $self->{ipver} eq '4' and $arg eq 'icmpv6' ) {
186 1         5 __errstr($self, 'icmpv6 not valid protocol for IPv4. Perhaps you meant "icmp"?');
187 1         10 return;
188             }
189              
190 9         21 $self->{proto} = $arg;
191             }
192              
193 9         37 return $self->{proto};
194             }
195              
196             sub in {
197 9     9 1 353 my $self = shift;
198 9         14 my ($arg) = @_;
199              
200 9 50       25 if ( $arg ) {
201 9         18 $self->{in} = $arg;
202             }
203              
204 9         37 return $self->{in};
205             }
206              
207             sub out {
208 8     8 1 317 my $self = shift;
209 8         14 my ($arg) = @_;
210              
211 8 50       22 if ( $arg ) {
212 8         15 $self->{out} = $arg;
213             }
214              
215 8         36 return $self->{out};
216             }
217              
218             *source = \&src;
219             sub src {
220 17     17 0 418 my $self = shift;
221 17         26 my ($arg) = @_;
222              
223 17 50       42 if ( $arg ) {
224 17 100 100     37 unless (
      66        
225             __is_valid_inet_host($arg) or
226             __is_valid_inet_cidr($arg) or
227             __is_valid_inet_range($arg)
228             ) {
229 7         22 __errstr($self, 'invalid source address: '.$arg);
230 7         34 return;
231             }
232              
233 10         61 $self->{src} = $arg;
234             }
235              
236 10         45 return $self->{src};
237             }
238              
239             *dest = \&dst;
240             *destination = \&dst;
241             sub dst {
242 16     16 0 354 my $self = shift;
243 16         26 my ($arg) = @_;
244              
245 16 50       39 if ( $arg ) {
246 16 100 100     64 unless (
      66        
247             __is_valid_inet_host($arg) or
248             __is_valid_inet_cidr($arg) or
249             __is_valid_inet_range($arg)
250             ) {
251 7         24 __errstr($self, 'invalid destination address: '.$arg);
252 7         35 return;
253             }
254              
255 9         55 $self->{dst} = $arg;
256             }
257              
258 9         55 return $self->{dst};
259             }
260              
261             *port = \&dpt;
262             *dport = \&dpt;
263             sub dpt {
264 14     14 1 390 my $self = shift;
265 14         23 my ($arg) = @_;
266              
267 14 50       34 if ( $arg ) {
268 14 100       30 unless ( __is_valid_inet_port($arg) ) {
269 5         15 __errstr($self, 'invalid destination port: '.$arg);
270 5         22 return;
271             }
272              
273 9         19 $self->{dpt} = $arg;
274             }
275              
276 9         48 return $self->{dpt};
277             }
278              
279             *sport = \&spt;
280             sub spt {
281 11     11 1 383 my $self = shift;
282 11         20 my ($arg) = @_;
283              
284 11 50       26 if ( $arg ) {
285 11 100       22 unless ( __is_valid_inet_port($arg) ) {
286 5         16 __errstr($self, 'invalid source port: '.$arg);
287 5         23 return;
288             }
289              
290 6         13 $self->{spt} = $arg;
291             }
292              
293 6         27 return $self->{spt};
294             }
295              
296             sub mac {
297 4     4 1 356 my $self = shift;
298 4         9 my ($arg) = @_;
299              
300 4 50       9 if ( $arg ) {
301 4 100       9 unless ( __is_valid_mac_address($arg) ) {
302 3         10 __errstr($self, 'invalid mac address: '.$arg);
303 3         25 return;
304             }
305              
306 1         4 $self->{mac} = $arg;
307             }
308              
309 1         6 return $self->{mac};
310             }
311              
312             sub state {
313 7     7 1 256 my $self = shift;
314 7         11 my ($arg) = @_;
315              
316 7 50       15 if ( $arg ) {
317 7 100       36 unless ( $arg =~ m/\A(NEW|ESTABLISHED|RELATED|INVALID|UNTRACKED)\z/i ) {
318 1         8 __errstr($self, 'invalid connection tracking state: '.$arg);
319 1         5 return;
320             }
321 6         12 $self->{state} = $arg;
322             }
323              
324 6         24 return $self->{state};
325             }
326              
327             *rate_limit = \&limit;
328             sub limit {
329 17     17 1 393 my $self = shift;
330 17         40 my ($arg) = @_;
331              
332 17 50       42 if ( $arg ) {
333             # --limit rate[/second|/minute|/hour|/day]
334 17 100       97 unless ( $arg =~ m/\A\d+\/(s(ec(ond)?)?|m(in(ute)?)?|h(our)?|d(ay)?)\z/i ) {
335 6         21 __errstr($self, 'invalid rate limit: '.$arg);
336 6         28 return;
337             }
338 11         24 $self->{limit} = $arg;
339             }
340              
341 11         54 return $self->{limit};
342             }
343              
344             sub icmp_type {
345 10     10 1 17 my $self = shift;
346 10         14 my ($arg) = @_;
347              
348 10 50       27 if ( $arg ) {
349 10 100       56 unless ( $arg =~ m|\A[a-z0-9\-]+(/[a-z0-9\-]+)?\z|i ) {
350 2         8 __errstr($self, 'invalid icmp type: '.$arg);
351 2         9 return;
352             }
353              
354 8         16 $self->{icmp_type} = $arg;
355             }
356              
357 8         47 return $self->{icmp_type};
358             }
359              
360             sub logprefix {
361 6     6 1 622 my $self = shift;
362 6         10 my ($arg) = @_;
363              
364 6         10 my $max_length = 29;
365              
366 6 50       17 if ( $arg ) {
367 6 100       17 if ( length($arg) > $max_length ) {
368 3         15 __errstr($self, 'log prefix too long (>'.$max_length.'): '.$arg);
369 3         13 return;
370             }
371 3 50       9 if ( $arg =~ m/[\"\']/ ) {
372 0         0 __errstr($self, 'quotes not permitted: '.$arg);
373 0         0 return;
374             }
375              
376 3         6 $self->{logprefix} = $arg;
377             }
378              
379 3         18 return $self->{logprefix};
380             }
381              
382             sub comment {
383 7     7 1 349 my $self = shift;
384 7         13 my ($arg) = @_;
385              
386 7         7 my $max_length = 256;
387              
388 7 50       17 if ( $arg ) {
389 7 100       17 if ( length($arg) > $max_length ) {
390 1         7 __errstr($self, 'comment too long (>'.$max_length.'): '.$arg);
391 1         5 return;
392             }
393 6 100       19 if ( $arg =~ m/[\"\']/ ) {
394 1         5 __errstr($self, 'quotes not permitted: '.$arg);
395 1         5 return;
396             }
397              
398 5         10 $self->{comment} = $arg;
399             }
400              
401 5         16 return $self->{comment};
402             }
403              
404             *compile = \&generate;
405             sub generate {
406 6     6 1 22 my $self = shift;
407              
408             # what is required?
409 6 50       16 unless ( $self->{chain} ) {
410 0         0 __errstr($self, 'Chain must be specified');
411 0         0 return;
412             }
413             # ports are only valid with protocol tcp and udp
414 6 50 33     17 if ( defined($self->{spt}) and $self->{proto} !~ m/\A(tcp|udp)\z/i ) {
415 0         0 __errstr($self, 'Protocol must be TCP or UDP when specifying source port');
416 0         0 return;
417             }
418 6 50 66     25 if ( defined($self->{dpt}) and $self->{proto} !~ m/\A(tcp|udp)\z/i ) {
419 0         0 __errstr($self, 'Protocol must be TCP or UDP when specifying destinatipn port');
420 0         0 return;
421             }
422             # cant use 'logprefix' unless the target is 'log'
423 6 50 33     20 if ( defined($self->{logprefix}) and $self->{target} !~ m/\Alog\z/i ) {
424 0         0 __errstr($self, 'Target must be LOG when specifying log prefix');
425 0         0 return;
426             }
427             # ipversion matches the source/dest addresses?
428 6 100       18 if ( $self->{ipver} eq '4' ) {
    50          
429 5 100       13 if ( $self->{src} ) {
430             # make sure it's ipv4
431 1 50       6 unless ( __is_valid_inet4($self->{src}) ) {
432 1         10 __errstr($self, 'IP Version is 4 but source is not valid IPv4');
433 1         4 return;
434             }
435             }
436 4 50       25 if ( $self->{dst} ) {
437             # make sure it's ipv4
438 0 0       0 unless ( __is_valid_inet4($self->{dst}) ) {
439 0         0 __errstr($self, 'IP Version is 4 but destination is not valid IPv4');
440 0         0 return;
441             }
442             }
443             } elsif ( $self->{ipver} eq '6' ) {
444 1 50       3 if ( $self->{src} ) {
445             # make sure it's ipv6
446 1 50       4 unless ( __is_valid_inet6($self->{src}) ) {
447 1         4 __errstr($self, 'IP Version is 6 but source is not valid IPv6');
448 1         4 return;
449             }
450             }
451 0 0       0 if ( $self->{dst} ) {
452             # make sure it's ipv6
453 0 0       0 unless ( __is_valid_inet6($self->{dst}) ) {
454 0         0 __errstr($self, 'IP Version is 6 but destination is not valid IPv6');
455 0         0 return;
456             }
457             }
458             } else {
459             # should never happen; the ipversion sub validates user input
460 0         0 __errstr($self, 'Code bug 0x01; Please report to developer.');
461 0         0 return;
462             }
463             # if icmp_type is set, protocol must be icmp or icmpv6
464 4 50 33     11 if ( defined($self->{icmp_type}) and $self->{proto} !~ m/\Aicmp(v6)?\z/i ) {
465 0         0 __errstr($self, 'icmp_type is set, but protocol is: '.$self->{proto});
466 0         0 return;
467             }
468              
469 4         5 my $rule_prefix;
470             my $rule_criteria;
471              
472 4         9 $rule_prefix = $self->{iptbinary};
473 4 100       17 $rule_prefix .= ' -t '.$self->{table} if ( defined($self->{'table'}) );
474 4         8 $rule_prefix .= ' '.$self->{iptaction};
475 4         7 $rule_prefix .= ' '.$self->{chain};
476            
477             # Source and Destination Addresses
478 4 50       10 if ( defined($self->{src}) ) {
479 0 0 0     0 if ( __is_valid_inet_host($self->{src}) or &is_valid_inet_cidr($self->{src}) ) {
480 0         0 $rule_criteria .= sprintf(' -s %s', $self->{src});
481             }
482 0 0       0 if ( __is_valid_inet_range($self->{src}) ) {
483 0         0 $rule_criteria .= sprintf(' -m iprange --src-range %s', $self->{'src'});
484             }
485             }
486 4 50       10 if ( defined($self->{dst}) ) {
487 0 0 0     0 if ( __is_valid_inet_host($self->{dst}) or &is_valid_inet_cidr($self->{dst}) ) {
488 0         0 $rule_criteria .= sprintf(' -d %s', $self->{dst});
489             }
490 0 0       0 if ( __is_valid_inet_range($self->{dst}) ) {
491 0         0 $rule_criteria .= sprintf(' -m iprange --dst-range %s', $self->{'dst'});
492             }
493             }
494            
495             # Source and Destination Ports
496 4 50       11 if ( defined($self->{spt}) ) {
497 0 0       0 if ( $self->{spt} =~ m/\A\w+\z/ ) {
498             # just a single port
499 0         0 $rule_criteria .= sprintf(' --sport %s', $self->{'spt'});
500             }
501 0 0       0 if ( $self->{spt} =~ m/\A\w+(:\w+)+\z/ ) {
502             # port range
503 0         0 $rule_criteria .= sprintf(' --sport %s', $self->{'spt'});
504             }
505 0 0       0 if ( $self->{spt} =~ m/\A\w+(:\w+)+\z/ ) {
506             # multiport
507 0         0 $rule_criteria .= sprintf(' -m multiport --sports %s', $self->{'spt'});
508             }
509             }
510 4 100       9 if ( defined($self->{dpt}) ) {
511 1 50       6 if ( $self->{dpt} =~ m/\A\w+\z/ ) {
512             # just a single port
513 1         4 $rule_criteria .= sprintf(' --dport %s', $self->{'dpt'});
514             }
515 1 50       14 if ( $self->{dpt} =~ m/\A\w+(:\w+)+\z/ ) {
516             # port range
517 0         0 $rule_criteria .= sprintf(' --dport %s', $self->{'dpt'});
518             }
519 1 50       4 if ( $self->{dpt} =~ m/\A\w+(:\w+)+\z/ ) {
520             # multiport
521 0         0 $rule_criteria .= sprintf(' -m multiport --dports %s', $self->{'dpt'});
522             }
523             }
524              
525 4 100       57 $rule_criteria .= sprintf(' -i %s', $self->{in}) if ( defined($self->{in}) );
526 4 100       108 $rule_criteria .= sprintf(' -o %s', $self->{out}) if ( defined($self->{out}) );
527 4 100       12 $rule_criteria .= sprintf(' -p %s', $self->{proto}) if ( defined($self->{proto}) );
528 4 50       9 $rule_criteria .= sprintf(' -m mac --mac-source %s', $self->{mac}) if ( defined($self->{mac}) );
529 4 100       11 $rule_criteria .= sprintf(' -m conntrack --ctstate %s', $self->{state}) if ( defined($self->{state}) );
530 4 50       9 $rule_criteria .= sprintf(' --icmp-type %s', $self->{icmp_type}) if ( defined($self->{icmp_type}) );
531 4 100       14 $rule_criteria .= sprintf(' -m comment --comment "%s"', $self->{comment}) if ( defined($self->{comment}) );
532 4 50       8 $rule_criteria .= sprintf(' -m limit --limit %s', $self->{limit}) if ( defined($self->{limit}) );
533 4 50       13 $rule_criteria .= sprintf(' -j %s', $self->{'target'}) if ( defined($self->{'target'}) );
534 4 50       12 $rule_criteria .= sprintf(' --log-prefix "[%s] "', $self->{logprefix}) if ( defined($self->{logprefix}) );
535              
536             # $ipt_rule .= sprintf(' -m statistic %s', $criteria{'statistic'}) if (defined($criteria{'statistic'}));
537             # $ipt_rule .= sprintf(' -m time %s', $criteria{'time'}) if (defined($criteria{'time'}));
538              
539 4         9 my $full_cmd = $rule_prefix.$rule_criteria;
540 4         17 return $full_cmd;
541             }
542              
543             ###############################################################################
544             ### INTERNAL HELPERS
545             # These are subs that are NOT expected to be used outside this module itself.
546             # They are for internal code reuse only.
547             # All sub named should be prefixed with double underslash (__) to indicate they
548             # are internal use only.
549              
550             sub __is_valid_mac_address {
551 4     4   6 my ( $arg ) = @_;
552 4         7 chomp($arg);
553              
554 4 50       9 return unless ( $arg );
555              
556 4 100       77 if ( $arg =~ m/\A$qr_mac_addr\z/ ) {
557 1         5 return 1;
558             }
559              
560             # fail by default
561 3         8 return;
562             }
563              
564             sub __is_valid_inet4 {
565 1     1   2 my ( $arg ) = @_;
566 1         3 chomp($arg);
567              
568 1 50       4 return unless ( $arg );
569              
570             # ipv4 address?
571 1 50       4 return 1 if ( __is_inet4_host($arg) );
572              
573             # ipv4 cidr?
574 1 50       3 return 1 if ( __is_inet4_cidr($arg) );
575              
576             # ipv4 range?
577 1 50       4 return 1 if ( __is_inet4_range($arg) );
578              
579             # fqdn?
580 1 50       287 return 1 if ( $arg =~ m/\A$qr_fqdn\z/ );
581              
582             # fail by default
583 1         17 return;
584             }
585              
586             sub __is_valid_inet6 {
587 1     1   2 my ( $arg ) = @_;
588 1         2 chomp($arg);
589              
590 1 50       3 return unless ( $arg );
591              
592             # ipv6 address?
593 1 50       3 return 1 if ( __is_inet6_host($arg) );
594              
595             # ipv4 cidr?
596 1 50       3 return 1 if ( __is_inet6_cidr($arg) );
597              
598             # ipv4 range?
599 1 50       3 return 1 if ( __is_inet6_range($arg) );
600              
601             # fqdn?
602 1 50       206 return 1 if ( $arg =~ m/\A$qr_fqdn\z/ );
603              
604             # fail by default
605 1         16 return;
606             }
607              
608             sub __is_valid_inet_host {
609 33     33   49 my ( $arg ) = @_;
610 33         53 chomp($arg);
611              
612 33 50       66 return unless ( $arg );
613              
614             # ipv4 address?
615 33 100       61 return 1 if ( __is_inet4_host($arg) );
616              
617             # ipv6 address?
618 30 100       54 return 1 if ( __is_inet6_host($arg) );
619              
620             # fqdn?
621 26 100       545 return 1 if ( $arg =~ m/\A$qr_fqdn\z/ );
622              
623             # fail by default
624 21         84 return;
625             }
626              
627             sub __is_inet4_host {
628 34     34   45 my ( $arg ) = @_;
629 34         36 chomp($arg);
630              
631 34 50       73 return unless ( $arg );
632              
633             # ipv4 address?
634 34 100       343 return 1 if ( $arg =~ m/\A$qr_ip4_addr\z/ );
635              
636             # fail by default
637 31         87 return;
638             }
639              
640             sub __is_inet6_host {
641 31     31   48 my ( $arg ) = @_;
642 31         36 chomp($arg);
643              
644 31 50       53 return unless ( $arg );
645              
646             # ipv6 address?
647 31 100       1055 return 1 if ( $arg =~ m/\A$qr_ip6_addr\z/ );
648              
649             # fail by default
650 27         87 return;
651             }
652              
653             sub __is_valid_inet_cidr {
654 21     21   29 my ( $arg ) = @_;
655 21         31 chomp($arg);
656              
657 21 50       38 return unless ( $arg );
658              
659             # ipv4 cidr?
660 21 100       40 return 1 if ( __is_inet4_cidr($arg) );
661              
662             # ipv6 cidr?
663 19 100       40 return 1 if ( __is_inet6_cidr($arg) );
664              
665             # fail by default
666 14         69 return;
667             }
668              
669             sub __is_inet4_cidr {
670 22     22   31 my ( $arg ) = @_;
671 22         27 chomp($arg);
672              
673 22 50       42 return unless ( $arg );
674              
675             # ipv4 cidr?
676 22 100       175 if ( $arg =~ m/\A$qr_ip4_cidr\z/ ) {
677             # validate the cidr
678 4         19 my ($host, $cidr) = split(/\//, $arg);
679 4 50       14 return if ( $cidr < 0 );
680 4 100       14 return if ( $cidr > 32 );
681              
682 2         21 return 1;
683             }
684              
685             # fail by default
686 18         48 return;
687             }
688              
689             sub __is_inet6_cidr {
690 20     20   27 my ( $arg ) = @_;
691 20         25 chomp($arg);
692              
693 20 50       39 return unless ( $arg );
694              
695             # ipv6 cidr?
696 20 100       801 if ( $arg =~ m/\A$qr_ip6_cidr\z/ ) {
697             # validate the cidr
698 9         35 my ($host, $cidr) = split(/\//, $arg);
699 9 50       28 return if ( $cidr < 0 );
700 9 100       29 return if ( $cidr > 128 );
701              
702 5         37 return 1;
703             }
704              
705             # fail by default
706 11         27 return;
707             }
708              
709             sub __is_valid_inet_range {
710 14     14   21 my ( $arg ) = @_;
711 14         18 chomp($arg);
712              
713 14 50       26 return unless ( $arg );
714              
715             # ipv4 address range?
716 14 50       25 return 1 if ( __is_inet4_range($arg) );
717              
718             # ipv6 address range?
719 14 50       59 return 1 if ( __is_inet6_range($arg) );
720              
721             # fail by default
722 14         53 return;
723             }
724              
725             sub __is_inet4_range {
726 15     15   22 my ( $arg ) = @_;
727 15         20 chomp($arg);
728              
729 15 50       25 return unless ( $arg );
730              
731             # ipv4 address range?
732 15 50       181 return 1 if (
733             $arg =~ m/\A$qr_ip4_addr\-$qr_ip4_addr\z/
734             );
735              
736             # fail by default
737 15         42 return;
738             }
739              
740             sub __is_inet6_range {
741 15     15   21 my ( $arg ) = @_;
742 15         18 chomp($arg);
743              
744 15 50       24 return unless ( $arg );
745              
746             # ipv6 address range?
747 15 50       1635 return 1 if (
748             $arg =~ m/\A$qr_ip6_addr\-$qr_ip6_addr\z/
749             );
750              
751             # fail by default
752 15         70 return;
753             }
754              
755             sub __is_valid_inet_port {
756 41     41   70 my ( $arg ) = @_;
757 41         63 chomp($arg);
758              
759 41 50       75 return unless ( $arg );
760              
761             # just a numeric port?
762 41 100       65 if ( __is_a_number($arg) ) {
763 21 50       51 return if ( $arg < 0 );
764 21 100       43 return if ( $arg > 65535 );
765              
766 19         54 return 1;
767             }
768              
769             # just a named port?
770 20 100       67 if ( $arg =~ m/\A[a-z]+\z/i ) {
771 6         19 return 1;
772             }
773              
774             # numeric port range?
775 14 100       41 if ( $arg =~ /\A\d+:\d+\z/ ) {
776 4         15 my ( $lower, $upper) = split(/:/, $arg, 2);
777              
778             # recursive call to this sub to validate individal ports in multiport
779 4 50       14 return unless ( __is_valid_inet_port($lower) );
780 4 50       6 return unless ( __is_valid_inet_port($upper) );
781              
782             # lower is higher than upper?
783 4 100       16 return if ( $upper < $lower );
784              
785 2         6 return 1;
786             }
787              
788             # named port range?
789 10 50       22 if ( $arg =~ /\A[a-z]+:[a-z]+\z/i ) {
790 0         0 my ( $lower, $upper) = split(/:/, $arg, 2);
791              
792             # recursive call to this sub to validate individal ports in multiport
793 0 0       0 return unless ( __is_valid_inet_port($lower) );
794 0 0       0 return unless ( __is_valid_inet_port($upper) );
795              
796 0         0 return 1;
797             }
798              
799             # numeric multiport?
800 10 100       28 if ( $arg =~ /\A\d+(,\d+)+\z/ ) {
801 2         9 my @ports = split(/,/, $arg);
802              
803 2         6 foreach my $port ( @ports ) {
804             # recursive call to this sub to validate individal ports in multiport
805 4 50       8 return unless ( __is_valid_inet_port($port) );
806             }
807              
808 2         7 return 1;
809             }
810              
811             # named multiport?
812 8 100       24 if ( $arg =~ /\A[a-z]+(,[a-z]+)+\z/i ) {
813 2         8 my @ports = split(/,/, $arg);
814              
815 2         5 foreach my $port ( @ports ) {
816             # recursive call to this sub to validate individal ports in multiport
817 4 50       9 return unless ( __is_valid_inet_port($port) );
818             }
819              
820 2         8 return 1;
821             }
822              
823             # fail by default
824 6         14 return;
825             }
826              
827             sub __is_a_number {
828 41     41   51 my ( $arg) = @_;
829 41 100       180 return 1 if ( $arg =~ /\A-?\d+\z/);
830 20         45 return;
831             }
832              
833             sub __errstr {
834 52     52   73 my $self = shift;
835 52         69 my $errstr = shift;
836 52         92 $self->{errstr} = $errstr;
837 52         80 return 1;
838             }
839              
840             1;
841             __END__