03f28475f8
Suggested by: roam
247 lines
6.4 KiB
Perl
247 lines
6.4 KiB
Perl
#!/usr/bin/perl -w
|
|
#-
|
|
# Copyright (c) 1999 Dag-Erling Coïdan Smørgrav
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer
|
|
# in this position and unchanged.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
# 3. The name of the author may not be used to endorse or promote products
|
|
# derived from this software without specific prior written permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
# $FreeBSD$
|
|
#
|
|
|
|
use strict;
|
|
use Getopt::Std;
|
|
|
|
my %netstat;
|
|
my %fstat;
|
|
my $unknown = [ "?", "?", "?", "?", "?", "?", "?", "?", "?" ];
|
|
|
|
my $inet_fmt = "%-8.8s %-8.8s %5.5s %4.4s %-6.6s %-21.21s %-21.21s\n";
|
|
my $unix_fmt = "%-8.8s %-8.8s %5.5s %4.4s %-6.6s %-43.43s\n";
|
|
|
|
my @ranges;
|
|
|
|
#
|
|
# Parse a port range specification
|
|
#
|
|
sub parse_port_ranges($) {
|
|
my $spec = shift; # Range spec
|
|
|
|
my $range; # Range
|
|
my ($low, $high); # Low/high ends of range
|
|
|
|
foreach $range (split(/\s*,\s*/, $spec)) {
|
|
if ($range =~ m/^(\d+)-(\d+)$/) {
|
|
($low, $high) = ($1, $2);
|
|
} elsif ($range =~ m/^-(\d+)$/) {
|
|
($low, $high) = (1, $1);
|
|
} elsif ($range =~ m/^(\d+)-$/) {
|
|
($low, $high) = ($1, 65535);
|
|
} elsif ($range =~ m/^(\d+)$/) {
|
|
$low = $high = $1;
|
|
} else {
|
|
die("invalid range specification: $range\n");
|
|
}
|
|
if ($low < 0 || $low > 65535 || $high < 0 || $high > 65535) {
|
|
die("valid ports numbers are 1-65535 inclusive\n");
|
|
}
|
|
if ($low > $high) {
|
|
$low ^= $high;
|
|
$high ^= $low;
|
|
$low ^= $high;
|
|
}
|
|
push(@ranges, [ $low, $high ]);
|
|
}
|
|
}
|
|
|
|
#
|
|
# Check if a port is in an allowed range
|
|
#
|
|
sub check_range($) {
|
|
my $addr = shift; # Address to check
|
|
|
|
my $port; # Port number
|
|
my $range; # Range
|
|
|
|
if (@ranges == 0) {
|
|
return 1;
|
|
}
|
|
|
|
if ($addr !~ m/\.(\d+)$/) {
|
|
return undef;
|
|
}
|
|
$port = $1;
|
|
|
|
foreach $range (@ranges) {
|
|
if ($port >= $range->[0] && $port <= $range->[1]) {
|
|
return 1;
|
|
}
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
#
|
|
# Gather information about sockets
|
|
#
|
|
sub gather() {
|
|
|
|
local *PIPE; # Pipe
|
|
my $pid; # Child PID
|
|
my $line; # Input line
|
|
my @fields; # Fields
|
|
|
|
# Netstat
|
|
if (!defined($pid = open(PIPE, "-|"))) {
|
|
die("open(netstat): $!\n");
|
|
} elsif ($pid == 0) {
|
|
exec("/usr/bin/netstat", "-AanW");
|
|
die("exec(netstat): $!\n");
|
|
}
|
|
while ($line = <PIPE>) {
|
|
next unless ($line =~ m/^[0-9a-f]{8} /) || ($line =~ m/^[0-9a-f]{16} /);
|
|
chomp($line);
|
|
@fields = split(' ', $line);
|
|
$netstat{$fields[0]} = [ @fields ];
|
|
}
|
|
close(PIPE)
|
|
or die("close(netstat): $!\n");
|
|
|
|
# Fstat
|
|
if (!defined($pid = open(PIPE, "-|"))) {
|
|
die("open(fstat): $!\n");
|
|
} elsif ($pid == 0) {
|
|
exec("/usr/bin/fstat");
|
|
die("exec(fstat): $!\n");
|
|
}
|
|
while ($line = <PIPE>) {
|
|
chomp($line);
|
|
@fields = split(' ', $line);
|
|
next if ($fields[4] eq "-");
|
|
push(@{$fstat{$fields[4]}}, [ @fields ]);
|
|
}
|
|
close(PIPE)
|
|
or die("close(fstat): $!\n");
|
|
}
|
|
|
|
#
|
|
# Replace the last dot in an "address.port" string with a colon
|
|
#
|
|
sub addr($) {
|
|
my $addr = shift; # Address
|
|
|
|
$addr =~ s/^(.*)\.([^\.]*)$/$1:$2/;
|
|
return $addr;
|
|
}
|
|
|
|
#
|
|
# Print information about Internet sockets
|
|
#
|
|
sub print_inet($$$) {
|
|
my $af = shift; # Address family
|
|
my $conn = shift || 0; # Show connected sockets
|
|
my $listen = shift || 0; # Show listen sockets
|
|
|
|
my $fsd; # Fstat data
|
|
my $nsd; # Netstat data
|
|
|
|
printf($inet_fmt, "USER", "COMMAND", "PID", "FD",
|
|
"PROTO", "LOCAL ADDRESS", "FOREIGN ADDRESS");
|
|
foreach $fsd (@{$fstat{$af}}) {
|
|
next unless defined($fsd->[7]);
|
|
$nsd = $netstat{$fsd->[7]} || $unknown;
|
|
next unless (check_range($nsd->[4]) || check_range($nsd->[5]));
|
|
next if (!$conn && $nsd->[5] ne '*.*');
|
|
next if (!$listen && $nsd->[5] eq '*.*');
|
|
printf($inet_fmt, $fsd->[0], $fsd->[1], $fsd->[2],
|
|
substr($fsd->[3], 0, -1),
|
|
$nsd->[1], addr($nsd->[4]), addr($nsd->[5]));
|
|
}
|
|
print("\n");
|
|
}
|
|
|
|
#
|
|
# Print information about Unix domain sockets
|
|
#
|
|
sub print_unix($$) {
|
|
my $conn = shift || 0; # Show connected sockets
|
|
my $listen = shift || 0; # Show listen sockets
|
|
|
|
my %endpoint; # Mad PCB to process/fd
|
|
my $fsd; # Fstat data
|
|
my $nsd; # Netstat data
|
|
|
|
foreach $fsd (@{$fstat{"local"}}) {
|
|
$endpoint{$fsd->[6]} = "$fsd->[1]\[$fsd->[2]\]:" .
|
|
substr($fsd->[3], 0, -1);
|
|
}
|
|
printf($unix_fmt, "USER", "COMMAND", "PID", "FD", "PROTO", "ADDRESS");
|
|
foreach $fsd (@{$fstat{"local"}}) {
|
|
next unless defined($fsd->[6]);
|
|
next if (!$conn && defined($fsd->[8]));
|
|
next if (!$listen && !defined($fsd->[8]));
|
|
$nsd = $netstat{$fsd->[6]} || $unknown;
|
|
printf($unix_fmt, $fsd->[0], $fsd->[1], $fsd->[2],
|
|
substr($fsd->[3], 0, -1), $fsd->[5],
|
|
$nsd->[8] || ($fsd->[8] ? $endpoint{$fsd->[8]} : "(none)"));
|
|
}
|
|
print("\n");
|
|
}
|
|
|
|
#
|
|
# Print usage message and exit
|
|
#
|
|
sub usage() {
|
|
print(STDERR "Usage: sockstat [-46clu] [-p ports]\n");
|
|
exit(1);
|
|
}
|
|
|
|
MAIN:{
|
|
my %opts; # Command-line options
|
|
|
|
getopts("46clp:u", \%opts)
|
|
or usage();
|
|
|
|
gather();
|
|
|
|
if (!$opts{'4'} && !$opts{'6'} && !$opts{'u'}) {
|
|
$opts{'4'} = $opts{'6'} = $opts{'u'} = 1;
|
|
}
|
|
if (!$opts{'c'} && !$opts{'l'}) {
|
|
$opts{'c'} = $opts{'l'} = 1;
|
|
}
|
|
if ($opts{'p'}) {
|
|
parse_port_ranges($opts{'p'});
|
|
}
|
|
if ($opts{'4'}) {
|
|
print_inet("internet", $opts{'c'}, $opts{'l'});
|
|
}
|
|
if ($opts{'6'}) {
|
|
print_inet("internet6", $opts{'c'}, $opts{'l'});
|
|
}
|
|
if ($opts{'u'}) {
|
|
print_unix($opts{'c'}, $opts{'l'});
|
|
}
|
|
|
|
exit(0);
|
|
}
|