File Coverage

blib/lib/Parse/Netstat/Search.pm
Criterion Covered Total %
statement 174 351 49.5
branch 57 144 39.5
condition 47 78 60.2
subroutine 12 32 37.5
pod 22 26 84.6
total 312 631 49.4


line stmt bran cond sub pod time code
1             package Parse::Netstat::Search;
2              
3 2     2   132114 use 5.006;
  2         19  
4 2     2   20 use strict;
  2         5  
  2         55  
5 2     2   12 use warnings;
  2         8  
  2         86  
6 2     2   12 use base 'Error::Helper';
  2         4  
  2         1023  
7 2     2   2567 use Net::CIDR;
  2         11287  
  2         101  
8 2     2   990 use Net::DNS;
  2         189872  
  2         7519  
9              
10             =head1 NAME
11              
12             Parse::Netstat::Search - Searches the connection list in the results returned by Parse::Netstat
13              
14             =head1 VERSION
15              
16             Version 0.2.2
17              
18             =cut
19              
20             our $VERSION = '0.2.2';
21              
22              
23             =head1 SYNOPSIS
24              
25              
26             use Parse::Netstat::Search;
27             use Parse::Netstat qw(parse_netstat);
28              
29             my $res = parse_netstat(output => join("", `netstat -n`), flavor=>$^O);
30              
31             my $search = Parse::Netstat::Search->new();
32              
33             $search->set_cidrs( [ '10.0.0.0/24', '192.168.0.0/16' ] );
34              
35             my @found=$search->search($res);
36              
37              
38             Two big things to bet aware of is this module does not currently resulve names and this module
39             does not handle unix sockets. Unix sockets will just be skipped over.
40              
41             The connection hashes returned differ from Parse::Netstat slightly. Below is what a standard ones
42             for IPv4/6 looks like.
43              
44             {
45             'foreign_host'=>'10.0.0.1',
46             'local_host'=>'10.0.0.2',
47             'foreign_port'=>'22222',
48             'local_port'=>'22',
49             'sendq'=>'0',
50             'recvq'=>'0',
51             'state' => 'ESTABLISHED',
52             'proto' => 'tcp4',
53             }
54              
55             This module has two additional keys, "local_pp" and "foreign_pp". Which contains and data
56             after % in a address. So "fe80::1%lo0" would be split into "fe80::1" and "lo0" as in the
57             example below.
58              
59             {
60             'state' => '',
61             'foreign_host' => '*',
62             'local_port' => '123',
63             'proto' => 'udp6',
64             'foreign_pp' => undef,
65             'foreign_port' => '*',
66             'local_host' => 'fe80::1',
67             'recvq' => '44',
68             'local_pp' => 'lo0',
69             'sendq' => '33'
70             }
71              
72             =head1 methods
73              
74             =head2 new
75              
76             This initiates it.
77              
78             No values are taken.
79              
80             my $search=Parse::Netstat::Search->new;
81              
82             =cut
83              
84             sub new{
85 1     1 1 673 my $self={
86             perror=>undef,
87             error=>undef,
88             errorString=>'',
89             errorExtra=>{
90             '1'=>'badCIDR',
91             '2' =>'unknownService',
92             '3'=>'badResults',
93             },
94             cidrs=>[],
95             protocols=>{},
96             ports=>{},
97             states=>{},
98             ptrs=>{},
99             ptrs_r=>[],
100             resolver=>Net::DNS::Resolver->new,
101             ptr_invert=>0,
102             ptr_r_invert=>0,
103             cidr_invert=>0,
104             protocol_invert=>0,
105             state_invert=>0,
106             port_invert=>0,
107             };
108 1         454 bless $self;
109              
110 1         4 return $self;
111              
112             }
113              
114             =head2 get_cidrs
115              
116             Retrieves the CIDR match list.
117              
118             The returned value is an array.
119              
120             my @CIDRs=$search->get_cidrs;
121              
122             =cut
123              
124             sub get_cidrs{
125 0     0 1 0 my $self=$_[0];
126              
127 0 0       0 if( ! $self->errorblank ){
128 0         0 return undef;
129             }
130              
131 0         0 return @{ $self->{cidrs} };
  0         0  
132             }
133              
134             =head2 get_cidrs_invert
135              
136             Gets the invert status of the CIDRs search.
137              
138             if ( $search->get_cidrs_invert ){
139             print "The search will be inverted\n";
140             }else{
141             print "The search will not be inverted";
142             }
143              
144             =cut
145              
146             sub get_cidrs_invert{
147 0     0 1 0 my $self=$_[0];
148              
149 0 0       0 if( ! $self->errorblank ){
150 0         0 return undef;
151             }
152              
153 0         0 return $self->{cidr_invert};
154             }
155              
156             =head2 get_ports
157              
158             Gets a list of desired ports.
159              
160             The returned value is a array. Each item is a port number,
161             regardless of if it was set based on number or service name.
162              
163             my @ports=$search->get_ports;
164              
165             =cut
166              
167             sub get_ports{
168 0     0 1 0 my $self=$_[0];
169              
170 0 0       0 if( ! $self->errorblank ){
171 0         0 return undef;
172             }
173              
174 0         0 return keys( %{ $self->{ports} } );
  0         0  
175             }
176              
177             =head2 get_ports_invert
178              
179             Gets the invert status of the ports search.
180              
181             if ( $search->get_ports_invert ){
182             print "The search will be inverted\n";
183             }else{
184             print "The search will not be inverted";
185             }
186              
187             =cut
188              
189             sub get_ports_invert{
190 0     0 1 0 my $self=$_[0];
191              
192 0 0       0 if( ! $self->errorblank ){
193 0         0 return undef;
194             }
195              
196 0         0 return $self->{port_invert};
197             }
198              
199             =head2 get_protocols
200              
201             Gets a list of desired protocols.
202              
203             The returned value is a array.
204              
205             Also if you've passed any named ones to it previously,
206             this will not return them, but the port number as that
207             is how they are stored internlly.
208              
209             my @protocols=$search->get_protocols;
210              
211             =cut
212              
213             sub get_protocols{
214 0     0 1 0 my $self=$_[0];
215              
216 0 0       0 if( ! $self->errorblank ){
217 0         0 return undef;
218             }
219              
220 0         0 return keys( %{ $self->{protocols} } );
  0         0  
221             }
222              
223             =head2 get_protocols_invert
224              
225             Gets the invert status of the protocols search.
226              
227             if ( $search->get_protocols_invert ){
228             print "The search will be inverted\n";
229             }else{
230             print "The search will not be inverted";
231             }
232              
233             =cut
234              
235             sub get_protocols_invert{
236 0     0 1 0 my $self=$_[0];
237              
238 0 0       0 if( ! $self->errorblank ){
239 0         0 return undef;
240             }
241              
242 0         0 return $self->{protocol_invert};
243             }
244              
245             =head2 get_states
246              
247             Get a list of desired sets.
248              
249             The returned value is a array.
250              
251             The returned values are all lowercased. Any trailing
252             or proceeding whitespace will also have been removed.
253              
254             my @states=$search->get_states;
255              
256             =cut
257              
258             sub get_states{
259 0     0 1 0 my $self=$_[0];
260              
261 0 0       0 if( ! $self->errorblank ){
262 0         0 return undef;
263             }
264              
265 0         0 return keys( %{ $self->{states} } );
  0         0  
266             }
267              
268             =head2 get_state_invert
269              
270             Gets the invert status of the states search.
271              
272             if ( $search->get_state_invert ){
273             print "The search will be inverted\n";
274             }else{
275             print "The search will not be inverted";
276             }
277              
278             =cut
279              
280             sub get_states_invert{
281 0     0 0 0 my $self=$_[0];
282              
283 0 0       0 if( ! $self->errorblank ){
284 0         0 return undef;
285             }
286              
287 0         0 return $self->{state_invert};
288             }
289              
290             =head2 get_ptrs
291              
292             Gets the list of PTRs to search for.
293              
294             The returned value is a array. Each item is a PTR.
295              
296             my @PTRs=$search->get_ptrs;
297              
298             =cut
299              
300             sub get_ptrs{
301 0     0 1 0 my $self=$_[0];
302              
303 0 0       0 if( ! $self->errorblank ){
304 0         0 return undef;
305             }
306              
307 0         0 return keys( %{ $self->{ptrs} } );
  0         0  
308             }
309              
310             =head2 get_ptrs_invert
311              
312             Gets the invert status of the PTRs search.
313              
314             if ( $search->get_ptr_invert ){
315             print "The search will be inverted\n";
316             }else{
317             print "The search will not be inverted";
318             }
319              
320             =cut
321              
322             sub get_ptrs_invert{
323 0     0 1 0 my $self=$_[0];
324              
325 0 0       0 if( ! $self->errorblank ){
326 0         0 return undef;
327             }
328              
329 0         0 return $self->{ptr_invert};
330             }
331              
332             =head2 get_ptrs_r
333              
334             Gets the list of PTR regexps to search for.
335              
336             The returned value is a array. Each item is a PTR.
337              
338             my @regexps=$search->get_ptrs_r;
339              
340             =cut
341              
342             sub get_ptrs_r{
343 0     0 1 0 my $self=$_[0];
344              
345 0 0       0 if( ! $self->errorblank ){
346 0         0 return undef;
347             }
348              
349 0         0 return @{ $self->{ptrs_r} };
  0         0  
350             }
351              
352             =head2 get_ptrs_invert
353              
354             Gets the invert status of the PTRs search.
355              
356             if ( $search->get_ptr_invert ){
357             print "The search will be inverted\n";
358             }else{
359             print "The search will not be inverted";
360             }
361              
362             =cut
363              
364             sub get_ptrs_r_invert{
365 0     0 0 0 my $self=$_[0];
366              
367 0 0       0 if( ! $self->errorblank ){
368 0         0 return undef;
369             }
370              
371 0         0 return $self->{ptr_r_invert};
372             }
373              
374             =head2 search
375              
376             This runs the search results.
377              
378             my @found=$search->search( $res );
379              
380             =cut
381              
382             sub search{
383 20     20 1 104 my $self=$_[0];
384 20         31 my $res=$_[1];
385              
386 20 50       69 if( ! $self->errorblank ){
387 0         0 return undef;
388             }
389              
390             #make sure what ever we are passed is sane and very likely a return from Parse::Netdata
391 20 50 33     287 if (
      33        
392             ( ref( $res ) ne 'ARRAY' ) ||
393             ( ! defined( $res->[2] ) ) ||
394             ( ! defined( $res->[2]->{active_conns} ) )
395             ){
396 0         0 $self->{error}=3;
397 0         0 $self->{errorString}='$res->[2]->{active_conns} not defined. Does not appear to be a Parse::Netstat return';
398 0         0 $self->warn;
399 0         0 return undef;
400             }
401              
402             # holds the found results
403 20         33 my @found;
404              
405             # requirements checks, defaulting to not required
406 20         39 my $port_require=0;
407 20         27 my $cidr_require=0;
408 20         28 my $protocol_require=0;
409 20         25 my $state_require=0;
410 20         29 my $ptr_require=0;
411 20         56 my $ptr_r_require=0;
412              
413             # figure out what we need to check for
414 20 100       47 if (defined( $self->{cidrs}[0] )){
415 6         12 $cidr_require=1;
416             }
417 20 100       28 if (defined( (keys(%{ $self->{ports} }))[0] )){
  20         67  
418 4         6 $port_require=1;
419             }
420 20 100       53 if (defined( (keys(%{ $self->{protocols} }))[0] )){
  20         54  
421 3         6 $protocol_require=1;
422             }
423 20 100       27 if (defined( (keys(%{ $self->{states} }))[0] )){
  20         53  
424 4         7 $state_require=1;
425             }
426 20 50       32 if (defined( (keys(%{ $self->{ptrs} }))[0] )){
  20         46  
427 0         0 $ptr_require=1;
428             }
429 20 50       42 if (defined( $self->{ptrs_r}[0] )){
430 0         0 $ptr_r_require=1;
431             }
432              
433 20         30 my $res_int=0;
434 20         49 while ( defined( $res->[2]->{active_conns}->[$res_int] ) ){
435             # ignore unix sockets
436 160 100 66     523 if ( defined( $res->[2]->{active_conns}->[$res_int]->{proto} ) &&
437             ($res->[2]->{active_conns}->[$res_int]->{proto} ne 'unix')
438             ){
439 140         227 my $foreign_port=$res->[2]->{active_conns}->[$res_int]->{foreign_port};
440 140         206 my $state=$res->[2]->{active_conns}->[$res_int]->{state};
441 140         203 my $protocol=$res->[2]->{active_conns}->[$res_int]->{proto};
442 140         214 my $local_port=$res->[2]->{active_conns}->[$res_int]->{local_port};
443             #my $local_host=$res->[2]->{active_conns}->[$res_int]->{local_host};
444             #my $foreign_host=$res->[2]->{active_conns}->[$res_int]->{foreign_host};
445 140         203 my $sendq=$res->[2]->{active_conns}->[$res_int]->{sendq};
446 140         208 my $recvq=$res->[2]->{active_conns}->[$res_int]->{recvq};
447              
448             #handle IPv6 % stuff if needed
449 140         382 my ( $local_host, $local_pp ) = split( /\%/, $res->[2]->{active_conns}->[$res_int]->{local_host} );
450 140         388 my ( $foreign_host, $foreign_pp ) = split( /\%/, $res->[2]->{active_conns}->[$res_int]->{foreign_host} );
451              
452             # Handle when parse netstat chokes on lines like...
453             # udp6 0 0 fe80::4ecc:6aff:.123 *.*
454 140 100       347 if ( $local_host =~ /[0123456789AaBbCcDdEeFf]\:$/ ){
455 20         67 $local_host =~ s/\:$//;
456             }
457 140 50       264 if ( $foreign_host =~ /[0123456789AaBbCcDdEeFf]\:$/ ){
458 0         0 $foreign_host =~ s/\:$//;
459             }
460              
461             # UDP is stateless and in some cases on listening ports for it Parse::Netstat
462             # does not return any host, so use * for it.
463 140 50       249 if (!defined( $foreign_host )){
464 0         0 $foreign_host='*';
465             }
466 140 50 100     433 if (
      66        
467             ( $foreign_host eq '*' ) &&
468             ( $protocol =~ /^[Uu][Dd][Pp]/ ) &&
469             ( ! defined( $state ) )
470             ){
471 0         0 $state='';
472             }
473 140 50 33     267 if (
474             ( !defined( $state ) ) &&
475             ( $protocol =~ /^[Uu][Dd][Pp]/ )
476             ){
477 0         0 $state='';
478             }
479              
480             # checks for making sure a check is meet... defaults to 1
481 140         178 my $port_meet=1;
482 140         189 my $cidr_meet=1;
483 140         174 my $protocol_meet=1;
484 140         184 my $ptr_meet=1;
485 140         169 my $ptr_r_meet=1;
486 140         220 my $protocol_search=lc( $protocol );
487 140         186 my $state_meet=1;
488 140         201 my $state_search=lc( $state );
489              
490             # XOR the meet and require, setting the meet to false if required
491 140         194 $port_meet = $port_meet ^ $port_require;
492 140         186 $cidr_meet = $cidr_meet ^ $cidr_require;
493 140         231 $protocol_meet = $protocol_meet ^ $protocol_require;
494 140         188 $state_meet = $state_meet ^ $state_require;
495 140         166 $ptr_meet = $ptr_meet ^ $ptr_require;
496 140         197 $ptr_r_meet = $ptr_r_meet ^ $ptr_r_require;
497              
498             # checks the forient port against each CIDR
499 140         211 my @cidrs=@{ $self->{cidrs} };
  140         287  
500 140 100       262 if ( $cidr_require ){
501             # check each one by its self... Net::CIDR will error if you tell it to search for in IPv4 and IPv6 space at the same time
502 42         50 my @cidrs=@{ $self->{cidrs} };
  42         81  
503 42         68 my $cidr=pop( @cidrs );
504 42   100     140 while (
505             ( defined( $cidr ) ) &&
506             ( ! $cidr_meet )
507             ){
508 54 100 100     162 if (
      100        
      100        
509             (
510             ( $foreign_host ne '*' ) &&
511 31         73 ( eval{ Net::CIDR::cidrlookup( $foreign_host, $cidr ) })
512             ) || (
513             ( $local_host ne '*' ) &&
514 38         8286 ( eval{ Net::CIDR::cidrlookup( $local_host, $cidr ) } )
515             )
516             ){
517 14         4706 $cidr_meet=1;
518             }
519              
520 54         10734 $cidr=pop( @cidrs );
521             }
522             }
523              
524             # handle it if port checking is required
525 140 100 100     297 if (
      100        
526             $port_require &&
527             (
528             ( defined( $self->{ports}{$foreign_port} ) ) ||
529             ( defined( $self->{ports}{$local_port} ) )
530             )
531             ) {
532 20         29 $port_meet=1;
533             }
534              
535             # check protocol to see if it is one that is required
536 140 100 100     290 if (
537             $protocol_require &&
538             defined( $self->{protocols}{$protocol_search} )
539             ){
540 11         14 $protocol_meet=1;
541             }
542              
543             # check state to see if it is one that is required
544 140 100 100     304 if (
545             $state_require &&
546             defined( $self->{states}{$state_search} )
547             ){
548 8         13 $state_meet=1;
549             }
550              
551             # check if the PTR of any matches
552 140 50       220 if ( $ptr_require ){
553             #look both up
554 0         0 my $answer_f=$self->{resolver}->search( $foreign_host );
555 0         0 my $answer_l=$self->{resolver}->search( $local_host );
556              
557             # figure out if we have a ptr or not for foriegn host and if so grab it
558 0         0 my $ptr_f='NOTFOUND';
559 0 0 0     0 if ( defined( $answer_f->{answer}[0] ) &&
560             ( ref( $answer_f->{answer}[0] ) eq 'Net::DNS::RR::PTR' )
561             ){
562 0         0 $ptr_f=lc($answer_f->{answer}[0]->ptrdname);
563             }
564              
565             # figure out if we have a ptr or not for foriegn host and if so grab it
566 0         0 my $ptr_l='NOTFOUND';
567 0 0 0     0 if ( defined( $answer_l->{answer}[0] ) &&
568             ( ref( $answer_l->{answer}[0] ) eq 'Net::DNS::RR::PTR' )
569             ){
570 0         0 $ptr_l=lc($answer_l->{answer}[0]->ptrdname);
571             }
572              
573             # now that we have it, check if either are defined in the lookup table
574 0 0 0     0 if (
575             defined( $self->{ptrs}{$ptr_l} ) ||
576             defined( $self->{ptrs}{$ptr_f} )
577             ){
578 0         0 $ptr_meet=1;
579             }
580             }
581              
582             # check if the PTR of any matches
583 140 50       230 if ( $ptr_r_require ){
584             #look both up
585 0         0 my $answer_f=$self->{resolver}->search( $foreign_host );
586 0         0 my $answer_l=$self->{resolver}->search( $local_host );
587              
588             # figure out if we have a ptr or not for foriegn host and if so grab it
589 0         0 my $ptr_f='NOTFOUND';
590 0 0 0     0 if ( defined( $answer_f->{answer}[0] ) &&
591             ( ref( $answer_f->{answer}[0] ) eq 'Net::DNS::RR::PTR' )
592             ){
593 0         0 $ptr_f=lc($answer_f->{answer}[0]->ptrdname);
594             }
595              
596             # figure out if we have a ptr or not for foriegn host and if so grab it
597 0         0 my $ptr_l='NOTFOUND';
598 0 0 0     0 if ( defined( $answer_l->{answer}[0] ) &&
599             ( ref( $answer_l->{answer}[0] ) eq 'Net::DNS::RR::PTR' )
600             ){
601 0         0 $ptr_l=lc($answer_l->{answer}[0]->ptrdname);
602             }
603              
604             # check if any of them match
605 0         0 my @ptrs_r=@{ $self->{ptrs_r} };
  0         0  
606 0         0 my $ptr=pop( @ptrs_r );
607 0   0     0 while (
608             defined( $ptr ) &&
609             ( ! $ptr_r_meet )
610             ){
611              
612 0 0 0     0 if (
613             ( $ptr_f =~ /$ptr/ ) ||
614             ( $ptr_l =~ /$ptr/ )
615             ){
616 0         0 $ptr_r_meet=1;
617             }
618              
619 0         0 $ptr=pop( @ptrs_r );
620             }
621             }
622              
623             # handle inversion
624 140         218 $port_meet = $port_meet ^ $self->{port_invert};
625 140         194 $protocol_meet = $protocol_meet ^ $self->{protocol_invert};
626 140         191 $cidr_meet = $cidr_meet ^ $self->{cidr_invert};
627 140         192 $state_meet = $state_meet ^ $self->{state_invert};
628 140         185 $ptr_require = $ptr_require ^ $self->{ptr_invert};
629 140         190 $ptr_r_require = $ptr_r_require ^ $self->{ptr_r_invert};
630              
631             # if these are all good, add them
632 140 50 100     786 if (
      100        
      100        
      66        
      66        
633             $port_meet && $protocol_meet && $cidr_meet && $state_meet &&
634             $ptr_meet && $ptr_r_meet
635             ){
636 81         476 push( @found, {
637             'foreign_port'=>$foreign_port,
638             'foreign_host'=>$foreign_host,
639             'local_port'=>$local_port,
640             'local_host'=>$local_host,
641             'sendq'=>$sendq,
642             'recvq'=>$recvq,
643             'proto'=>$protocol,
644             'state'=>$state,
645             'local_pp'=>$local_pp,
646             'foreign_pp'=>$foreign_pp,
647             }
648             );
649             }
650              
651             }
652              
653 160         348 $res_int++;
654             }
655              
656 20         182 return @found;
657             }
658              
659             =head2 set_cidrs
660              
661             This sets the list of CIDRs to search for
662             in either the local or remote field.
663              
664             One value is taken and that is a array ref of CIDRs.
665              
666             Validating in is done by Net::CIDR::cidrvalidate.
667              
668             If you are using this, you will want to use -n with netstat
669             as this module currently does not resolve names.
670              
671             # set the desired CIDRs to the contents of @CIDRs
672             $search->set_cidrs( \@CIDRs );
673             if ( $search->error ){
674             warn("Improper CIDR");
675             }
676              
677             # clear any previously set
678             $search->set_cidrs;
679              
680             =cut
681              
682             sub set_cidrs{
683 8     8 1 2805 my $self=$_[0];
684 8         16 my @cidrs;
685 8 100       19 if ( defined( $_[1] ) ){
686 6         10 @cidrs=@{ $_[1] };
  6         14  
687             }
688              
689 8 50       26 if( ! $self->errorblank ){
690 0         0 return undef;
691             }
692              
693             #blank it if none is given
694 8 100       79 if ( !defined( $cidrs[0] ) ){
695 2         8 $self->{cidrs}=\@cidrs;
696             }
697              
698             #chueck each one
699 8         12 my $cidr_int=0;
700 8         21 while ( defined( $cidrs[$cidr_int] ) ){
701 8         15 my $cidr=$cidrs[$cidr_int];
702 8 50       23 if ( ! Net::CIDR::cidrvalidate( $cidr ) ){
703 0         0 $self->{error}=1;
704 0         0 $self->{errorString}='"'.$cidr.'" is not a valid CIDR according to Net::CIDR::cidrvalidate';
705 0         0 $self->warn;
706 0         0 return undef;
707              
708             }
709              
710 8         30217 $cidr_int++;
711             }
712              
713 8         22 $self->{cidrs}=\@cidrs;
714              
715 8         24 return 1;
716             }
717              
718             =head2 set_cidrs_invert
719              
720             This sets if the CIDRs search should be inverted or not.
721              
722             One value is taken and that is a boolean.
723              
724             # if it does not match, hit on it
725             $search->set_cidrs_invert(1);
726              
727             # only hit on matches, the default
728             $search->set_cidrs_invert; # or...
729             $search->set_cidrs_invert(0);
730              
731             =cut
732              
733             sub set_cidrs_invert{
734 0     0 1 0 my $self=$_[0];
735 0         0 my $bool=$_[1];
736              
737 0 0       0 if( ! $self->errorblank ){
738 0         0 return undef;
739             }
740              
741 0 0       0 if ( $bool ){
742 0         0 $self->{cidr_invert}=1;
743             }else{
744 0         0 $self->{cidr_invert}=0;
745             }
746              
747 0         0 return 1;
748             }
749              
750             =head2 set_ports
751              
752             This sets the ports to search for in either
753             the local or remote field.
754              
755             One value is taken and that is a array ref of ports.
756              
757             The ports can be either numeric or by name.
758              
759             # Set the desired ports to the contents of @ports.
760             $search->set_ports( \@ports );
761             if ( $search->error ){
762             warn("Bad value in ports array");
763             }
764              
765             # removes any previous selections
766             $search->set_ports;
767              
768             =cut
769              
770             sub set_ports{
771 4     4 1 917 my $self=$_[0];
772 4         7 my @ports;
773 4 100       14 if ( defined( $_[1] ) ){
774 3         7 @ports=@{ $_[1] };
  3         7  
775             }
776              
777 4 50       13 if( ! $self->errorblank ){
778 0         0 return undef;
779             }
780              
781 4 100       37 if ( !defined( $ports[0] ) ){
782 1         3 $self->{ports}={};
783             }
784              
785 4         10 my $port=pop( @ports );
786 4         7 my %lookup_hash;
787 4         40 while( defined( $port ) ){
788 4         10 my $port_number=$port;
789 4 100       29 if ( $port !~ /^\d+$/ ){
790             # Find the first matching port number.
791             # Does not care what protocol comes up.
792 2         262 $port_number=(getservbyname( $port , '' ))[2];
793              
794             # If it is not defined, we did not find a matching
795             # service record for the requested port name.
796 2 50       12 if ( !defined( $port_number ) ){
797 0         0 $self->{error}=2;
798 0         0 $self->{errorString}='"'.$port.'" was not found as a known service';
799 0         0 $self->warn;
800 0         0 return undef;
801             }
802             }
803              
804 4         13 $lookup_hash{$port_number}=1;
805              
806 4         12 $port=pop( @ports );
807             }
808              
809             #save this for later
810 4         12 $self->{ports}=\%lookup_hash;
811              
812 4         12 return 1;
813             }
814              
815             =head2 set_ports_invert
816              
817             This sets if the ports search should be inverted or not.
818              
819             One value is taken and that is a boolean.
820              
821             # if it does not match, hit on it
822             $search->set_port_invert(1);
823              
824             # only hit on matches, the default
825             $search->set_port_invert; # or...
826             $search->set_port_invert(0);
827              
828             =cut
829              
830             sub set_ports_invert{
831 0     0 1 0 my $self=$_[0];
832 0         0 my $bool=$_[1];
833              
834 0 0       0 if( ! $self->errorblank ){
835 0         0 return undef;
836             }
837              
838 0 0       0 if ( $bool ){
839 0         0 $self->{port_invert}=1;
840             }else{
841 0         0 $self->{port_invert}=0;
842             }
843              
844 0         0 return 1;
845             }
846              
847             =head2 set_protocols
848              
849             Sets the list of desired protocols to match.
850              
851             One value is taken and that is a array.
852              
853             If this is undef, then previous settings will be cleared.
854              
855             Lacking of exhaustive list of possible values for the
856             OSes supported by Parse::Netstat, no santity checking
857             is done.
858              
859             Starting and trailing white space is removed.
860              
861             # Set the desired ports to the contents of @protocols.
862             $search->set_protocols( \@protocols );
863              
864             # removes any previous selections
865             $search->set_protocols;
866              
867             =cut
868              
869             sub set_protocols{
870 3     3 1 610 my $self=$_[0];
871 3         5 my @protocols;
872 3 100       8 if ( defined( $_[1] ) ){
873 2         5 @protocols=@{ $_[1] };
  2         5  
874             }
875              
876 3 50       8 if( ! $self->errorblank ){
877 0         0 return undef;
878             }
879              
880 3 100       34 if ( !defined( $protocols[0] ) ){
881 1         4 $self->{protocols}={};
882             }
883              
884 3         5 my %lookup_hash;
885 3         7 my $protocol=pop( @protocols );
886 3         7 while( defined( $protocol ) ){
887 2         11 $protocol=~s/^[\ \t]*//;
888 2         6 $protocol=~s/^[\ \t]*//;
889              
890             #create a LCed version of the protocol name
891 2         7 $lookup_hash{ lc( $protocol ) }=1;
892              
893 2         7 $protocol=pop( @protocols );
894             }
895              
896             #save it for usage later
897 3         7 $self->{protocols}=\%lookup_hash;
898              
899 3         8 return 1;
900             }
901              
902             =head2 set_protocols_invert
903              
904             This sets if the protocols search should be inverted or not.
905              
906             One value is taken and that is a boolean.
907              
908             # if it does not match, hit on it
909             $search->set_port_invert(1);
910              
911             # only hit on matches, the default
912             $search->set_protocol_invert; # or...
913             $search->set_protocol_invert(0);
914              
915             =cut
916              
917             sub set_protocols_invert{
918 0     0 1 0 my $self=$_[0];
919 0         0 my $bool=$_[1];
920              
921 0 0       0 if( ! $self->errorblank ){
922 0         0 return undef;
923             }
924              
925 0 0       0 if ( $bool ){
926 0         0 $self->{protocol_invert}=1;
927             }else{
928 0         0 $self->{protocol_invert}=0;
929             }
930              
931 0         0 return 1;
932             }
933              
934             =head2 set_ptrs
935              
936             This sets a list of PTRs to search for.
937              
938             One value is taken and that is a array.
939              
940             If this is undef, then previous settings will be cleared.
941              
942             White space, [\ \t], at the start or end of each
943             item is removed. It is then converted to lowercase
944             and saved for later lookup.
945              
946             # Set the desired PTRs to the contents of @ptrs.
947             $search->set_ptrs( \@ptrs );
948              
949             # removes any previous selections
950             $search->set_ptrs;
951              
952             =cut
953              
954             sub set_ptrs{
955 0     0 1 0 my $self=$_[0];
956 0         0 my @ptrs;
957 0 0       0 if ( defined( $_[1] ) ){
958 0         0 @ptrs=@{ $_[1] };
  0         0  
959             }
960              
961 0 0       0 if( ! $self->errorblank ){
962 0         0 return undef;
963             }
964              
965 0 0       0 if ( !defined( $ptrs[0] ) ){
966 0         0 $self->{ptrs}={};
967             }
968              
969             # convert each one to a array
970 0         0 my %lookup_hash;
971 0         0 my $ptr=pop( @ptrs );
972 0         0 while( defined( $ptr ) ){
973 0         0 $ptr=~s/^[\ \t]*//;
974 0         0 $ptr=~s/^[\ \t]*//;
975              
976             #create a LCed version of the ptr name
977 0         0 $lookup_hash{ lc( $ptr) }=1;
978              
979 0         0 $ptr=pop( @ptrs );
980             }
981              
982             # save it for later use
983 0         0 $self->{ptrs}=\%lookup_hash;
984              
985 0         0 return 1;
986             }
987              
988             =head2 set_ptrs_invert
989              
990             This sets if the PTRs search should be inverted or not.
991              
992             One value is taken and that is a boolean.
993              
994             # if it does not match, hit on it
995             $search->set_ptrs_invert(1);
996              
997             # only hit on match, the default
998             $search->set_ptrs_invert; # or...
999             $search->set_ptrs_invert(0);
1000              
1001             =cut
1002              
1003             sub set_ptrs_invert{
1004 0     0 1 0 my $self=$_[0];
1005 0         0 my $bool=$_[1];
1006              
1007 0 0       0 if( ! $self->errorblank ){
1008 0         0 return undef;
1009             }
1010              
1011 0 0       0 if ( $bool ){
1012 0         0 $self->{ptr_invert}=1;
1013             }else{
1014 0         0 $self->{ptr_invert}=0;
1015             }
1016              
1017 0         0 return 1;
1018             }
1019              
1020             =head2 set_ptrs_r
1021              
1022             This sets a list of PTRs to search for via regexp
1023              
1024             One value is taken and that is a array.
1025              
1026             If this is undef, then previous settings will be cleared.
1027              
1028             # Set the desired PTRs regexps to the contents of @ptrs.
1029             $search->set_ptrs_r( \@ptrs );
1030              
1031             # removes any previous selections
1032             $search->set_ptrs;
1033              
1034             =cut
1035              
1036             sub set_ptrs_r{
1037 0     0 1 0 my $self=$_[0];
1038 0         0 my @regexps;
1039 0 0       0 if ( defined( $_[1] ) ){
1040 0         0 @regexps=@{ $_[1] };
  0         0  
1041             }
1042              
1043 0 0       0 if( ! $self->errorblank ){
1044 0         0 return undef;
1045             }
1046              
1047 0         0 $self->{ptrs_r}=\@regexps;
1048              
1049 0         0 return 1;
1050             }
1051              
1052             =head2 set_ptrs_invert
1053              
1054             This sets if the regexp PTRs search should be inverted or not.
1055              
1056             One value is taken and that is a boolean.
1057              
1058             # if it does not match, hit on it
1059             $search->set_ptrs_r_invert(1);
1060              
1061             # only hit on match, the default
1062             $search->set_ptrs_r_invert; # or...
1063             $search->set_ptrs_r_invert(0);
1064              
1065             =cut
1066              
1067             sub set_ptrs_r_invert{
1068 0     0 0 0 my $self=$_[0];
1069 0         0 my $bool=$_[1];
1070              
1071 0 0       0 if( ! $self->errorblank ){
1072 0         0 return undef;
1073             }
1074              
1075 0 0       0 if ( $bool ){
1076 0         0 $self->{ptr_r_invert}=1;
1077             }else{
1078 0         0 $self->{ptr_r_invert}=0;
1079             }
1080              
1081 0         0 return 1;
1082             }
1083              
1084             =head2 set_states
1085              
1086             Sets the list of desired states to match.
1087              
1088             One value is taken and that is a array.
1089              
1090             If this is undef, then previous settings will be cleared.
1091              
1092             Lacking of exhaustive list of possible values for the
1093             OSes supported by Parse::Netstat, no santity checking
1094             is done.
1095              
1096             Starting and trailing white space is removed.
1097              
1098             # Set the desired ports to the contents of @protocols.
1099             $search->set_protocols( \@protocols );
1100             if ( $search->error ){
1101             warn("Bad value in ports array");
1102             }
1103              
1104             # removes any previous selections
1105             $search->set_protocols;
1106              
1107             =cut
1108              
1109             sub set_states{
1110 8     8 1 1871 my $self=$_[0];
1111 8         12 my @states;
1112 8 100       20 if ( defined( $_[1] ) ){
1113 4         7 @states=@{ $_[1] };
  4         9  
1114             }
1115              
1116 8 50       25 if( ! $self->errorblank ){
1117 0         0 return undef;
1118             }
1119              
1120 8 100       75 if ( !defined( $states[0] ) ){
1121 4         11 $self->{staes}={};
1122             }
1123              
1124 8         20 my %lookup_hash;
1125 8         15 my $state=pop(@states);
1126 8         19 while ( defined( $state ) ){
1127 4         19 $state=~s/^[\ \t]*//;
1128 4         11 $state=~s/^[\ \t]*//;
1129              
1130             #create a LCed version of the protocol name
1131 4         14 $lookup_hash{ lc( $state ) }=1;
1132              
1133 4         10 $state=pop(@states);
1134             }
1135              
1136             #save it for usage later
1137 8         22 $self->{states}=\%lookup_hash;
1138              
1139 8         19 return 1;
1140             }
1141              
1142             =head2 set_ptrs_invert
1143              
1144             This sets if the state search should be inverted or not.
1145              
1146             One value is taken and that is a boolean.
1147              
1148             # if it does not match, hit on it
1149             $search->set_state_invert(1);
1150              
1151             # only hit on match, the default
1152             $search->set_state_invert; # or...
1153             $search->set_state_invert(0);
1154              
1155             =cut
1156              
1157             sub set_state_invert{
1158 0     0 0   my $self=$_[0];
1159 0           my $bool=$_[1];
1160              
1161 0 0         if( ! $self->errorblank ){
1162 0           return undef;
1163             }
1164              
1165 0 0         if ( $bool ){
1166 0           $self->{state_invert}=1;
1167             }else{
1168 0           $self->{state_invert}=0;
1169             }
1170              
1171 0           return 1;
1172             }
1173              
1174             =head1 ERROR CODES / FLAGS
1175              
1176             Error handling is provided by L.
1177              
1178             =head2 1 / badCIDR
1179              
1180             Invalid CIDR passed.
1181              
1182             Validation is done by Net::CIDR::cidrvalidate.
1183              
1184             =head2 2 / unknownService
1185              
1186             Could not look up the port number for the specified service.
1187              
1188             =head2 3 / badResults
1189              
1190             The passed array does not appear to be properly formatted.
1191              
1192             =head1 AUTHOR
1193              
1194             Zane C. Bowers-Hadley, C<< >>
1195              
1196             =head1 BUGS
1197              
1198             Please report any bugs or feature requests to C, or through
1199             the web interface at L. I will be notified, and then you'll
1200             automatically be notified of progress on your bug as I make changes.
1201              
1202              
1203              
1204              
1205             =head1 SUPPORT
1206              
1207             You can find documentation for this module with the perldoc command.
1208              
1209             perldoc Parse::Netstat::Search
1210              
1211              
1212             You can also look for information at:
1213              
1214             =over 4
1215              
1216             =item * RT: CPAN's request tracker (report bugs here)
1217              
1218             L
1219              
1220             =item * AnnoCPAN: Annotated CPAN documentation
1221              
1222             L
1223              
1224             =item * CPAN Ratings
1225              
1226             L
1227              
1228             =item * Search CPAN
1229              
1230             L
1231              
1232             =item * Code Repo
1233              
1234             L
1235              
1236             =back
1237              
1238              
1239             =head1 ACKNOWLEDGEMENTS
1240              
1241              
1242             =head1 LICENSE AND COPYRIGHT
1243              
1244             Copyright 2019 Zane C. Bowers-Hadley.
1245              
1246             This program is free software; you can redistribute it and/or modify it
1247             under the terms of the the Artistic License (2.0). You may obtain a
1248             copy of the full license at:
1249              
1250             L
1251              
1252             Any use, modification, and distribution of the Standard or Modified
1253             Versions is governed by this Artistic License. By using, modifying or
1254             distributing the Package, you accept this license. Do not use, modify,
1255             or distribute the Package, if you do not accept this license.
1256              
1257             If your Modified Version has been derived from a Modified Version made
1258             by someone other than you, you are nevertheless required to ensure that
1259             your Modified Version complies with the requirements of this license.
1260              
1261             This license does not grant you the right to use any trademark, service
1262             mark, tradename, or logo of the Copyright Holder.
1263              
1264             This license includes the non-exclusive, worldwide, free-of-charge
1265             patent license to make, have made, use, offer to sell, sell, import and
1266             otherwise transfer the Package with respect to any patent claims
1267             licensable by the Copyright Holder that are necessarily infringed by the
1268             Package. If you institute patent litigation (including a cross-claim or
1269             counterclaim) against any party alleging that the Package constitutes
1270             direct or contributory patent infringement, then this Artistic License
1271             to you shall terminate on the date that such litigation is filed.
1272              
1273             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
1274             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
1275             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
1276             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
1277             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
1278             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
1279             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
1280             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1281              
1282              
1283             =cut
1284              
1285             1; # End of Parse::Netstat::Search