File Coverage

blib/lib/IPTables/Rule.pm
Criterion Covered Total %
statement 333 378 88.1
branch 175 280 62.5
condition 25 42 59.5
subroutine 42 42 100.0
pod 21 24 87.5
total 596 766 77.8


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