File Coverage

blib/lib/Parse/Netstat/solaris.pm
Criterion Covered Total %
statement 46 46 100.0
branch 31 36 86.1
condition 12 15 80.0
subroutine 5 5 100.0
pod 1 1 100.0
total 95 103 92.2


line stmt bran cond sub pod time code
1             package Parse::Netstat::solaris;
2              
3 1     1   18 use 5.010001;
  1         3  
4 1     1   6 use strict;
  1         3  
  1         21  
5 1     1   5 use warnings;
  1         2  
  1         27  
6              
7 1     1   6 use Exporter 'import';
  1         2  
  1         830  
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 Solaris "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.
26              
27             _
28             args => {
29             output => {
30             summary => 'Output of netstat command',
31             schema => 'str*',
32             req => 1,
33             },
34             tcp => {
35             summary => 'Whether to parse TCP (and TCP6) connections',
36             schema => [bool => default => 1],
37             },
38             udp => {
39             summary => 'Whether to parse UDP (and UDP6) connections',
40             schema => [bool => default => 1],
41             },
42             unix => {
43             summary => 'Whether to parse Unix socket connections',
44             schema => [bool => default => 1],
45             },
46             },
47             };
48             sub parse_netstat {
49 4     4 1 12 my %args = @_;
50 4 50       12 my $output = $args{output} or return [400, "Please specify output"];
51 4   50     9 my $tcp = $args{tcp} // 1;
52 4   50     9 my $udp = $args{udp} // 1;
53 4   50     9 my $unix = $args{unix} // 1;
54              
55 4         6 my $proto = '';
56 4         7 my @conns;
57 4         4 my $i = 0;
58 4         78 for my $line (split /^/, $output) {
59 124         166 $i++;
60 124         151 my %k;
61 124 100 100     652 if ($line =~ /^UDP: IPv([46])/) {
    100 100        
    100 100        
    100          
    100          
    100          
62 4         15 $proto = "udp$1";
63             } elsif ($line =~ /^TCP: IPv([46])/) {
64 4         10 $proto = "tcp$1";
65             } elsif ($line =~ /^Active UNIX domain sockets/) {
66 4         8 $proto = "unix";
67             } elsif ($proto =~ /udp/ && $udp) {
68             #UDP: IPv4
69             # Local Address Remote Address State
70             #-------------------- -------------------- ----------
71             #8.8.17.4.15934 8.8.7.7.53 Connected
72 18 100       56 $line =~ /^\s*$/ and next; # blank line
73 15 100       36 $line =~ /^\s+/ and next; # header
74 12 100       35 $line =~ /^[- ]+$/ and next; # separator
75 9 50       63 $line =~ m!^(?P\S+?)\.(?P\w+)\s+
76             (?P\S+?)\.(?P\w+|\*)\s+
77             (?P\S+)
78             \s*$!x
79             or return [400, "Can't parse udp line (#$i): $line"];
80 9         124 %k = %+;
81 9         34 $k{proto} = $proto;
82             } elsif ($proto =~ /tcp/ && $tcp) {
83             #TCP: IPv4
84             # Local Address Remote Address Swind Send-Q Rwind Recv-Q State
85             #-------------------- -------------------- ----- ------ ----- ------ -----------
86             #8.8.17.4.1337 8.8.213.120.65472 262140 0 1049920 0 ESTABLISHED
87 27 100       79 $line =~ /^\s*$/ and next; # blank line
88 24 100       51 $line =~ /^\s+/ and next; # header
89 21 100       58 $line =~ /^[- ]+$/ and next; # separator
90 18 50       124 $line =~ m!^(?P\S+?)\.(?P\w+)\s+
91             (?P\S+?)\.(?P\w+|\*)\s+
92             (?P\d+) \s+
93             (?P\d+) \s+
94             (?P\d+) \s+
95             (?P\d+) \s+
96             (?P\S+)
97             \s*$!x
98             or return [400, "Can't parse tcp line (#$i): $line"];
99 18         343 %k = %+;
100 18         78 $k{proto} = $proto;
101             } elsif ($proto eq 'unix' && $unix) {
102             #Active UNIX domain sockets
103             #Address Type Vnode Conn Local Addr Remote Addr
104             #30258256428 stream-ord 00000000 00000000
105 36 50       106 $line =~ /^\s*$/ and next; # blank line
106 36 100       70 $line =~ /^Address\s/ and next; # header
107             #$line =~ /^[- ]+$/ and next; # separator
108 33 50       146 $line =~ m!^(?P
[0-9a-f]+)\s+
109             (?P\S+)\s+
110             (?P[0-9a-f]+)\s+
111             (?P[0-9a-f]+)\s+
112             (?:
113             (?P\S+)\s+
114             (?:
115             (?P\S+)\s+
116             )?
117             )?
118             \s*$!x
119             or return [400, "Can't parse unix line (#$i): $line"];
120 33         393 %k = %+;
121 33         109 $k{proto} = $proto;
122             } else {
123             # XXX error? because there are no other lines
124 31         54 next;
125             }
126 72         165 push @conns, \%k;
127             }
128              
129 4         36 [200, "OK", {active_conns => \@conns}];
130             }
131              
132             1;
133             # ABSTRACT: Parse the output of Solaris "netstat" command
134              
135             __END__