File Coverage

blib/lib/Parse/Netstat/win32.pm
Criterion Covered Total %
statement 40 40 100.0
branch 14 18 77.7
condition 10 12 83.3
subroutine 5 5 100.0
pod 1 1 100.0
total 70 76 92.1


line stmt bran cond sub pod time code
1             package Parse::Netstat::win32;
2              
3             our $DATE = '2017-02-10'; # DATE
4             our $VERSION = '0.14'; # VERSION
5              
6 1     1   23 use 5.010001;
  1         2  
7 1     1   4 use strict;
  1         1  
  1         18  
8 1     1   4 use warnings;
  1         1  
  1         22  
9              
10 1     1   3 use Exporter;
  1         1  
  1         583  
11             our @ISA = qw(Exporter);
12             our @EXPORT_OK = qw(parse_netstat);
13              
14             our %SPEC;
15              
16             $SPEC{parse_netstat} = {
17             v => 1.1,
18             summary => 'Parse the output of Windows "netstat" command',
19             description => <<'_',
20              
21             Netstat can be called with `-n` (show raw IP addresses and port numbers instead
22             of hostnames or port names) or without. It can be called with `-a` (show all
23             listening and non-listening socket) option or without. And can be called with
24             `-p` (show PID/program names) or without.
25              
26             _
27             args => {
28             output => {
29             summary => 'Output of netstat command',
30             schema => 'str*',
31             req => 1,
32             },
33             tcp => {
34             summary => 'Whether to parse TCP (and TCP6) connections',
35             schema => [bool => default => 1],
36             },
37             udp => {
38             summary => 'Whether to parse UDP (and UDP6) connections',
39             schema => [bool => default => 1],
40             },
41             },
42             };
43             sub parse_netstat {
44 3     3 1 8 my %args = @_;
45 3 50       9 my $output = $args{output} or return [400, "Please specify output"];
46 3   50     7 my $tcp = $args{tcp} // 1;
47 3   50     4 my $udp = $args{udp} // 1;
48              
49 3         3 my @conns;
50 3         1 my $i = 0;
51 3         3 my $cur; # whether we're currently parsing TCP or UDP entry
52             my $k;
53 3         32 for my $line (split /^/, $output) {
54 90         56 $i++;
55 90 100 100     280 if ($line =~ /^\s*TCP\s/ && $tcp) {
    100 100        
    100          
56             # Proto Local Address Foreign Address State PID
57             # TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 988
58             # c:\windows\system32\WS2_32.dll
59             # C:\WINDOWS\system32\RPCRT4.dll
60             # c:\windows\system32\rpcss.dll
61             # C:\WINDOWS\system32\svchost.exe
62             # -- unknown component(s) --
63             # [svchost.exe]
64             #
65 8 50       37 $line =~ m!^\s*(?PTCP6?) \s+
66             (?P\S+?):(?P\w+)\s+
67             (?P\S+?):(?P\w+|\*)\s+
68             (?P\S+) (?: \s+ (?:
69             (?P\d+)
70             ))? \s*$!x
71             or return [400, "Can't parse tcp line (#$i): $line"];
72 8         120 $k = { %+ };
73 8         20 $cur = 'tcp';
74 8         13 for ($k->{proto}) { $_ = lc }
  8         18  
75 8         11 push @conns, $k;
76             } elsif ($line =~ /^\s*UDP\s/ && $udp) {
77             # UDP 0.0.0.0:500 *:* 696
78             # [lsass.exe]
79             #
80             # XXX state not yet parsed
81 4 50       23 $line =~ m!^\s*(?PUDP6?) \s+
82             (?P\S+?):(?P\w+)\s+
83             (?P\S+?):(?P\w+|\*)\s+
84             (?: \s+ (?:
85             (?P\d+)
86             ))? \s*$!x
87             or return [400, "Can't parse udp line (#$i): $line"];
88 4         49 $k = { %+ };
89 4         10 $cur = 'udp';
90 4         4 for ($k->{proto}) { $_ = lc }
  4         7  
91 4         4 push @conns, $k;
92             } elsif ($cur) {
93 52   100     88 $k->{execs} //= [];
94 52 100       72 next if $line =~ /^\s*--/; # e.g. -- unknown component(s) --
95 50 50       77 next if $line =~ /^\s*can not/i; # e.g. Can not obtain ownership information
96 50 100       166 push @{ $k->{execs} }, $1 if $line =~ /^\s*(\S.*?)\s*$/;
  39         60  
97 50         46 next;
98             } else {
99             # a blank line or headers. ignore.
100             }
101             }
102              
103 3         20 [200, "OK", {active_conns => \@conns}];
104             }
105              
106             1;
107             # ABSTRACT: Parse the output of Windows "netstat" command
108              
109             __END__