File Coverage

blib/lib/IPTables/Rule.pm
Criterion Covered Total %
statement 324 369 87.8
branch 170 272 62.5
condition 25 42 59.5
subroutine 41 41 100.0
pod 20 23 86.9
total 580 747 77.6


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