1 | #!/usr/bin/perl -w |
---|
2 | #- |
---|
3 | # Copyright (c) 1999 Dag-Erling Coïdan Smørgrav |
---|
4 | # All rights reserved. |
---|
5 | # |
---|
6 | # Redistribution and use in source and binary forms, with or without |
---|
7 | # modification, are permitted provided that the following conditions |
---|
8 | # are met: |
---|
9 | # 1. Redistributions of source code must retain the above copyright |
---|
10 | # notice, this list of conditions and the following disclaimer |
---|
11 | # in this position and unchanged. |
---|
12 | # 2. Redistributions in binary form must reproduce the above copyright |
---|
13 | # notice, this list of conditions and the following disclaimer in the |
---|
14 | # documentation and/or other materials provided with the distribution. |
---|
15 | # 3. The name of the author may not be used to endorse or promote products |
---|
16 | # derived from this software without specific prior written permission. |
---|
17 | # |
---|
18 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
---|
19 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
---|
20 | # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
---|
21 | # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
---|
22 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
---|
23 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
---|
24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
---|
25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
---|
26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
---|
27 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
28 | # |
---|
29 | # $FreeBSD: src/usr.bin/sockstat/sockstat.pl,v 1.16 2002/04/22 13:44:39 des Exp $ |
---|
30 | # |
---|
31 | |
---|
32 | use strict; |
---|
33 | use Getopt::Std; |
---|
34 | |
---|
35 | my %netstat; |
---|
36 | my %fstat; |
---|
37 | my $unknown = [ "?", "?", "?", "?", "?", "?", "?", "?", "?" ]; |
---|
38 | |
---|
39 | my $inet_fmt = "%-8.8s %-8.8s %5.5s %4.4s %-6.6s %-21.21s %-21.21s\n"; |
---|
40 | my $unix_fmt = "%-8.8s %-8.8s %5.5s %4.4s %-6.6s %-43.43s\n"; |
---|
41 | |
---|
42 | my @ranges; |
---|
43 | |
---|
44 | # |
---|
45 | # Parse a port range specification |
---|
46 | # |
---|
47 | sub parse_port_ranges($) { |
---|
48 | my $spec = shift; # Range spec |
---|
49 | |
---|
50 | my $range; # Range |
---|
51 | my ($low, $high); # Low/high ends of range |
---|
52 | |
---|
53 | foreach $range (split(/\s*,\s*/, $spec)) { |
---|
54 | if ($range =~ m/^(\d+)-(\d+)$/) { |
---|
55 | ($low, $high) = ($1, $2); |
---|
56 | } elsif ($range =~ m/^-(\d+)$/) { |
---|
57 | ($low, $high) = (1, $1); |
---|
58 | } elsif ($range =~ m/^(\d+)-$/) { |
---|
59 | ($low, $high) = ($1, 65535); |
---|
60 | } elsif ($range =~ m/^(\d+)$/) { |
---|
61 | $low = $high = $1; |
---|
62 | } else { |
---|
63 | die("invalid range specification: $range\n"); |
---|
64 | } |
---|
65 | if ($low < 0 || $low > 65535 || $high < 0 || $high > 65535) { |
---|
66 | die("valid ports numbers are 1-65535 inclusive\n"); |
---|
67 | } |
---|
68 | if ($low > $high) { |
---|
69 | $low ^= $high; |
---|
70 | $high ^= $low; |
---|
71 | $low ^= $high; |
---|
72 | } |
---|
73 | push(@ranges, [ $low, $high ]); |
---|
74 | } |
---|
75 | } |
---|
76 | |
---|
77 | # |
---|
78 | # Check if a port is in an allowed range |
---|
79 | # |
---|
80 | sub check_range($) { |
---|
81 | my $addr = shift; # Address to check |
---|
82 | |
---|
83 | my $port; # Port number |
---|
84 | my $range; # Range |
---|
85 | |
---|
86 | if (@ranges == 0) { |
---|
87 | return 1; |
---|
88 | } |
---|
89 | |
---|
90 | if ($addr !~ m/\.(\d+)$/) { |
---|
91 | return undef; |
---|
92 | } |
---|
93 | $port = $1; |
---|
94 | |
---|
95 | foreach $range (@ranges) { |
---|
96 | if ($port >= $range->[0] && $port <= $range->[1]) { |
---|
97 | return 1; |
---|
98 | } |
---|
99 | } |
---|
100 | return undef; |
---|
101 | } |
---|
102 | |
---|
103 | # |
---|
104 | # Gather information about sockets |
---|
105 | # |
---|
106 | sub gather() { |
---|
107 | |
---|
108 | local *PIPE; # Pipe |
---|
109 | my $pid; # Child PID |
---|
110 | my $line; # Input line |
---|
111 | my @fields; # Fields |
---|
112 | |
---|
113 | # Netstat |
---|
114 | if (!defined($pid = open(PIPE, "-|"))) { |
---|
115 | die("open(netstat): $!\n"); |
---|
116 | } elsif ($pid == 0) { |
---|
117 | exec("/usr/sbin/netstat", "-Aan"); |
---|
118 | die("exec(netstat): $!\n"); |
---|
119 | } |
---|
120 | while ($line = <PIPE>) { |
---|
121 | next unless ($line =~ m/^ [0-9a-f]{7} /) || ($line =~ m/^[0-9a-f]{16} /); |
---|
122 | chomp($line); |
---|
123 | @fields = split(' ', $line); |
---|
124 | $netstat{$fields[0]} = [ @fields ]; |
---|
125 | } |
---|
126 | close(PIPE) |
---|
127 | or die("close(netstat): $!\n"); |
---|
128 | |
---|
129 | # Fstat |
---|
130 | if (!defined($pid = open(PIPE, "-|"))) { |
---|
131 | die("open(fstat): $!\n"); |
---|
132 | } elsif ($pid == 0) { |
---|
133 | exec("/usr/bin/fstat"); |
---|
134 | die("exec(fstat): $!\n"); |
---|
135 | } |
---|
136 | while ($line = <PIPE>) { |
---|
137 | chomp($line); |
---|
138 | @fields = split(' ', $line); |
---|
139 | next if ($fields[4] eq "-"); |
---|
140 | push(@{$fstat{$fields[4]}}, [ @fields ]); |
---|
141 | } |
---|
142 | close(PIPE) |
---|
143 | or die("close(fstat): $!\n"); |
---|
144 | } |
---|
145 | |
---|
146 | # |
---|
147 | # Replace the last dot in an "address.port" string with a colon |
---|
148 | # |
---|
149 | sub addr($) { |
---|
150 | my $addr = shift; # Address |
---|
151 | |
---|
152 | $addr =~ s/^(.*)\.([^\.]*)$/$1:$2/; |
---|
153 | return $addr; |
---|
154 | } |
---|
155 | |
---|
156 | # |
---|
157 | # Print information about Internet sockets |
---|
158 | # |
---|
159 | sub print_inet($$$) { |
---|
160 | my $af = shift; # Address family |
---|
161 | my $conn = shift || 0; # Show connected sockets |
---|
162 | my $listen = shift || 0; # Show listen sockets |
---|
163 | |
---|
164 | my $fsd; # Fstat data |
---|
165 | my $nsd; # Netstat data |
---|
166 | |
---|
167 | printf($inet_fmt, "USER", "COMMAND", "PID", "FD", |
---|
168 | "PROTO", "LOCAL ADDRESS", "FOREIGN ADDRESS"); |
---|
169 | foreach $fsd (@{$fstat{$af}}) { |
---|
170 | next unless defined($fsd->[7]); |
---|
171 | $nsd = $netstat{$fsd->[7]} || $unknown; |
---|
172 | next unless (check_range($nsd->[4]) || check_range($nsd->[5])); |
---|
173 | next if (!$conn && $nsd->[5] ne '*.*'); |
---|
174 | next if (!$listen && $nsd->[5] eq '*.*'); |
---|
175 | printf($inet_fmt, $fsd->[0], $fsd->[1], $fsd->[2], |
---|
176 | substr($fsd->[3], 0, -1), |
---|
177 | $nsd->[1], addr($nsd->[4]), addr($nsd->[5])); |
---|
178 | } |
---|
179 | print("\n"); |
---|
180 | } |
---|
181 | |
---|
182 | # |
---|
183 | # Print information about Unix domain sockets |
---|
184 | # |
---|
185 | sub print_unix($$) { |
---|
186 | my $conn = shift || 0; # Show connected sockets |
---|
187 | my $listen = shift || 0; # Show listen sockets |
---|
188 | |
---|
189 | my %endpoint; # Mad PCB to process/fd |
---|
190 | my $fsd; # Fstat data |
---|
191 | my $nsd; # Netstat data |
---|
192 | |
---|
193 | foreach $fsd (@{$fstat{"unix"}}) { |
---|
194 | $endpoint{$fsd->[6]} = "$fsd->[1]\[$fsd->[2]\]:" . |
---|
195 | substr($fsd->[3], 0, -1); |
---|
196 | } |
---|
197 | printf($unix_fmt, "USER", "COMMAND", "PID", "FD", "PROTO", "ADDRESS"); |
---|
198 | foreach $fsd (@{$fstat{"unix"}}) { |
---|
199 | next unless defined($fsd->[6]); |
---|
200 | next if (!$conn && defined($fsd->[8])); |
---|
201 | next if (!$listen && !defined($fsd->[8])); |
---|
202 | $nsd = $netstat{$fsd->[6]} || $unknown; |
---|
203 | printf($unix_fmt, $fsd->[0], $fsd->[1], $fsd->[2], |
---|
204 | substr($fsd->[3], 0, -1), $fsd->[5], |
---|
205 | $nsd->[8] || ($fsd->[8] ? $endpoint{$fsd->[8]} : "(none)")); |
---|
206 | } |
---|
207 | print("\n"); |
---|
208 | } |
---|
209 | |
---|
210 | # |
---|
211 | # Print usage message and exit |
---|
212 | # |
---|
213 | sub usage() { |
---|
214 | print(STDERR "usage: sockstat [-46clu] [-p ports]\n"); |
---|
215 | exit(1); |
---|
216 | } |
---|
217 | |
---|
218 | MAIN:{ |
---|
219 | my %opts; # Command-line options |
---|
220 | |
---|
221 | getopts("46clp:u", \%opts) |
---|
222 | or usage(); |
---|
223 | |
---|
224 | gather(); |
---|
225 | |
---|
226 | if (!$opts{'4'} && !$opts{'6'} && !$opts{'u'}) { |
---|
227 | $opts{'4'} = $opts{'6'} = $opts{'u'} = 1; |
---|
228 | } |
---|
229 | if (!$opts{'c'} && !$opts{'l'}) { |
---|
230 | $opts{'c'} = $opts{'l'} = 1; |
---|
231 | } |
---|
232 | if ($opts{'p'}) { |
---|
233 | parse_port_ranges($opts{'p'}); |
---|
234 | } |
---|
235 | if ($opts{'4'}) { |
---|
236 | print_inet("internet", $opts{'c'}, $opts{'l'}); |
---|
237 | } |
---|
238 | if ($opts{'6'}) { |
---|
239 | print_inet("internet6", $opts{'c'}, $opts{'l'}); |
---|
240 | } |
---|
241 | if ($opts{'u'}) { |
---|
242 | print_unix($opts{'c'}, $opts{'l'}); |
---|
243 | } |
---|
244 | |
---|
245 | exit(0); |
---|
246 | } |
---|