File Coverage

blib/lib/Parse/Netstat/Colorizer.pm
Criterion Covered Total %
statement 78 124 62.9
branch 17 48 35.4
condition 5 12 41.6
subroutine 18 23 78.2
pod 10 13 76.9
total 128 220 58.1


line stmt bran cond sub pod time code
1             package Parse::Netstat::Colorizer;
2              
3 2     2   133248 use 5.006;
  2         16  
4 2     2   14 use strict;
  2         3  
  2         43  
5 2     2   8 use warnings;
  2         27  
  2         74  
6 2     2   12 use base 'Error::Helper';
  2         4  
  2         972  
7 2     2   2634 use Parse::Netstat;
  2         1198  
  2         94  
8 2     2   1031 use Parse::Netstat::Search;
  2         16499  
  2         74  
9 2     2   987 use Parse::Netstat::Search::Sort;
  2         124568  
  2         86  
10 2     2   1326 use Term::ANSIColor;
  2         16985  
  2         133  
11 2     2   1340 use Text::Table;
  2         18221  
  2         62  
12 2     2   1120 use Net::DNS;
  2         194425  
  2         4230  
13              
14             =head1 NAME
15              
16             Parse::Netstat::Colorizer - Searches and colorizes the output from Parse::Netstat
17              
18             =head1 VERSION
19              
20             Version 0.1.0
21              
22             =cut
23              
24             our $VERSION = '0.1.0';
25              
26              
27             =head1 SYNOPSIS
28              
29             use Parse::Netstat;
30             use Parse::Netstat::Colorizer;
31            
32             my $pnc = Parse::Netstat::Colorizer->new();
33            
34             # don't even bother parsing unix sockets... Parse::Netstat::Search, Parse::Netsat::Search::Sort;
35             # and this only currently handle non-unix network connections
36             my $res = parse_netstat(output => join("", `netstat -n`), tcp=>1, udp=>1, unix=>0, flavor=>$^O);
37            
38             # search only for connections to/from specific networks
39             my @networks=('192.168.0.0/24', '10.10.10.0/24');
40             my $search=$pnc->get_search;
41             $search->set_cidrs( \@networks );
42             if ( $search->error ){
43             warn( 'One of the passed CIDRs is bad' );
44             }
45            
46             # set it to host local sort
47             my $sorter=$pnc->get_sort;
48             $sorter->set_sort( 'host_l' );
49              
50             Sorting and searching is handled via L and
51             L. Their objects for tweaking can be
52             fetched via get_sort and get_search.
53              
54             L is used for resolving hostnames.
55              
56             =head1 METHODS
57              
58             =head2 new
59              
60             Creates a new object. This will never error.
61              
62             my $pnc->new;
63              
64             =cut
65              
66             sub new {
67 1     1 1 639 my $self={
68             perror=>undef,
69             error=>undef,
70             errorString=>'',
71             errorExtra=>{
72             1 => 'badResults',
73             2 => 'searchErrored',
74             3 => 'sortErrored',
75             },
76             os=>$^O,
77             invert=>undef,
78             port_resolve=>1,
79             search=>Parse::Netstat::Search->new,
80             sort=>Parse::Netstat::Search::Sort->new,
81             resolver=>Net::DNS::Resolver->new,
82             use_ptr=>1,
83             no_color=>0,
84             };
85 1         506 bless $self;
86              
87 1         19 return $self;
88             }
89              
90             =head1 colorize
91              
92             This runs the configured search and colorizes
93             the output.
94              
95             One value is taken and that is the array ref returned
96             by Parse::Netstat.
97              
98             my $colorized=$pnc->colorize($res);
99             if ( $pnc->error ){
100             warn( 'Either $res is not valid post a basic check or sorting failed.
101             }
102              
103             =cut
104              
105             sub colorize{
106 1     1 0 277 my $self=$_[0];
107 1         2 my $res=$_[1];
108              
109 1 50       3 if( ! $self->errorblank ){
110 0         0 return undef;
111             }
112              
113             #make sure what ever we are passed is sane and very likely a return from Parse::Netdata
114 1 50 33     30 if (
      33        
115             ( ref( $res ) ne 'ARRAY' ) ||
116             ( ! defined( $res->[2] ) ) ||
117             ( ! defined( $res->[2]->{active_conns} ) )
118             ){
119 0         0 $self->{error}=1;
120 0         0 $self->{errorString}='$res->[2]->{active_conns} not defiend. Does not appear to be a Parse::Netstat return';
121 0         0 $self->warn;
122 0         0 return undef;
123             }
124              
125 1         8 my @found=$self->{search}->search( $res );
126              
127             # sort it all
128 1         432 @found=$self->{sort}->sort( \@found );
129 1 50       55410 if ( $self->{sort}->error ){
130 0         0 $self->{error}=3;
131 0         0 $self->{errorString}='Sort failed';
132 0         0 $self->warn;
133 0         0 return undef;
134             }
135              
136             # invert if needed
137 1 50       12 if ( $self->{invert} ){
138 0         0 @found=reverse(@found);
139             }
140              
141             # Holds colorized lines for the table.
142              
143 1         3 my @colored;
144              
145 1 50       6 if ( $self->{no_color} ){
146 0         0 push( @colored, ([
147             color('underline').'Proto'.color('reset'),
148             color('underline').'SendQ'.color('reset'),
149             color('underline').'RecvQ'.color('reset'),
150             color('underline').'Local Host'.color('reset'),
151             color('underline').'Port'.color('reset'),
152             color('underline').'Remote Host'.color('reset'),
153             color('underline').'Port'.color('reset'),
154             color('underline').'State'.color('reset'),
155             ])
156             );
157             }else{
158 1         18 push( @colored, ([
159             color('underline white').'Proto'.color('reset'),
160             color('underline white').'SendQ'.color('reset'),
161             color('underline white').'RecvQ'.color('reset'),
162             color('underline white').'Local Host'.color('reset'),
163             color('underline white').'Port'.color('reset'),
164             color('underline white').'Remote Host'.color('reset'),
165             color('underline white').'Port'.color('reset'),
166             color('underline white').'State'.color('reset'),
167             ])
168             );
169             }
170              
171             # process each connection
172 1         302 my $conn=pop(@found);
173 1         6 while ( defined( $conn->{local_port} ) ){
174 7         15 my $port_l=$conn->{local_port};
175 7         15 my $port_f=$conn->{foreign_port};
176              
177             #resolve port numbers if needed
178 7 50       19 if ( $self->{port_resolve} ){
179 0         0 my $port_l_search=getservbyport( $port_l, '' );
180 0 0       0 if ( defined( $port_l_search ) ){
181 0         0 $port_l=$port_l_search;
182             }
183              
184             # make sure we have have a actual number
185             # UDP may not have one of these listed
186 0 0       0 if ( $port_f =~ /^\d+$/ ){
187 0         0 my $port_f_search=getservbyport( $port_f, '' );
188 0 0       0 if ( defined( $port_f_search ) ){
189 0         0 $port_f=$port_f_search;
190             }
191             }
192             }
193              
194 7 50       19 if ( $self->{use_ptr} ){
195 7         41 my $answer_f=$self->{resolver}->search( $conn->{foreign_host} );
196 7         43757 my $answer_l=$self->{resolver}->search( $conn->{local_host} );
197              
198 7 50 33     217655 if ( defined( $answer_f->{answer}[0] ) &&
199             ( ref( $answer_f->{answer}[0] ) eq 'Net::DNS::RR::PTR' )
200             ){
201 0         0 $conn->{foreign_host}=lc($answer_f->{answer}[0]->ptrdname);
202             }
203              
204 7 100 66     38 if ( defined( $answer_l->{answer}[0] ) &&
205             ( ref( $answer_l->{answer}[0] ) eq 'Net::DNS::RR::PTR' )
206             ){
207 1         4 $conn->{local_host}=lc($answer_l->{answer}[0]->ptrdname);
208             }
209             }
210              
211 7 50       82 if ( $self->{no_color} ){
212             push( @colored, ([
213             $conn->{proto},
214             $conn->{sendq},
215             $conn->{recvq},
216             $conn->{local_host},
217             $port_l,
218             $conn->{foreign_host},
219             $port_f,
220             $conn->{state},
221 0         0 ])
222             );
223             }else{
224             push( @colored, ([
225             color('BRIGHT_YELLOW').$conn->{proto}.color('reset'),
226             color('BRIGHT_CYAN').$conn->{sendq}.color('reset'),
227             color('BRIGHT_RED').$conn->{recvq}.color('reset'),
228             color('BRIGHT_GREEN').$conn->{local_host}.color('reset'),
229             color('GREEN').$port_l.color('reset'),
230             color('BRIGHT_MAGENTA').$conn->{foreign_host}.color('reset'),
231             color('MAGENTA').$port_f.color('reset'),
232 7         28 color('BRIGHT_BLUE').$conn->{state}.color('reset'),
233             ])
234             );
235             }
236              
237 7         1779 $conn=pop(@found);
238             }
239              
240 1         21 my $tb = Text::Table->new;
241              
242 1         180 return $tb->load( @colored );
243             }
244              
245             =head2 get_invert
246              
247             This returns a boolean as to if the return
248             from the sort is inverted or not.
249              
250             my $invert=$pnc->get_invert;
251              
252             =cut
253              
254             sub get_invert{
255 3     3 1 295 my $self=$_[0];
256              
257 3 50       8 if( ! $self->errorblank ){
258 0         0 return undef;
259             }
260              
261 3         25 return $self->{invert};
262             }
263              
264             =head2 get_no_color
265              
266             This returns a boolean as to if the return
267             is to be colorized or not.
268              
269             my $no_color=$pnc->get_no_color;
270              
271             =cut
272              
273             sub get_no_color{
274 3     3 1 303 my $self=$_[0];
275              
276 3 50       7 if( ! $self->errorblank ){
277 0         0 return undef;
278             }
279              
280 3         29 return $self->{no_color};
281             }
282              
283             =head2 get_port_resolve
284              
285             This gets the port_resolve value, which is if it should try to resolve
286             port names or not.
287              
288             The returned value is a boolean and defaults to 1.
289              
290             my $port_resolve=$pnc->get_port_resolve;
291              
292             =cut
293              
294             sub get_port_resolve{
295 3     3 1 13 my $self=$_[0];
296              
297 3 50       13 if( ! $self->errorblank ){
298 0         0 return undef;
299             }
300              
301 3         41 return $self->{port_resolve};
302             }
303              
304             =head2 get_ptr_resolve
305              
306             This gets the current setting for if it should resolve
307             IPs to PTRs or not.
308              
309             The returned value is a boolean and defaults to 1.
310              
311             my $use_ptr=$pnc->get_ptr_resolve;
312              
313             =cut
314              
315             sub get_ptr_resolve{
316 0     0 1 0 my $self=$_[0];
317              
318 0 0       0 if( ! $self->errorblank ){
319 0         0 return undef;
320             }
321              
322 0         0 return $self->{use_ptr};
323             }
324              
325             =head2 get_resolver
326              
327             This returns the L object used
328             for resolving IPs to PTRs.
329              
330             my $resolver=$pnc->get_resolver;
331              
332             =cut
333              
334             sub get_resolver{
335 0     0 1 0 my $self=$_[0];
336              
337 0 0       0 if( ! $self->errorblank ){
338 0         0 return undef;
339             }
340              
341 0         0 return $self->{resolver};
342             }
343              
344             =head1 get_search
345              
346             This returns the Parse::Netstat::Search object.
347              
348             my $search=$pnc->get_search;
349              
350             =cut
351              
352             sub get_search{
353 0     0 0 0 my $self=$_[0];
354              
355 0 0       0 if( ! $self->errorblank ){
356 0         0 return undef;
357             }
358              
359 0         0 return $self->{search};
360             }
361              
362             =head1 get_sort
363              
364             This returns the Parse::Netstat::Search::Sort object.
365              
366             my $sorter=$pnc->get_sort;
367              
368             # set it to host local sort
369             $sorter->set_sort( 'host_l' );
370              
371             =cut
372              
373             sub get_sort{
374 0     0 0 0 my $self=$_[0];
375              
376 0 0       0 if( ! $self->errorblank ){
377 0         0 return undef;
378             }
379              
380 0         0 return $self->{sort};
381             }
382              
383             =head2 set_invert
384              
385             This sets wether or not it should invert the
386             returned sort or not.
387              
388             # sets it to false, the default
389             $pnc->set_invert;
390              
391             # the results will be inverted
392             $pnc->set_invert(1);
393              
394             =cut
395              
396             sub set_invert{
397 2     2 1 583 my $self=$_[0];
398              
399 2 50       7 if( ! $self->errorblank ){
400 0         0 return undef;
401             }
402              
403 2         18 $self->{invert}=$_[1];
404              
405 2         4 return 1;
406             }
407              
408             =head2 set_no_color
409              
410             This sets wether or not the return from
411             colorized should be colored or not.
412              
413             # sets it to false, the default
414             $pnc->set_no_color;
415              
416             # the results will by the default console
417             # text color when printed
418             $pnc->set_no_color(1);
419              
420             =cut
421              
422             sub set_no_color{
423 2     2 1 591 my $self=$_[0];
424              
425 2 50       7 if( ! $self->errorblank ){
426 0         0 return undef;
427             }
428              
429 2         20 $self->{no_color}=$_[1];
430             }
431              
432             =head2 set_port_resolve
433              
434             This sets wether or not the ports should be resolved or not.
435              
436             One value is taken and that is a perl boolean.
437              
438             # sets it to true, the default
439             $pnc->set_port_resolve(1);
440              
441             # set it false, don't resolve the ports
442             $pnc->set_port_resolve;
443              
444             =cut
445              
446             sub set_port_resolve{
447 2     2 1 608 my $self=$_[0];
448              
449 2 50       6 if( ! $self->errorblank ){
450 0         0 return undef;
451             }
452              
453 2         21 $self->{port_resolve}=$_[1];
454             }
455              
456             =head2 set_ptr_resolve
457              
458             This sets wether or not it will resolve IPs to PTRs.
459              
460             One value is taken and that is a perl boolean.
461              
462             # sets it to true, the default
463             $pnc->set_ptr_resolve(1);
464              
465             # set it false, don't resolve the ports
466             $pnc->set_ptr_resolve;
467              
468             =cut
469              
470             sub set_ptr_resolve{
471 0     0 1   my $self=$_[0];
472              
473 0 0         if( ! $self->errorblank ){
474 0           return undef;
475             }
476              
477 0           $self->{use_ptr}=$_[1];
478             }
479              
480             =head
481              
482             =head1 ERROR CODES / FLAGS
483              
484             Error handling is provided by L.
485              
486             =head2 1 / badResults
487              
488             The passed Parse::Netstat array does not appear to be properly formatted.
489              
490             =head2 2 / searchErrored
491              
492             Parse::Netstat::Search->search errored.
493              
494             =head2 3 / sortErrored
495              
496             Parse::Netsat::Search::Sort errored.
497              
498             =head1 AUTHOR
499              
500             Zane C. Bowers-Hadley, C<< >>
501              
502             =head1 BUGS
503              
504             Please report any bugs or feature requests to C, or through
505             the web interface at L. I will be notified, and then you'll
506             automatically be notified of progress on your bug as I make changes.
507              
508              
509              
510              
511             =head1 SUPPORT
512              
513             You can find documentation for this module with the perldoc command.
514              
515             perldoc Parse::Netstat::Colorizer
516              
517              
518             You can also look for information at:
519              
520             =over 4
521              
522             =item * RT: CPAN's request tracker (report bugs here)
523              
524             L
525              
526             =item * AnnoCPAN: Annotated CPAN documentation
527              
528             L
529              
530             =item * CPAN Ratings
531              
532             L
533              
534             =item * Search CPAN
535              
536             L
537              
538             =item * Code Repo
539              
540             L
541              
542             =back
543              
544              
545             =head1 ACKNOWLEDGEMENTS
546              
547              
548             =head1 LICENSE AND COPYRIGHT
549              
550             Copyright 2019 Zane C. Bowers-Hadley.
551              
552             This program is free software; you can redistribute it and/or modify it
553             under the terms of the the Artistic License (2.0). You may obtain a
554             copy of the full license at:
555              
556             L
557              
558             Any use, modification, and distribution of the Standard or Modified
559             Versions is governed by this Artistic License. By using, modifying or
560             distributing the Package, you accept this license. Do not use, modify,
561             or distribute the Package, if you do not accept this license.
562              
563             If your Modified Version has been derived from a Modified Version made
564             by someone other than you, you are nevertheless required to ensure that
565             your Modified Version complies with the requirements of this license.
566              
567             This license does not grant you the right to use any trademark, service
568             mark, tradename, or logo of the Copyright Holder.
569              
570             This license includes the non-exclusive, worldwide, free-of-charge
571             patent license to make, have made, use, offer to sell, sell, import and
572             otherwise transfer the Package with respect to any patent claims
573             licensable by the Copyright Holder that are necessarily infringed by the
574             Package. If you institute patent litigation (including a cross-claim or
575             counterclaim) against any party alleging that the Package constitutes
576             direct or contributory patent infringement, then this Artistic License
577             to you shall terminate on the date that such litigation is filed.
578              
579             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
580             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
581             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
582             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
583             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
584             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
585             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
586             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
587              
588              
589             =cut
590              
591             1; # End of Parse::Netstat::Colorizer