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