diff options
author | des <des@FreeBSD.org> | 2002-07-31 12:32:03 +0000 |
---|---|---|
committer | des <des@FreeBSD.org> | 2002-07-31 12:32:03 +0000 |
commit | 06dac202026d0eaa1803bcc5fb7d3f295b40ab07 (patch) | |
tree | 33d43a07ac97b574bfd3614953005b973010504a /usr.bin/sockstat | |
parent | 5e9bc3c12a5ff59cda5edb46ea46bc9a43a902c6 (diff) | |
download | FreeBSD-src-06dac202026d0eaa1803bcc5fb7d3f295b40ab07.zip FreeBSD-src-06dac202026d0eaa1803bcc5fb7d3f295b40ab07.tar.gz |
Rewrite sockstat(1) in C.
Sponsored by: DARPA, NAI Labs
Diffstat (limited to 'usr.bin/sockstat')
-rw-r--r-- | usr.bin/sockstat/Makefile | 3 | ||||
-rw-r--r-- | usr.bin/sockstat/sockstat.1 | 20 | ||||
-rw-r--r-- | usr.bin/sockstat/sockstat.c | 584 | ||||
-rw-r--r-- | usr.bin/sockstat/sockstat.pl | 246 |
4 files changed, 596 insertions, 257 deletions
diff --git a/usr.bin/sockstat/Makefile b/usr.bin/sockstat/Makefile index ab20a8c..f2b82bb 100644 --- a/usr.bin/sockstat/Makefile +++ b/usr.bin/sockstat/Makefile @@ -1,6 +1,7 @@ # $FreeBSD$ -SCRIPTS= sockstat.pl +PROG= sockstat +WARNS?= 4 MAN= sockstat.1 .include <bsd.prog.mk> diff --git a/usr.bin/sockstat/sockstat.1 b/usr.bin/sockstat/sockstat.1 index 383849c..a2f544a 100644 --- a/usr.bin/sockstat/sockstat.1 +++ b/usr.bin/sockstat/sockstat.1 @@ -104,18 +104,18 @@ The transport protocol associated with the socket for Internet sockets, or the type of socket (stream or datagram) for .Ux sockets. -.It Li ADDRESS -.No ( Ux -sockets only) -For bound sockets, this is the filename of the socket. -For other sockets, it is the name, PID and file descriptor number of -the peer, or -.Dq Li "(none)" -if the socket is neither bound nor connected. .It Li LOCAL ADDRESS -(Internet sockets only) -The address the local end of the socket is bound to (see +For Internet sockets, this is the address the local end of the socket +is bound to (see .Xr getsockname 2 ) . +For bound +.Ux +sockets, it is the socket's filename. +For other +.Ux +sockets, it is a right arrow followed by the endpoint's filename, or +.Dq Li ?? +if the endpoint could not be determined. .It Li FOREIGN ADDRESS (Internet sockets only) The address the foreign end of the socket is bound to (see diff --git a/usr.bin/sockstat/sockstat.c b/usr.bin/sockstat/sockstat.c new file mode 100644 index 0000000..3c28bb2 --- /dev/null +++ b/usr.bin/sockstat/sockstat.c @@ -0,0 +1,584 @@ +/*- + * Copyright (c) 2002 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> +#include <sys/file.h> +#include <sys/user.h> + +#include <sys/un.h> +#include <sys/unpcb.h> + +#include <netinet/in.h> +#include <netinet/in_pcb.h> +#include <netinet/tcp.h> +#include <netinet/tcp_seq.h> +#include <netinet/tcp_var.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <netdb.h> +#include <pwd.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static int opt_4; /* Show IPv4 sockets */ +static int opt_6; /* Show IPv6 sockets */ +static int opt_c; /* Show connected sockets */ +static int opt_l; /* Show listening sockets */ +static int opt_u; /* Show Unix domain sockets */ +static int opt_v; /* Verbose mode */ + +static int *ports; + +#define INT_BIT (sizeof(int)*CHAR_BIT) +#define SET_PORT(p) do { ports[p / INT_BIT] |= 1 << (p % INT_BIT); } while (0) +#define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT))) + +struct sock { + void *socket; + void *pcb; + int vflag; + int family; + int proto; + const char *protoname; + struct sockaddr_storage laddr; + struct sockaddr_storage faddr; + struct sock *next; +}; + +#define HASHSIZE 1009 +static struct sock *sockhash[HASHSIZE]; + +static struct xfile *xfiles; +static int nxfiles; + +static int +xprintf(const char *fmt, ...) +{ + va_list ap; + int len; + + va_start(ap, fmt); + len = vprintf(fmt, ap); + va_end(ap); + if (len < 0) + err(1, "printf()"); + return (len); +} + +static void +parse_ports(const char *portspec) +{ + const char *p, *q; + int port, end; + + if (ports == NULL) + if ((ports = calloc(1, 65536 / INT_BIT)) == NULL) + err(1, "calloc()"); + p = portspec; + while (*p != '\0') { + if (!isdigit(*p)) + errx(1, "syntax error in port range"); + for (q = p; *q != '\0' && isdigit(*q); ++q) + /* nothing */ ; + for (port = 0; p < q; ++p) + port = port * 10 + digittoint(*p); + if (port < 0 || port > 65535) + errx(1, "invalid port number"); + SET_PORT(port); + switch (*p) { + case '-': + ++p; + break; + case ',': + ++p; + /* fall through */ + case '\0': + default: + continue; + } + for (q = p; *q != '\0' && isdigit(*q); ++q) + /* nothing */ ; + for (end = 0; p < q; ++p) + end = end * 10 + digittoint(*p); + if (end < port || end > 65535) + errx(1, "invalid port number"); + while (port++ < end) + SET_PORT(port); + if (*p == ',') + ++p; + } +} + +static void +sockaddr(struct sockaddr_storage *sa, int af, void *addr, int port) +{ + struct sockaddr_in *sin4; + struct sockaddr_in6 *sin6; + + bzero(sa, sizeof *sa); + switch (af) { + case AF_INET: + sin4 = (struct sockaddr_in *)sa; + sin4->sin_len = sizeof *sin4; + sin4->sin_family = af; + sin4->sin_port = port; + sin4->sin_addr = *(struct in_addr *)addr; + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)sa; + sin6->sin6_len = sizeof *sin6; + sin6->sin6_family = af; + sin6->sin6_port = port; + sin6->sin6_addr = *(struct in6_addr *)addr; + break; + default: + abort(); + } +} + +static void +gather_inet(int proto) +{ + struct xinpgen *xig, *exig; + struct xinpcb *xip; + struct xtcpcb *xtp; + struct inpcb *inp; + struct xsocket *so; + struct sock *sock; + const char *varname, *protoname; + size_t len, bufsize; + void *buf; + int hash, retry, vflag; + + vflag = 0; + if (opt_4) + vflag |= INP_IPV4; + if (opt_6) + vflag |= INP_IPV6; + + switch (proto) { + case IPPROTO_TCP: + varname = "net.inet.tcp.pcblist"; + protoname = "tcp"; + break; + case IPPROTO_UDP: + varname = "net.inet.udp.pcblist"; + protoname = "udp"; + break; + default: + abort(); + } + + buf = NULL; + bufsize = 8192; + retry = 5; + do { + for (;;) { + if ((buf = realloc(buf, bufsize)) == NULL) + err(1, "realloc()"); + len = bufsize; + if (sysctlbyname(varname, buf, &len, NULL, 0) == 0) + break; + if (errno != ENOMEM) + err(1, "sysctlbyname()"); + bufsize *= 2; + } + xig = (struct xinpgen *)buf; + exig = (struct xinpgen *)((char *)buf + len - sizeof *exig); + if (xig->xig_len != sizeof *xig || + exig->xig_len != sizeof *exig) + errx(1, "struct xinpgen size mismatch"); + } while (xig->xig_gen != exig->xig_gen && retry--); + + if (xig->xig_gen != exig->xig_gen && opt_v) + warnx("warning: data may be inconsistent"); + + for (;;) { + xig = (struct xinpgen *)((char *)xig + xig->xig_len); + if (xig >= exig) + break; + switch (proto) { + case IPPROTO_TCP: + xtp = (struct xtcpcb *)xig; + if (xtp->xt_len != sizeof *xtp) { + warnx("struct xtcpcb size mismatch"); + goto out; + } + inp = &xtp->xt_inp; + so = &xtp->xt_socket; + break; + case IPPROTO_UDP: + xip = (struct xinpcb *)xig; + if (xip->xi_len != sizeof *xip) { + warnx("struct xinpcb size mismatch"); + goto out; + } + inp = &xip->xi_inp; + so = &xip->xi_socket; + break; + default: + abort(); + } + if ((inp->inp_vflag & vflag) == 0) + continue; + if ((sock = calloc(1, sizeof *sock)) == NULL) + err(1, "malloc()"); + sock->socket = so->xso_so; + sock->proto = proto; + if (inp->inp_vflag & INP_IPV4) { + sock->family = AF_INET; + sockaddr(&sock->laddr, sock->family, + &inp->inp_laddr, inp->inp_lport); + sockaddr(&sock->faddr, sock->family, + &inp->inp_faddr, inp->inp_fport); + } else if (inp->inp_vflag & INP_IPV6) { + sock->family = AF_INET6; + sockaddr(&sock->laddr, sock->family, + &inp->in6p_laddr, inp->in6p_lport); + sockaddr(&sock->faddr, sock->family, + &inp->in6p_faddr, inp->in6p_fport); + } else { + if (opt_v) + warnx("invalid vflag 0x%x", inp->inp_vflag); + free(sock); + continue; + } + sock->vflag = inp->inp_vflag; + sock->protoname = protoname; + hash = (int)((uintptr_t)sock->socket % HASHSIZE); + sock->next = sockhash[hash]; + sockhash[hash] = sock; + } +out: + free(buf); +} + +static void +gather_unix(int proto) +{ + struct xunpgen *xug, *exug; + struct xunpcb *xup; + struct sock *sock; + const char *varname, *protoname; + size_t len, bufsize; + void *buf; + int hash, retry; + + switch (proto) { + case SOCK_STREAM: + varname = "net.local.stream.pcblist"; + protoname = "stream"; + break; + case SOCK_DGRAM: + varname = "net.local.dgram.pcblist"; + protoname = "dgram"; + break; + default: + abort(); + } + buf = NULL; + bufsize = 8192; + retry = 5; + do { + for (;;) { + if ((buf = realloc(buf, bufsize)) == NULL) + err(1, "realloc()"); + len = bufsize; + if (sysctlbyname(varname, buf, &len, NULL, 0) == 0) + break; + if (errno != ENOMEM) + err(1, "sysctlbyname()"); + bufsize *= 2; + } + xug = (struct xunpgen *)buf; + exug = (struct xunpgen *)((char *)buf + len - sizeof *exug); + if (xug->xug_len != sizeof *xug || + exug->xug_len != sizeof *exug) { + warnx("struct xinpgen size mismatch"); + goto out; + } + } while (xug->xug_gen != exug->xug_gen && retry--); + + if (xug->xug_gen != exug->xug_gen && opt_v) + warnx("warning: data may be inconsistent"); + + for (;;) { + xug = (struct xunpgen *)((char *)xug + xug->xug_len); + if (xug >= exug) + break; + xup = (struct xunpcb *)xug; + if (xup->xu_len != sizeof *xup) { + warnx("struct xunpcb size mismatch"); + goto out; + } + if ((sock = calloc(1, sizeof *sock)) == NULL) + err(1, "malloc()"); + sock->socket = xup->xu_socket.xso_so; + sock->pcb = xup->xu_unpp; + sock->proto = proto; + sock->family = AF_UNIX; + sock->protoname = protoname; + if (xup->xu_unp.unp_addr != NULL) + sock->laddr = *(struct sockaddr_storage *)&xup->xu_addr; + else if (xup->xu_unp.unp_conn != NULL) + *(void **)&sock->faddr = xup->xu_unp.unp_conn; + hash = (int)((uintptr_t)sock->socket % HASHSIZE); + sock->next = sockhash[hash]; + sockhash[hash] = sock; + } +out: + free(buf); +} + +static void +getfiles(void) +{ + size_t len; + + if ((xfiles = malloc(len = sizeof *xfiles)) == NULL) + err(1, "malloc()"); + while (sysctlbyname("kern.file", xfiles, &len, 0, 0) == -1) { + if (errno != ENOMEM) + err(1, "sysctlbyname()"); + len *= 2; + if ((xfiles = realloc(xfiles, len)) == NULL) + err(1, "realloc()"); + } + if (len > 0 && xfiles->xf_size != sizeof *xfiles) + errx(1, "struct xfile size mismatch"); + nxfiles = len / sizeof *xfiles; +} + +static int +printaddr(int af, struct sockaddr_storage *ss) +{ + char addrstr[INET6_ADDRSTRLEN] = { '\0', '\0' }; + struct sockaddr_un *sun; + void *addr; + int off, port; + + switch (af) { + case AF_INET: + addr = &((struct sockaddr_in *)ss)->sin_addr; + if (inet_lnaof(*(struct in_addr *)addr) == INADDR_ANY) + addrstr[0] = '*'; + port = ntohs(((struct sockaddr_in *)ss)->sin_port); + break; + case AF_INET6: + addr = &((struct sockaddr_in6 *)ss)->sin6_addr; + if (IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *)addr)) + addrstr[0] = '*'; + port = ntohs(((struct sockaddr_in6 *)ss)->sin6_port); + break; + case AF_UNIX: + sun = (struct sockaddr_un *)ss; + off = (int)((char *)&sun->sun_path - (char *)sun); + return (xprintf("%.*s", sun->sun_len - off, sun->sun_path)); + } + if (addrstr[0] == '\0') + inet_ntop(af, addr, addrstr, sizeof addrstr); + if (port == 0) + return xprintf("%s:*", addrstr); + else + return xprintf("%s:%d", addrstr, port); +} + +static const char * +getprocname(pid_t pid) +{ + static struct kinfo_proc proc; + size_t len; + int mib[4]; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = (int)pid; + len = sizeof proc; + if (sysctl(mib, 4, &proc, &len, NULL, 0) == -1) { + warn("sysctl()"); + return ("??"); + } + return (proc.ki_ocomm); +} + +static void +display(void) +{ + struct passwd *pwd; + struct xfile *xf; + struct sock *s; + void *p; + int hash, n, pos; + + printf("%-8s %-10s %-5s %-2s %-6s %-21s %-21s\n", + "USER", "COMMAND", "PID", "FD", "PROTO", + "LOCAL ADDRESS", "FOREIGN ADDRESS"); + setpassent(1); + for (xf = xfiles, n = 0; n < nxfiles; ++n, ++xf) { + hash = (int)((uintptr_t)xf->xf_data % HASHSIZE); + /*xprintf("%p %d\n", xf->xf_data, hash);*/ + for (s = sockhash[hash]; s != NULL; s = s->next) + if (s->socket == xf->xf_data) + break; + if (s == NULL) + continue; + pos = 0; + if ((pwd = getpwuid(xf->xf_uid)) == NULL) + pos += xprintf("%lu", (u_long)xf->xf_uid); + else + pos += xprintf("%s", pwd->pw_name); + while (pos < 9) + pos += xprintf(" "); + pos += xprintf("%.10s", getprocname(xf->xf_pid)); + while (pos < 20) + pos += xprintf(" "); + pos += xprintf("%lu", (u_long)xf->xf_pid); + while (pos < 26) + pos += xprintf(" "); + pos += xprintf("%d", xf->xf_fd); + while (pos < 29) + pos += xprintf(" "); + pos += xprintf("%s", s->protoname); + if (s->vflag & INP_IPV4) + pos += xprintf("4"); + if (s->vflag & INP_IPV6) + pos += xprintf("6"); + while (pos < 36) + pos += xprintf(" "); + switch (s->family) { + case AF_INET: + case AF_INET6: + pos += printaddr(s->family, &s->laddr); + while (pos < 58) + pos += xprintf(" "); + pos += printaddr(s->family, &s->faddr); + break; + case AF_UNIX: + /* server */ + if (s->laddr.ss_len > 0) { + pos += printaddr(s->family, &s->laddr); + break; + } + /* client */ + pos += xprintf("-> "); + p = *(void **)&s->faddr; + for (hash = 0; hash < HASHSIZE; ++hash) { + for (s = sockhash[hash]; s != NULL; s = s->next) + if (s->pcb == p) + break; + if (s != NULL) + break; + } + if (s == NULL || s->laddr.ss_len == 0) + pos += xprintf("??"); + else + pos += printaddr(s->family, &s->laddr); + break; + default: + abort(); + } + xprintf("\n"); + } +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: sockstat [-46clu] [-p ports]\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int o; + + while ((o = getopt(argc, argv, "46clp:uv")) != -1) + switch (o) { + case '4': + opt_4 = 1; + break; + case '6': + opt_6 = 1; + break; + case 'c': + opt_c = 1; + break; + case 'l': + opt_l = 1; + break; + case 'p': + parse_ports(optarg); + break; + case 'u': + opt_u = 1; + break; + case 'v': + ++opt_v; + break; + default: + usage(); + } + + argc -= optind; + argv += optind; + + if (argc > 0) + usage(); + + if (!opt_4 && !opt_6 && !opt_u) + opt_4 = opt_6 = opt_u = 1; + if (!opt_c && !opt_l) + opt_c = opt_l = 1; + + if (opt_4 || opt_6) { + gather_inet(IPPROTO_TCP); + gather_inet(IPPROTO_UDP); + } + if (opt_u) { + gather_unix(SOCK_STREAM); + gather_unix(SOCK_DGRAM); + } + getfiles(); + display(); + + exit(0); +} diff --git a/usr.bin/sockstat/sockstat.pl b/usr.bin/sockstat/sockstat.pl deleted file mode 100644 index 6d1ac0f..0000000 --- a/usr.bin/sockstat/sockstat.pl +++ /dev/null @@ -1,246 +0,0 @@ -#!/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); -} |