aboutsummaryrefslogtreecommitdiff
path: root/usr.bin/sockstat/sockstat.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/sockstat/sockstat.c')
-rw-r--r--usr.bin/sockstat/sockstat.c1883
1 files changed, 22 insertions, 1861 deletions
diff --git a/usr.bin/sockstat/sockstat.c b/usr.bin/sockstat/sockstat.c
index 6761faae5210..7bb7f6a66e3f 100644
--- a/usr.bin/sockstat/sockstat.c
+++ b/usr.bin/sockstat/sockstat.c
@@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2002 Dag-Erling Smørgrav
+ * Copyright (c) 2025 ConnectWise
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -13,8 +13,6 @@
* 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
@@ -28,1889 +26,52 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <sys/param.h>
-#include <sys/file.h>
-#include <sys/socket.h>
-#include <sys/socketvar.h>
-#include <sys/sysctl.h>
-#include <sys/jail.h>
-#include <sys/user.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-
-#include <sys/un.h>
-#include <sys/unpcb.h>
-
-#include <net/route.h>
-
-#include <netinet/in.h>
-#include <netinet/in_pcb.h>
-#include <netinet/sctp.h>
-#include <netinet/tcp.h>
-#define TCPSTATES /* load state names */
-#include <netinet/tcp_fsm.h>
-#include <netinet/tcp_seq.h>
-#include <netinet/tcp_var.h>
-#include <arpa/inet.h>
-
-#include <capsicum_helpers.h>
#include <ctype.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <jail.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
#include <libxo/xo.h>
-#include <libcasper.h>
-#include <casper/cap_net.h>
-#include <casper/cap_netdb.h>
-#include <casper/cap_pwd.h>
-#include <casper/cap_sysctl.h>
-
-#define SOCKSTAT_XO_VERSION "1"
-#define sstosin(ss) ((struct sockaddr_in *)(ss))
-#define sstosin6(ss) ((struct sockaddr_in6 *)(ss))
-#define sstosun(ss) ((struct sockaddr_un *)(ss))
-#define sstosa(ss) ((struct sockaddr *)(ss))
-
-static bool opt_4; /* Show IPv4 sockets */
-static bool opt_6; /* Show IPv6 sockets */
-static bool opt_A; /* Show kernel address of pcb */
-static bool opt_C; /* Show congestion control */
-static bool opt_c; /* Show connected sockets */
-static bool opt_f; /* Show FIB numbers */
-static bool opt_I; /* Show spliced socket addresses */
-static bool opt_i; /* Show inp_gencnt */
-static int opt_j; /* Show specified jail */
-static bool opt_L; /* Don't show IPv4 or IPv6 loopback sockets */
-static bool opt_l; /* Show listening sockets */
-static bool opt_n; /* Don't resolve UIDs to user names */
-static bool opt_q; /* Don't show header */
-static bool opt_S; /* Show protocol stack if applicable */
-static bool opt_s; /* Show protocol state if applicable */
-static bool opt_U; /* Show remote UDP encapsulation port number */
-static bool opt_u; /* Show Unix domain sockets */
-static u_int opt_v; /* Verbose mode */
-static bool opt_w; /* Automatically size the columns */
-
-/*
- * Default protocols to use if no -P was defined.
- */
-static const char *default_protos[] = {"sctp", "tcp", "udp", "divert" };
-static size_t default_numprotos = nitems(default_protos);
-
-static int *protos; /* protocols to use */
-static size_t numprotos; /* allocated size of protos[] */
+#include "sockstat.h"
-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 addr {
- union {
- struct sockaddr_storage address;
- struct { /* unix(4) faddr */
- kvaddr_t conn;
- kvaddr_t firstref;
- kvaddr_t nextref;
- };
- };
- unsigned int encaps_port;
- int state;
- struct addr *next;
-};
-
-struct sock {
- union {
- RB_ENTRY(sock) socket_tree; /* tree of pcbs with socket */
- SLIST_ENTRY(sock) socket_list; /* list of pcbs w/o socket */
- };
- RB_ENTRY(sock) pcb_tree;
- kvaddr_t socket;
- kvaddr_t pcb;
- kvaddr_t splice_socket;
- uint64_t inp_gencnt;
- int shown;
- int vflag;
- int family;
- int proto;
- int state;
- int fibnum;
- const char *protoname;
- char stack[TCP_FUNCTION_NAME_LEN_MAX];
- char cc[TCP_CA_NAME_MAX];
- struct addr *laddr;
- struct addr *faddr;
-};
-
-static RB_HEAD(socks_t, sock) socks = RB_INITIALIZER(&socks);
-static int64_t
-socket_compare(const struct sock *a, const struct sock *b)
-{
- return ((int64_t)(a->socket/2 - b->socket/2));
-}
-RB_GENERATE_STATIC(socks_t, sock, socket_tree, socket_compare);
-
-static RB_HEAD(pcbs_t, sock) pcbs = RB_INITIALIZER(&pcbs);
-static int64_t
-pcb_compare(const struct sock *a, const struct sock *b)
-{
- return ((int64_t)(a->pcb/2 - b->pcb/2));
-}
-RB_GENERATE_STATIC(pcbs_t, sock, pcb_tree, pcb_compare);
-
-static SLIST_HEAD(, sock) nosocks = SLIST_HEAD_INITIALIZER(&nosocks);
-
-struct file {
- RB_ENTRY(file) file_tree;
- kvaddr_t xf_data;
- pid_t xf_pid;
- uid_t xf_uid;
- int xf_fd;
-};
-
-static RB_HEAD(files_t, file) ftree = RB_INITIALIZER(&ftree);
-static int64_t
-file_compare(const struct file *a, const struct file *b)
-{
- return ((int64_t)(a->xf_data/2 - b->xf_data/2));
-}
-RB_GENERATE_STATIC(files_t, file, file_tree, file_compare);
-
-static struct file *files;
-static int nfiles;
-
-static cap_channel_t *capnet;
-static cap_channel_t *capnetdb;
-static cap_channel_t *capsysctl;
-static cap_channel_t *cappwd;
-
-static bool
-_check_ksize(size_t received_size, size_t expected_size, const char *struct_name)
-{
- if (received_size != expected_size) {
- xo_warnx("%s size mismatch: expected %zd, received %zd",
- struct_name, expected_size, received_size);
- return false;
- }
- return true;
-}
-#define check_ksize(_sz, _struct) (_check_ksize(_sz, sizeof(_struct), #_struct))
-
-static void
-_enforce_ksize(size_t received_size, size_t expected_size, const char *struct_name)
-{
- if (received_size != expected_size) {
- xo_errx(1, "fatal: struct %s size mismatch: expected %zd, received %zd",
- struct_name, expected_size, received_size);
- }
-}
-#define enforce_ksize(_sz, _struct) (_enforce_ksize(_sz, sizeof(_struct), #_struct))
-
-static int
-get_proto_type(const char *proto)
-{
- struct protoent *pent;
+int *ports;
- if (strlen(proto) == 0)
- return (0);
- if (capnetdb != NULL)
- pent = cap_getprotobyname(capnetdb, proto);
- else
- pent = getprotobyname(proto);
- if (pent == NULL) {
- xo_warn("cap_getprotobyname");
- return (-1);
- }
- return (pent->p_proto);
-}
-
-static void
-init_protos(int num)
-{
- int proto_count = 0;
-
- if (num > 0) {
- proto_count = num;
- } else {
- /* Find the maximum number of possible protocols. */
- while (getprotoent() != NULL)
- proto_count++;
- endprotoent();
- }
-
- if ((protos = malloc(sizeof(int) * proto_count)) == NULL)
- xo_err(1, "malloc");
- numprotos = proto_count;
-}
-
-static int
-parse_protos(char *protospec)
-{
- char *prot;
- int proto_type, proto_index;
-
- if (protospec == NULL)
- return (-1);
-
- init_protos(0);
- proto_index = 0;
- while ((prot = strsep(&protospec, ",")) != NULL) {
- if (strlen(prot) == 0)
- continue;
- proto_type = get_proto_type(prot);
- if (proto_type != -1)
- protos[proto_index++] = proto_type;
- }
- numprotos = proto_index;
- return (proto_index);
-}
-
-static void
+int
parse_ports(const char *portspec)
{
- const char *p, *q;
- int port, end;
+ const char *p;
if (ports == NULL)
if ((ports = calloc(65536 / INT_BIT, sizeof(int))) == NULL)
xo_err(1, "calloc()");
p = portspec;
while (*p != '\0') {
- if (!isdigit(*p))
- xo_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);
+ long port, end;
+ char *endptr = NULL;
+
+ errno = 0;
+ port = strtol(p, &endptr, 10);
+ if (errno)
+ return (errno);
if (port < 0 || port > 65535)
- xo_errx(1, "invalid port number");
+ return (ERANGE);
SET_PORT(port);
- switch (*p) {
+ switch (*endptr) {
case '-':
- ++p;
+ p = endptr + 1;
+ end = strtol(p, &endptr, 10);
break;
case ',':
- ++p;
- /* fall through */
- case '\0':
+ p = endptr + 1;
+ continue;
default:
+ p = endptr;
continue;
}
- for (q = p; *q != '\0' && isdigit(*q); ++q)
- /* nothing */ ;
- for (end = 0; p < q; ++p)
- end = end * 10 + digittoint(*p);
+ if (errno)
+ return (errno);
if (end < port || end > 65535)
- xo_errx(1, "invalid port number");
+ return (ERANGE);
while (port++ < end)
SET_PORT(port);
- if (*p == ',')
- ++p;
- }
-}
-
-static void
-sockaddr(struct sockaddr_storage *ss, int af, void *addr, int port)
-{
- struct sockaddr_in *sin4;
- struct sockaddr_in6 *sin6;
-
- bzero(ss, sizeof(*ss));
- switch (af) {
- case AF_INET:
- sin4 = sstosin(ss);
- 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 = sstosin6(ss);
- sin6->sin6_len = sizeof(*sin6);
- sin6->sin6_family = af;
- sin6->sin6_port = port;
- sin6->sin6_addr = *(struct in6_addr *)addr;
-#define s6_addr16 __u6_addr.__u6_addr16
- if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
- sin6->sin6_scope_id =
- ntohs(sin6->sin6_addr.s6_addr16[1]);
- sin6->sin6_addr.s6_addr16[1] = 0;
- }
- break;
- default:
- abort();
- }
-}
-
-static void
-free_socket(struct sock *sock)
-{
- struct addr *cur, *next;
-
- cur = sock->laddr;
- while (cur != NULL) {
- next = cur->next;
- free(cur);
- cur = next;
- }
- cur = sock->faddr;
- while (cur != NULL) {
- next = cur->next;
- free(cur);
- cur = next;
- }
- free(sock);
-}
-
-static void
-gather_sctp(void)
-{
- struct sock *sock;
- struct addr *laddr, *prev_laddr, *faddr, *prev_faddr;
- struct xsctp_inpcb *xinpcb;
- struct xsctp_tcb *xstcb;
- struct xsctp_raddr *xraddr;
- struct xsctp_laddr *xladdr;
- const char *varname;
- size_t len, offset;
- char *buf;
- int vflag;
- int no_stcb, local_all_loopback, foreign_all_loopback;
-
- vflag = 0;
- if (opt_4)
- vflag |= INP_IPV4;
- if (opt_6)
- vflag |= INP_IPV6;
-
- varname = "net.inet.sctp.assoclist";
- if (cap_sysctlbyname(capsysctl, varname, 0, &len, 0, 0) < 0) {
- if (errno != ENOENT)
- xo_err(1, "cap_sysctlbyname()");
- return;
- }
- if ((buf = (char *)malloc(len)) == NULL) {
- xo_err(1, "malloc()");
- return;
- }
- if (cap_sysctlbyname(capsysctl, varname, buf, &len, 0, 0) < 0) {
- xo_err(1, "cap_sysctlbyname()");
- free(buf);
- return;
- }
- xinpcb = (struct xsctp_inpcb *)(void *)buf;
- offset = sizeof(struct xsctp_inpcb);
- while ((offset < len) && (xinpcb->last == 0)) {
- if ((sock = calloc(1, sizeof *sock)) == NULL)
- xo_err(1, "malloc()");
- sock->socket = xinpcb->socket;
- sock->proto = IPPROTO_SCTP;
- sock->protoname = "sctp";
- if (xinpcb->maxqlen == 0)
- sock->state = SCTP_CLOSED;
- else
- sock->state = SCTP_LISTEN;
- if (xinpcb->flags & SCTP_PCB_FLAGS_BOUND_V6) {
- sock->family = AF_INET6;
- /*
- * Currently there is no way to distinguish between
- * IPv6 only sockets or dual family sockets.
- * So mark it as dual socket.
- */
- sock->vflag = INP_IPV6 | INP_IPV4;
- } else {
- sock->family = AF_INET;
- sock->vflag = INP_IPV4;
- }
- prev_laddr = NULL;
- local_all_loopback = 1;
- while (offset < len) {
- xladdr = (struct xsctp_laddr *)(void *)(buf + offset);
- offset += sizeof(struct xsctp_laddr);
- if (xladdr->last == 1)
- break;
- if ((laddr = calloc(1, sizeof(struct addr))) == NULL)
- xo_err(1, "malloc()");
- switch (xladdr->address.sa.sa_family) {
- case AF_INET:
-#define __IN_IS_ADDR_LOOPBACK(pina) \
- ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
- if (!__IN_IS_ADDR_LOOPBACK(
- &xladdr->address.sin.sin_addr))
- local_all_loopback = 0;
-#undef __IN_IS_ADDR_LOOPBACK
- sockaddr(&laddr->address, AF_INET,
- &xladdr->address.sin.sin_addr,
- htons(xinpcb->local_port));
- break;
- case AF_INET6:
- if (!IN6_IS_ADDR_LOOPBACK(
- &xladdr->address.sin6.sin6_addr))
- local_all_loopback = 0;
- sockaddr(&laddr->address, AF_INET6,
- &xladdr->address.sin6.sin6_addr,
- htons(xinpcb->local_port));
- break;
- default:
- xo_errx(1, "address family %d not supported",
- xladdr->address.sa.sa_family);
- }
- laddr->next = NULL;
- if (prev_laddr == NULL)
- sock->laddr = laddr;
- else
- prev_laddr->next = laddr;
- prev_laddr = laddr;
- }
- if (sock->laddr == NULL) {
- if ((sock->laddr =
- calloc(1, sizeof(struct addr))) == NULL)
- xo_err(1, "malloc()");
- sock->laddr->address.ss_family = sock->family;
- if (sock->family == AF_INET)
- sock->laddr->address.ss_len =
- sizeof(struct sockaddr_in);
- else
- sock->laddr->address.ss_len =
- sizeof(struct sockaddr_in6);
- local_all_loopback = 0;
- }
- if ((sock->faddr = calloc(1, sizeof(struct addr))) == NULL)
- xo_err(1, "malloc()");
- sock->faddr->address.ss_family = sock->family;
- if (sock->family == AF_INET)
- sock->faddr->address.ss_len =
- sizeof(struct sockaddr_in);
- else
- sock->faddr->address.ss_len =
- sizeof(struct sockaddr_in6);
- no_stcb = 1;
- while (offset < len) {
- xstcb = (struct xsctp_tcb *)(void *)(buf + offset);
- offset += sizeof(struct xsctp_tcb);
- if (no_stcb) {
- if (opt_l && (sock->vflag & vflag) &&
- (!opt_L || !local_all_loopback) &&
- ((xinpcb->flags & SCTP_PCB_FLAGS_UDPTYPE) ||
- (xstcb->last == 1))) {
- RB_INSERT(socks_t, &socks, sock);
- } else {
- free_socket(sock);
- }
- }
- if (xstcb->last == 1)
- break;
- no_stcb = 0;
- if (opt_c) {
- if ((sock = calloc(1, sizeof *sock)) == NULL)
- xo_err(1, "malloc()");
- sock->socket = xinpcb->socket;
- sock->proto = IPPROTO_SCTP;
- sock->protoname = "sctp";
- sock->state = (int)xstcb->state;
- if (xinpcb->flags & SCTP_PCB_FLAGS_BOUND_V6) {
- sock->family = AF_INET6;
- /*
- * Currently there is no way to distinguish
- * between IPv6 only sockets or dual family
- * sockets. So mark it as dual socket.
- */
- sock->vflag = INP_IPV6 | INP_IPV4;
- } else {
- sock->family = AF_INET;
- sock->vflag = INP_IPV4;
- }
- }
- prev_laddr = NULL;
- local_all_loopback = 1;
- while (offset < len) {
- xladdr = (struct xsctp_laddr *)(void *)(buf +
- offset);
- offset += sizeof(struct xsctp_laddr);
- if (xladdr->last == 1)
- break;
- if (!opt_c)
- continue;
- laddr = calloc(1, sizeof(struct addr));
- if (laddr == NULL)
- xo_err(1, "malloc()");
- switch (xladdr->address.sa.sa_family) {
- case AF_INET:
-#define __IN_IS_ADDR_LOOPBACK(pina) \
- ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
- if (!__IN_IS_ADDR_LOOPBACK(
- &xladdr->address.sin.sin_addr))
- local_all_loopback = 0;
-#undef __IN_IS_ADDR_LOOPBACK
- sockaddr(&laddr->address, AF_INET,
- &xladdr->address.sin.sin_addr,
- htons(xstcb->local_port));
- break;
- case AF_INET6:
- if (!IN6_IS_ADDR_LOOPBACK(
- &xladdr->address.sin6.sin6_addr))
- local_all_loopback = 0;
- sockaddr(&laddr->address, AF_INET6,
- &xladdr->address.sin6.sin6_addr,
- htons(xstcb->local_port));
- break;
- default:
- xo_errx(1,
- "address family %d not supported",
- xladdr->address.sa.sa_family);
- }
- laddr->next = NULL;
- if (prev_laddr == NULL)
- sock->laddr = laddr;
- else
- prev_laddr->next = laddr;
- prev_laddr = laddr;
- }
- prev_faddr = NULL;
- foreign_all_loopback = 1;
- while (offset < len) {
- xraddr = (struct xsctp_raddr *)(void *)(buf +
- offset);
- offset += sizeof(struct xsctp_raddr);
- if (xraddr->last == 1)
- break;
- if (!opt_c)
- continue;
- faddr = calloc(1, sizeof(struct addr));
- if (faddr == NULL)
- xo_err(1, "malloc()");
- switch (xraddr->address.sa.sa_family) {
- case AF_INET:
-#define __IN_IS_ADDR_LOOPBACK(pina) \
- ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
- if (!__IN_IS_ADDR_LOOPBACK(
- &xraddr->address.sin.sin_addr))
- foreign_all_loopback = 0;
-#undef __IN_IS_ADDR_LOOPBACK
- sockaddr(&faddr->address, AF_INET,
- &xraddr->address.sin.sin_addr,
- htons(xstcb->remote_port));
- break;
- case AF_INET6:
- if (!IN6_IS_ADDR_LOOPBACK(
- &xraddr->address.sin6.sin6_addr))
- foreign_all_loopback = 0;
- sockaddr(&faddr->address, AF_INET6,
- &xraddr->address.sin6.sin6_addr,
- htons(xstcb->remote_port));
- break;
- default:
- xo_errx(1,
- "address family %d not supported",
- xraddr->address.sa.sa_family);
- }
- faddr->encaps_port = xraddr->encaps_port;
- faddr->state = xraddr->state;
- faddr->next = NULL;
- if (prev_faddr == NULL)
- sock->faddr = faddr;
- else
- prev_faddr->next = faddr;
- prev_faddr = faddr;
- }
- if (opt_c) {
- if ((sock->vflag & vflag) &&
- (!opt_L ||
- !(local_all_loopback ||
- foreign_all_loopback))) {
- RB_INSERT(socks_t, &socks, sock);
- } else {
- free_socket(sock);
- }
- }
- }
- xinpcb = (struct xsctp_inpcb *)(void *)(buf + offset);
- offset += sizeof(struct xsctp_inpcb);
- }
- free(buf);
-}
-
-static void
-gather_inet(int proto)
-{
- struct xinpgen *xig, *exig;
- struct xinpcb *xip;
- struct xtcpcb *xtp = NULL;
- struct xsocket *so;
- struct sock *sock;
- struct addr *laddr, *faddr;
- const char *varname, *protoname;
- size_t len, bufsize;
- void *buf;
- int 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;
- case IPPROTO_DIVERT:
- varname = "net.inet.divert.pcblist";
- protoname = "div";
- break;
- default:
- xo_errx(1, "protocol %d not supported", proto);
- }
-
- buf = NULL;
- bufsize = 8192;
- retry = 5;
- do {
- for (;;) {
- if ((buf = realloc(buf, bufsize)) == NULL)
- xo_err(1, "realloc()");
- len = bufsize;
- if (cap_sysctlbyname(capsysctl, varname, buf, &len,
- NULL, 0) == 0)
- break;
- if (errno == ENOENT)
- goto out;
- if (errno != ENOMEM || len != bufsize)
- xo_err(1, "cap_sysctlbyname()");
- bufsize *= 2;
- }
- xig = (struct xinpgen *)buf;
- exig = (struct xinpgen *)(void *)
- ((char *)buf + len - sizeof *exig);
- enforce_ksize(xig->xig_len, struct xinpgen);
- enforce_ksize(exig->xig_len, struct xinpgen);
- } while (xig->xig_gen != exig->xig_gen && retry--);
-
- if (xig->xig_gen != exig->xig_gen && opt_v)
- xo_warnx("warning: data may be inconsistent");
-
- for (;;) {
- xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len);
- if (xig >= exig)
- break;
- switch (proto) {
- case IPPROTO_TCP:
- xtp = (struct xtcpcb *)xig;
- xip = &xtp->xt_inp;
- if (!check_ksize(xtp->xt_len, struct xtcpcb))
- goto out;
- protoname = xtp->t_flags & TF_TOE ? "toe" : "tcp";
- break;
- case IPPROTO_UDP:
- case IPPROTO_DIVERT:
- xip = (struct xinpcb *)xig;
- if (!check_ksize(xip->xi_len, struct xinpcb))
- goto out;
- break;
- default:
- xo_errx(1, "protocol %d not supported", proto);
- }
- so = &xip->xi_socket;
- if ((xip->inp_vflag & vflag) == 0)
- continue;
- if (xip->inp_vflag & INP_IPV4) {
- if ((xip->inp_fport == 0 && !opt_l) ||
- (xip->inp_fport != 0 && !opt_c))
- continue;
-#define __IN_IS_ADDR_LOOPBACK(pina) \
- ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
- if (opt_L &&
- (__IN_IS_ADDR_LOOPBACK(&xip->inp_faddr) ||
- __IN_IS_ADDR_LOOPBACK(&xip->inp_laddr)))
- continue;
-#undef __IN_IS_ADDR_LOOPBACK
- } else if (xip->inp_vflag & INP_IPV6) {
- if ((xip->inp_fport == 0 && !opt_l) ||
- (xip->inp_fport != 0 && !opt_c))
- continue;
- if (opt_L &&
- (IN6_IS_ADDR_LOOPBACK(&xip->in6p_faddr) ||
- IN6_IS_ADDR_LOOPBACK(&xip->in6p_laddr)))
- continue;
- } else {
- if (opt_v)
- xo_warnx("invalid vflag 0x%x", xip->inp_vflag);
- continue;
- }
- if ((sock = calloc(1, sizeof(*sock))) == NULL)
- xo_err(1, "malloc()");
- if ((laddr = calloc(1, sizeof *laddr)) == NULL)
- xo_err(1, "malloc()");
- if ((faddr = calloc(1, sizeof *faddr)) == NULL)
- xo_err(1, "malloc()");
- sock->socket = so->xso_so;
- sock->pcb = so->so_pcb;
- sock->splice_socket = so->so_splice_so;
- sock->proto = proto;
- sock->inp_gencnt = xip->inp_gencnt;
- sock->fibnum = so->so_fibnum;
- if (xip->inp_vflag & INP_IPV4) {
- sock->family = AF_INET;
- sockaddr(&laddr->address, sock->family,
- &xip->inp_laddr, xip->inp_lport);
- sockaddr(&faddr->address, sock->family,
- &xip->inp_faddr, xip->inp_fport);
- } else if (xip->inp_vflag & INP_IPV6) {
- sock->family = AF_INET6;
- sockaddr(&laddr->address, sock->family,
- &xip->in6p_laddr, xip->inp_lport);
- sockaddr(&faddr->address, sock->family,
- &xip->in6p_faddr, xip->inp_fport);
- }
- if (proto == IPPROTO_TCP)
- faddr->encaps_port = xtp->xt_encaps_port;
- laddr->next = NULL;
- faddr->next = NULL;
- sock->laddr = laddr;
- sock->faddr = faddr;
- sock->vflag = xip->inp_vflag;
- if (proto == IPPROTO_TCP) {
- sock->state = xtp->t_state;
- memcpy(sock->stack, xtp->xt_stack,
- TCP_FUNCTION_NAME_LEN_MAX);
- memcpy(sock->cc, xtp->xt_cc, TCP_CA_NAME_MAX);
- }
- sock->protoname = protoname;
- if (sock->socket != 0)
- RB_INSERT(socks_t, &socks, sock);
- else
- SLIST_INSERT_HEAD(&nosocks, sock, socket_list);
- }
-out:
- free(buf);
-}
-
-static void
-gather_unix(int proto)
-{
- struct xunpgen *xug, *exug;
- struct xunpcb *xup;
- struct sock *sock;
- struct addr *laddr, *faddr;
- const char *varname, *protoname;
- size_t len, bufsize;
- void *buf;
- int 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;
- case SOCK_SEQPACKET:
- varname = "net.local.seqpacket.pcblist";
- protoname = (xo_get_style(NULL) == XO_STYLE_TEXT)
- ? "seqpac"
- : "seqpacket";
- break;
- default:
- abort();
- }
- buf = NULL;
- bufsize = 8192;
- retry = 5;
- do {
- for (;;) {
- if ((buf = realloc(buf, bufsize)) == NULL)
- xo_err(1, "realloc()");
- len = bufsize;
- if (cap_sysctlbyname(capsysctl, varname, buf, &len,
- NULL, 0) == 0)
- break;
- if (errno != ENOMEM || len != bufsize)
- xo_err(1, "cap_sysctlbyname()");
- bufsize *= 2;
- }
- xug = (struct xunpgen *)buf;
- exug = (struct xunpgen *)(void *)
- ((char *)buf + len - sizeof(*exug));
- if (!check_ksize(xug->xug_len, struct xunpgen) ||
- !check_ksize(exug->xug_len, struct xunpgen))
- goto out;
- } while (xug->xug_gen != exug->xug_gen && retry--);
-
- if (xug->xug_gen != exug->xug_gen && opt_v)
- xo_warnx("warning: data may be inconsistent");
-
- for (;;) {
- xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len);
- if (xug >= exug)
- break;
- xup = (struct xunpcb *)xug;
- if (!check_ksize(xup->xu_len, struct xunpcb))
- goto out;
- if ((xup->unp_conn == 0 && !opt_l) ||
- (xup->unp_conn != 0 && !opt_c))
- continue;
- if ((sock = calloc(1, sizeof(*sock))) == NULL)
- xo_err(1, "malloc()");
- if ((laddr = calloc(1, sizeof *laddr)) == NULL)
- xo_err(1, "malloc()");
- if ((faddr = calloc(1, sizeof *faddr)) == NULL)
- xo_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_addr.sun_family == AF_UNIX)
- laddr->address =
- *(struct sockaddr_storage *)(void *)&xup->xu_addr;
- faddr->conn = xup->unp_conn;
- faddr->firstref = xup->xu_firstref;
- faddr->nextref = xup->xu_nextref;
- laddr->next = NULL;
- faddr->next = NULL;
- sock->laddr = laddr;
- sock->faddr = faddr;
- RB_INSERT(socks_t, &socks, sock);
- RB_INSERT(pcbs_t, &pcbs, sock);
- }
-out:
- free(buf);
-}
-
-static void
-getfiles(void)
-{
- struct xfile *xfiles;
- size_t len, olen;
-
- olen = len = sizeof(*xfiles);
- if ((xfiles = malloc(len)) == NULL)
- xo_err(1, "malloc()");
- while (cap_sysctlbyname(capsysctl, "kern.file", xfiles, &len, 0, 0)
- == -1) {
- if (errno != ENOMEM || len != olen)
- xo_err(1, "cap_sysctlbyname()");
- olen = len *= 2;
- if ((xfiles = realloc(xfiles, len)) == NULL)
- xo_err(1, "realloc()");
- }
- if (len > 0)
- enforce_ksize(xfiles->xf_size, struct xfile);
- nfiles = len / sizeof(*xfiles);
-
- if ((files = malloc(nfiles * sizeof(struct file))) == NULL)
- xo_err(1, "malloc()");
-
- for (int i = 0; i < nfiles; i++) {
- files[i].xf_data = xfiles[i].xf_data;
- files[i].xf_pid = xfiles[i].xf_pid;
- files[i].xf_uid = xfiles[i].xf_uid;
- files[i].xf_fd = xfiles[i].xf_fd;
- RB_INSERT(files_t, &ftree, &files[i]);
- }
-
- free(xfiles);
-}
-
-static int
-formataddr(struct sockaddr_storage *ss, char *buf, size_t bufsize)
-{
- struct sockaddr_un *sun;
- char addrstr[NI_MAXHOST] = { '\0', '\0' };
- int error, off, port = 0;
- const bool is_text_style = (xo_get_style(NULL) == XO_STYLE_TEXT);
-
- switch (ss->ss_family) {
- case AF_INET:
- if (sstosin(ss)->sin_addr.s_addr == INADDR_ANY)
- addrstr[0] = '*';
- port = ntohs(sstosin(ss)->sin_port);
- break;
- case AF_INET6:
- if (IN6_IS_ADDR_UNSPECIFIED(&sstosin6(ss)->sin6_addr))
- addrstr[0] = '*';
- port = ntohs(sstosin6(ss)->sin6_port);
- break;
- case AF_UNIX:
- sun = sstosun(ss);
- off = (int)((char *)&sun->sun_path - (char *)sun);
- if (!is_text_style) {
- xo_emit("{:path/%.*s}", sun->sun_len - off,
- sun->sun_path);
- return 0;
- }
- return snprintf(buf, bufsize, "%.*s",
- sun->sun_len - off, sun->sun_path);
- }
- if (addrstr[0] == '\0') {
- error = cap_getnameinfo(capnet, sstosa(ss), ss->ss_len,
- addrstr, sizeof(addrstr), NULL, 0, NI_NUMERICHOST);
- if (error)
- xo_errx(1, "cap_getnameinfo()");
- }
- if (!is_text_style) {
- xo_emit("{:address/%s}", addrstr);
- xo_emit("{:port/%d}", port);
- return 0;
- }
- if (port == 0)
- return snprintf(buf, bufsize, "%s:*", addrstr);
- return snprintf(buf, bufsize, "%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 (cap_sysctl(capsysctl, mib, nitems(mib), &proc, &len, NULL, 0)
- == -1) {
- /* Do not warn if the process exits before we get its name. */
- if (errno != ESRCH)
- xo_warn("cap_sysctl()");
- return ("??");
- }
- return (proc.ki_comm);
-}
-
-static int
-getprocjid(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 (cap_sysctl(capsysctl, mib, nitems(mib), &proc, &len, NULL, 0)
- == -1) {
- /* Do not warn if the process exits before we get its jid. */
- if (errno != ESRCH)
- xo_warn("cap_sysctl()");
- return (-1);
- }
- return (proc.ki_jid);
-}
-
-static int
-check_ports(struct sock *s)
-{
- int port;
- struct addr *addr;
-
- if (ports == NULL)
- return (1);
- if ((s->family != AF_INET) && (s->family != AF_INET6))
- return (1);
- for (addr = s->laddr; addr != NULL; addr = addr->next) {
- if (s->family == AF_INET)
- port = ntohs(sstosin(&addr->address)->sin_port);
- else
- port = ntohs(sstosin6(&addr->address)->sin6_port);
- if (CHK_PORT(port))
- return (1);
- }
- for (addr = s->faddr; addr != NULL; addr = addr->next) {
- if (s->family == AF_INET)
- port = ntohs(sstosin(&addr->address)->sin_port);
- else
- port = ntohs(sstosin6(&addr->address)->sin6_port);
- if (CHK_PORT(port))
- return (1);
}
return (0);
}
-
-static const char *
-sctp_conn_state(int state)
-{
- switch (state) {
- case SCTP_CLOSED:
- return "CLOSED";
- break;
- case SCTP_BOUND:
- return "BOUND";
- break;
- case SCTP_LISTEN:
- return "LISTEN";
- break;
- case SCTP_COOKIE_WAIT:
- return "COOKIE_WAIT";
- break;
- case SCTP_COOKIE_ECHOED:
- return "COOKIE_ECHOED";
- break;
- case SCTP_ESTABLISHED:
- return "ESTABLISHED";
- break;
- case SCTP_SHUTDOWN_SENT:
- return "SHUTDOWN_SENT";
- break;
- case SCTP_SHUTDOWN_RECEIVED:
- return "SHUTDOWN_RECEIVED";
- break;
- case SCTP_SHUTDOWN_ACK_SENT:
- return "SHUTDOWN_ACK_SENT";
- break;
- case SCTP_SHUTDOWN_PENDING:
- return "SHUTDOWN_PENDING";
- break;
- default:
- return "UNKNOWN";
- break;
- }
-}
-
-static const char *
-sctp_path_state(int state)
-{
- switch (state) {
- case SCTP_UNCONFIRMED:
- return "UNCONFIRMED";
- break;
- case SCTP_ACTIVE:
- return "ACTIVE";
- break;
- case SCTP_INACTIVE:
- return "INACTIVE";
- break;
- default:
- return "UNKNOWN";
- break;
- }
-}
-
-static int
-format_unix_faddr(struct addr *faddr, char *buf, size_t bufsize) {
- #define SAFEBUF (buf == NULL ? NULL : buf + pos)
- #define SAFESIZE (buf == NULL ? 0 : bufsize - pos)
-
- size_t pos = 0;
- const bool is_text_style = (xo_get_style(NULL) == XO_STYLE_TEXT);
- if (faddr->conn != 0) {
- /* Remote peer we connect(2) to, if any. */
- struct sock *p;
- if (is_text_style)
- pos += strlcpy(SAFEBUF, "-> ", SAFESIZE);
- p = RB_FIND(pcbs_t, &pcbs,
- &(struct sock){ .pcb = faddr->conn });
- if (__predict_false(p == NULL) && is_text_style) {
- /* XXGL: can this happen at all? */
- pos += snprintf(SAFEBUF, SAFESIZE, "??");
- } else if (p->laddr->address.ss_len == 0) {
- struct file *f;
- f = RB_FIND(files_t, &ftree,
- &(struct file){ .xf_data =
- p->socket });
- if (f != NULL) {
- if (is_text_style) {
- pos += snprintf(SAFEBUF, SAFESIZE,
- "[%lu %d]", (u_long)f->xf_pid,
- f->xf_fd);
- } else {
- xo_open_list("connections");
- xo_open_instance("connections");
- xo_emit("{:pid/%lu}", (u_long)f->xf_pid);
- xo_emit("{:fd/%d}", f->xf_fd);
- xo_close_instance("connections");
- xo_close_list("connections");
- }
- }
- } else
- pos += formataddr(&p->laddr->address,
- SAFEBUF, SAFESIZE);
- } else if (faddr->firstref != 0) {
- /* Remote peer(s) connect(2)ed to us, if any. */
- struct sock *p;
- struct file *f;
- kvaddr_t ref = faddr->firstref;
- bool fref = true;
-
- if (is_text_style)
- pos += snprintf(SAFEBUF, SAFESIZE, " <- ");
- xo_open_list("connections");
- while ((p = RB_FIND(pcbs_t, &pcbs,
- &(struct sock){ .pcb = ref })) != 0) {
- f = RB_FIND(files_t, &ftree,
- &(struct file){ .xf_data = p->socket });
- if (f != NULL) {
- if (is_text_style) {
- pos += snprintf(SAFEBUF, SAFESIZE,
- "%s[%lu %d]", fref ? "" : ",",
- (u_long)f->xf_pid, f->xf_fd);
- } else {
- xo_open_instance("connections");
- xo_emit("{:pid/%lu}", (u_long)f->xf_pid);
- xo_emit("{:fd/%d}", f->xf_fd);
- xo_close_instance("connections");
- }
- }
- ref = p->faddr->nextref;
- fref = false;
- }
- xo_close_list("connections");
- }
- return pos;
-}
-
-struct col_widths {
- int user;
- int command;
- int pid;
- int fd;
- int proto;
- int local_addr;
- int foreign_addr;
- int pcb_kva;
- int fib;
- int splice_address;
- int inp_gencnt;
- int encaps;
- int path_state;
- int conn_state;
- int stack;
- int cc;
-};
-
-static void
-calculate_sock_column_widths(struct col_widths *cw, struct sock *s)
-{
- struct addr *laddr, *faddr;
- bool first = true;
- int len = 0;
- laddr = s->laddr;
- faddr = s->faddr;
- first = true;
-
- len = strlen(s->protoname);
- if (s->vflag & (INP_IPV4 | INP_IPV6))
- len += 1;
- cw->proto = MAX(cw->proto, len);
-
- while (laddr != NULL || faddr != NULL) {
- if (opt_w && s->family == AF_UNIX) {
- if ((laddr == NULL) || (faddr == NULL))
- xo_errx(1, "laddr = %p or faddr = %p is NULL",
- (void *)laddr, (void *)faddr);
- if (laddr->address.ss_len > 0)
- len = formataddr(&laddr->address, NULL, 0);
- cw->local_addr = MAX(cw->local_addr, len);
- len = format_unix_faddr(faddr, NULL, 0);
- cw->foreign_addr = MAX(cw->foreign_addr, len);
- } else if (opt_w) {
- if (laddr != NULL) {
- len = formataddr(&laddr->address, NULL, 0);
- cw->local_addr = MAX(cw->local_addr, len);
- }
- if (faddr != NULL) {
- len = formataddr(&faddr->address, NULL, 0);
- cw->foreign_addr = MAX(cw->foreign_addr, len);
- }
- }
- if (opt_f) {
- len = snprintf(NULL, 0, "%d", s->fibnum);
- cw->fib = MAX(cw->fib, len);
- }
- if (opt_I) {
- if (s->splice_socket != 0) {
- struct sock *sp;
-
- sp = RB_FIND(socks_t, &socks, &(struct sock)
- { .socket = s->splice_socket });
- if (sp != NULL) {
- len = formataddr(&sp->laddr->address,
- NULL, 0);
- cw->splice_address = MAX(
- cw->splice_address, len);
- }
- }
- }
- if (opt_i) {
- if (s->proto == IPPROTO_TCP || s->proto == IPPROTO_UDP)
- {
- len = snprintf(NULL, 0,
- "%" PRIu64, s->inp_gencnt);
- cw->inp_gencnt = MAX(cw->inp_gencnt, len);
- }
- }
- if (opt_U) {
- if (faddr != NULL &&
- ((s->proto == IPPROTO_SCTP &&
- s->state != SCTP_CLOSED &&
- s->state != SCTP_BOUND &&
- s->state != SCTP_LISTEN) ||
- (s->proto == IPPROTO_TCP &&
- s->state != TCPS_CLOSED &&
- s->state != TCPS_LISTEN))) {
- len = snprintf(NULL, 0, "%u",
- ntohs(faddr->encaps_port));
- cw->encaps = MAX(cw->encaps, len);
- }
- }
- if (opt_s) {
- if (faddr != NULL &&
- s->proto == IPPROTO_SCTP &&
- s->state != SCTP_CLOSED &&
- s->state != SCTP_BOUND &&
- s->state != SCTP_LISTEN) {
- len = strlen(sctp_path_state(faddr->state));
- cw->path_state = MAX(cw->path_state, len);
- }
- }
- if (first) {
- if (opt_s) {
- if (s->proto == IPPROTO_SCTP ||
- s->proto == IPPROTO_TCP) {
- switch (s->proto) {
- case IPPROTO_SCTP:
- len = strlen(
- sctp_conn_state(s->state));
- cw->conn_state = MAX(
- cw->conn_state, len);
- break;
- case IPPROTO_TCP:
- if (s->state >= 0 &&
- s->state < TCP_NSTATES) {
- len = strlen(
- tcpstates[s->state]);
- cw->conn_state = MAX(
- cw->conn_state, len);
- }
- break;
- }
- }
- }
- if (opt_S && s->proto == IPPROTO_TCP) {
- len = strlen(s->stack);
- cw->stack = MAX(cw->stack, len);
- }
- if (opt_C && s->proto == IPPROTO_TCP) {
- len = strlen(s->cc);
- cw->cc = MAX(cw->cc, len);
- }
- }
- if (laddr != NULL)
- laddr = laddr->next;
- if (faddr != NULL)
- faddr = faddr->next;
- first = false;
- }
-}
-
-static void
-calculate_column_widths(struct col_widths *cw)
-{
- int n, len;
- struct file *xf;
- struct sock *s;
- struct passwd *pwd;
-
- cap_setpassent(cappwd, 1);
- for (xf = files, n = 0; n < nfiles; ++n, ++xf) {
- if (xf->xf_data == 0)
- continue;
- if (opt_j >= 0 && opt_j != getprocjid(xf->xf_pid))
- continue;
- s = RB_FIND(socks_t, &socks,
- &(struct sock){ .socket = xf->xf_data});
- if (s == NULL || (!check_ports(s)))
- continue;
- s->shown = 1;
- if (opt_n ||
- (pwd = cap_getpwuid(cappwd, xf->xf_uid)) == NULL)
- len = snprintf(NULL, 0, "%lu", (u_long)xf->xf_uid);
- else
- len = snprintf(NULL, 0, "%s", pwd->pw_name);
- cw->user = MAX(cw->user, len);
- len = snprintf(NULL, 0, "%lu", (u_long)xf->xf_pid);
- cw->pid = MAX(cw->pid, len);
- len = snprintf(NULL, 0, "%d", xf->xf_fd);
- cw->fd = MAX(cw->fd, len);
-
- calculate_sock_column_widths(cw, s);
- }
- if (opt_j >= 0)
- return;
- SLIST_FOREACH(s, &nosocks, socket_list) {
- if (!check_ports(s))
- continue;
- calculate_sock_column_widths(cw, s);
- }
- RB_FOREACH(s, socks_t, &socks) {
- if (s->shown)
- continue;
- if (!check_ports(s))
- continue;
- calculate_sock_column_widths(cw, s);
- }
-}
-
-static void
-display_sock(struct sock *s, struct col_widths *cw, char *buf, size_t bufsize)
-{
- struct addr *laddr, *faddr;
- bool first;
- laddr = s->laddr;
- faddr = s->faddr;
- first = true;
- const bool is_text_style = (xo_get_style(NULL) == XO_STYLE_TEXT);
-
- snprintf(buf, bufsize, "%s%s%s",
- s->protoname,
- s->vflag & INP_IPV4 ? "4" : "",
- s->vflag & INP_IPV6 ? "6" : "");
- xo_emit(" {:proto/%-*s}", cw->proto, buf);
- while (laddr != NULL || faddr != NULL) {
- if (s->family == AF_UNIX) {
- if ((laddr == NULL) || (faddr == NULL))
- xo_errx(1, "laddr = %p or faddr = %p is NULL",
- (void *)laddr, (void *)faddr);
- if (laddr->address.ss_len > 0) {
- xo_open_container("local");
- formataddr(&laddr->address, buf, bufsize);
- if (is_text_style) {
- xo_emit(" {:/%-*.*s}", cw->local_addr,
- cw->local_addr, buf);
- }
- xo_close_container("local");
- } else if (laddr->address.ss_len == 0 &&
- faddr->conn == 0 && is_text_style) {
- xo_emit(" {:/%-*.*s}", cw->local_addr,
- cw->local_addr, "(not connected)");
- } else if (is_text_style) {
- xo_emit(" {:/%-*.*s}", cw->local_addr,
- cw->local_addr, "??");
- }
- if (faddr->conn != 0 || faddr->firstref != 0) {
- xo_open_container("foreign");
- int len = format_unix_faddr(faddr, buf,
- bufsize);
- if (len == 0 && is_text_style)
- xo_emit(" {:/%-*s}",
- cw->foreign_addr, "??");
- else if (is_text_style)
- xo_emit(" {:/%-*.*s}", cw->foreign_addr,
- cw->foreign_addr, buf);
- xo_close_container("foreign");
- } else if (is_text_style)
- xo_emit(" {:/%-*s}", cw->foreign_addr, "??");
- } else {
- if (laddr != NULL) {
- xo_open_container("local");
- formataddr(&laddr->address, buf, bufsize);
- if (is_text_style) {
- xo_emit(" {:/%-*.*s}", cw->local_addr,
- cw->local_addr, buf);
- }
- xo_close_container("local");
- } else if (is_text_style)
- xo_emit(" {:/%-*.*s}", cw->local_addr,
- cw->local_addr, "??");
- if (faddr != NULL) {
- xo_open_container("foreign");
- formataddr(&faddr->address, buf, bufsize);
- if (is_text_style) {
- xo_emit(" {:/%-*.*s}", cw->foreign_addr,
- cw->foreign_addr, buf);
- }
- xo_close_container("foreign");
- } else if (is_text_style) {
- xo_emit(" {:/%-*.*s}", cw->foreign_addr,
- cw->foreign_addr, "??");
- }
- }
- if (opt_A) {
- snprintf(buf, bufsize, "%#*" PRIx64,
- cw->pcb_kva, s->pcb);
- xo_emit(" {:pcb-kva/%s}", buf);
- }
- if (opt_f)
- xo_emit(" {:fib/%*d}", cw->fib, s->fibnum);
- if (opt_I) {
- if (s->splice_socket != 0) {
- struct sock *sp;
- sp = RB_FIND(socks_t, &socks, &(struct sock)
- { .socket = s->splice_socket });
- if (sp != NULL) {
- xo_open_container("splice");
- formataddr(&sp->laddr->address,
- buf, bufsize);
- xo_close_container("splice");
- } else if (is_text_style)
- strlcpy(buf, "??", bufsize);
- } else if (is_text_style)
- strlcpy(buf, "??", bufsize);
- if (is_text_style)
- xo_emit(" {:/%-*s}", cw->splice_address, buf);
- }
- if (opt_i) {
- if (s->proto == IPPROTO_TCP || s->proto == IPPROTO_UDP)
- {
- snprintf(buf, bufsize, "%" PRIu64,
- s->inp_gencnt);
- xo_emit(" {:id/%*s}", cw->inp_gencnt, buf);
- } else if (is_text_style)
- xo_emit(" {:/%*s}", cw->inp_gencnt, "??");
- }
- if (opt_U) {
- if (faddr != NULL &&
- ((s->proto == IPPROTO_SCTP &&
- s->state != SCTP_CLOSED &&
- s->state != SCTP_BOUND &&
- s->state != SCTP_LISTEN) ||
- (s->proto == IPPROTO_TCP &&
- s->state != TCPS_CLOSED &&
- s->state != TCPS_LISTEN))) {
- xo_emit(" {:encaps/%*u}", cw->encaps,
- ntohs(faddr->encaps_port));
- } else if (is_text_style)
- xo_emit(" {:/%*s}", cw->encaps, "??");
- }
- if (opt_s) {
- if (faddr != NULL &&
- s->proto == IPPROTO_SCTP &&
- s->state != SCTP_CLOSED &&
- s->state != SCTP_BOUND &&
- s->state != SCTP_LISTEN) {
- xo_emit(" {:path-state/%-*s}", cw->path_state,
- sctp_path_state(faddr->state));
- } else if (is_text_style)
- xo_emit(" {:/%-*s}", cw->path_state, "??");
- }
- if (first) {
- if (opt_s) {
- if (s->proto == IPPROTO_SCTP ||
- s->proto == IPPROTO_TCP) {
- switch (s->proto) {
- case IPPROTO_SCTP:
- xo_emit(" {:conn-state/%-*s}",
- cw->conn_state,
- sctp_conn_state(s->state));
- break;
- case IPPROTO_TCP:
- if (s->state >= 0 &&
- s->state < TCP_NSTATES)
- xo_emit(" {:conn-state/%-*s}",
- cw->conn_state,
- tcpstates[s->state]);
- else if (is_text_style)
- xo_emit(" {:/%-*s}",
- cw->conn_state, "??");
- break;
- }
- } else if (is_text_style)
- xo_emit(" {:/%-*s}",
- cw->conn_state, "??");
- }
- if (opt_S) {
- if (s->proto == IPPROTO_TCP)
- xo_emit(" {:stack/%-*s}",
- cw->stack, s->stack);
- else if (is_text_style)
- xo_emit(" {:/%-*s}",
- cw->stack, "??");
- }
- if (opt_C) {
- if (s->proto == IPPROTO_TCP)
- xo_emit(" {:cc/%-*s}", cw->cc, s->cc);
- else if (is_text_style)
- xo_emit(" {:/%-*s}", cw->cc, "??");
- }
- }
- if (laddr != NULL)
- laddr = laddr->next;
- if (faddr != NULL)
- faddr = faddr->next;
- if (is_text_style && (laddr != NULL || faddr != NULL))
- xo_emit("{:/%-*s} {:/%-*s} {:/%*s} {:/%*s}",
- cw->user, "??", cw->command, "??",
- cw->pid, "??", cw->fd, "??");
- first = false;
- }
- xo_emit("\n");
-}
-
-static void
-display(void)
-{
- struct passwd *pwd;
- struct file *xf;
- struct sock *s;
- int n;
- struct col_widths cw;
- const size_t bufsize = 512;
- void *buf;
- if ((buf = (char *)malloc(bufsize)) == NULL) {
- xo_err(1, "malloc()");
- return;
- }
-
- if (xo_get_style(NULL) == XO_STYLE_TEXT) {
- cw = (struct col_widths) {
- .user = strlen("USER"),
- .command = 10,
- .pid = strlen("PID"),
- .fd = strlen("FD"),
- .proto = strlen("PROTO"),
- .local_addr = opt_w ? strlen("LOCAL ADDRESS") : 21,
- .foreign_addr = opt_w ? strlen("FOREIGN ADDRESS") : 21,
- .pcb_kva = 18,
- .fib = strlen("FIB"),
- .splice_address = strlen("SPLICE ADDRESS"),
- .inp_gencnt = strlen("ID"),
- .encaps = strlen("ENCAPS"),
- .path_state = strlen("PATH STATE"),
- .conn_state = strlen("CONN STATE"),
- .stack = strlen("STACK"),
- .cc = strlen("CC"),
- };
- calculate_column_widths(&cw);
- } else
- memset(&cw, 0, sizeof(cw));
-
- xo_set_version(SOCKSTAT_XO_VERSION);
- xo_open_container("sockstat");
- xo_open_list("socket");
- if (!opt_q) {
- xo_emit("{T:/%-*s} {T:/%-*s} {T:/%*s} {T:/%*s} {T:/%-*s} "
- "{T:/%-*s} {T:/%-*s}", cw.user, "USER", cw.command,
- "COMMAND", cw.pid, "PID", cw.fd, "FD", cw.proto,
- "PROTO", cw.local_addr, "LOCAL ADDRESS",
- cw.foreign_addr, "FOREIGN ADDRESS");
- if (opt_A)
- xo_emit(" {T:/%-*s}", cw.pcb_kva, "PCB KVA");
- if (opt_f)
- /* RT_MAXFIBS is 65535. */
- xo_emit(" {T:/%*s}", cw.fib, "FIB");
- if (opt_I)
- xo_emit(" {T:/%-*s}", cw.splice_address,
- "SPLICE ADDRESS");
- if (opt_i)
- xo_emit(" {T:/%*s}", cw.inp_gencnt, "ID");
- if (opt_U)
- xo_emit(" {T:/%*s}", cw.encaps, "ENCAPS");
- if (opt_s) {
- xo_emit(" {T:/%-*s}", cw.path_state, "PATH STATE");
- xo_emit(" {T:/%-*s}", cw.conn_state, "CONN STATE");
- }
- if (opt_S)
- xo_emit(" {T:/%-*s}", cw.stack, "STACK");
- if (opt_C)
- xo_emit(" {T:/%-*s}", cw.cc, "CC");
- xo_emit("\n");
- }
- cap_setpassent(cappwd, 1);
- for (xf = files, n = 0; n < nfiles; ++n, ++xf) {
- if (xf->xf_data == 0)
- continue;
- if (opt_j >= 0 && opt_j != getprocjid(xf->xf_pid))
- continue;
- s = RB_FIND(socks_t, &socks,
- &(struct sock){ .socket = xf->xf_data});
- if (s != NULL && check_ports(s)) {
- xo_open_instance("socket");
- s->shown = 1;
- if (opt_n ||
- (pwd = cap_getpwuid(cappwd, xf->xf_uid)) == NULL)
- xo_emit("{:user/%-*lu}", cw.user,
- (u_long)xf->xf_uid);
- else
- xo_emit("{:user/%-*s}", cw.user, pwd->pw_name);
- if (xo_get_style(NULL) == XO_STYLE_TEXT)
- xo_emit(" {:/%-*.10s}", cw.command,
- getprocname(xf->xf_pid));
- else
- xo_emit(" {:command/%-*s}", cw.command,
- getprocname(xf->xf_pid));
- xo_emit(" {:pid/%*lu}", cw.pid, (u_long)xf->xf_pid);
- xo_emit(" {:fd/%*d}", cw.fd, xf->xf_fd);
- display_sock(s, &cw, buf, bufsize);
- xo_close_instance("socket");
- }
- }
- if (opt_j >= 0)
- return;
- SLIST_FOREACH(s, &nosocks, socket_list) {
- if (!check_ports(s))
- continue;
- xo_open_instance("socket");
- if (xo_get_style(NULL) == XO_STYLE_TEXT)
- xo_emit("{:/%-*s} {:/%-*s} {:/%*s} {:/%*s}",
- cw.user, "??", cw.command, "??",
- cw.pid, "??", cw.fd, "??");
- display_sock(s, &cw, buf, bufsize);
- xo_close_instance("socket");
- }
- RB_FOREACH(s, socks_t, &socks) {
- if (s->shown)
- continue;
- if (!check_ports(s))
- continue;
- xo_open_instance("socket");
- if (xo_get_style(NULL) == XO_STYLE_TEXT)
- xo_emit("{:/%-*s} {:/%-*s} {:/%*s} {:/%*s}",
- cw.user, "??", cw.command, "??",
- cw.pid, "??", cw.fd, "??");
- display_sock(s, &cw, buf, bufsize);
- xo_close_instance("socket");
- }
- xo_close_list("socket");
- xo_close_container("sockstat");
- if (xo_finish() < 0)
- xo_err(1, "stdout");
- free(buf);
- cap_endpwent(cappwd);
-}
-
-static int
-set_default_protos(void)
-{
- struct protoent *prot;
- const char *pname;
- size_t pindex;
-
- init_protos(default_numprotos);
-
- for (pindex = 0; pindex < default_numprotos; pindex++) {
- pname = default_protos[pindex];
- prot = cap_getprotobyname(capnetdb, pname);
- if (prot == NULL)
- xo_err(1, "cap_getprotobyname: %s", pname);
- protos[pindex] = prot->p_proto;
- }
- numprotos = pindex;
- return (pindex);
-}
-
-/*
- * Return the vnet property of the jail, or -1 on error.
- */
-static int
-jail_getvnet(int jid)
-{
- struct iovec jiov[6];
- int vnet;
- size_t len = sizeof(vnet);
-
- if (sysctlbyname("kern.features.vimage", &vnet, &len, NULL, 0) != 0)
- return (0);
-
- vnet = -1;
- jiov[0].iov_base = __DECONST(char *, "jid");
- jiov[0].iov_len = sizeof("jid");
- jiov[1].iov_base = &jid;
- jiov[1].iov_len = sizeof(jid);
- jiov[2].iov_base = __DECONST(char *, "vnet");
- jiov[2].iov_len = sizeof("vnet");
- jiov[3].iov_base = &vnet;
- jiov[3].iov_len = sizeof(vnet);
- jiov[4].iov_base = __DECONST(char *, "errmsg");
- jiov[4].iov_len = sizeof("errmsg");
- jiov[5].iov_base = jail_errmsg;
- jiov[5].iov_len = JAIL_ERRMSGLEN;
- jail_errmsg[0] = '\0';
- if (jail_get(jiov, nitems(jiov), 0) < 0) {
- if (!jail_errmsg[0])
- snprintf(jail_errmsg, JAIL_ERRMSGLEN,
- "jail_get: %s", strerror(errno));
- return (-1);
- }
- return (vnet);
-}
-
-static void
-usage(void)
-{
- xo_error(
-"usage: sockstat [--libxo] [-46ACcfIiLlnqSsUuvw] [-j jid] [-p ports]\n"
-" [-P protocols]\n");
- exit(1);
-}
-
-int
-main(int argc, char *argv[])
-{
- cap_channel_t *capcas;
- cap_net_limit_t *limit;
- const char *pwdcmds[] = { "setpassent", "getpwuid" };
- const char *pwdfields[] = { "pw_name" };
- int protos_defined = -1;
- int o, i;
-
- argc = xo_parse_args(argc, argv);
- if (argc < 0)
- exit(1);
- opt_j = -1;
- while ((o = getopt(argc, argv, "46ACcfIij:Llnp:P:qSsUuvw")) != -1)
- switch (o) {
- case '4':
- opt_4 = true;
- break;
- case '6':
- opt_6 = true;
- break;
- case 'A':
- opt_A = true;
- break;
- case 'C':
- opt_C = true;
- break;
- case 'c':
- opt_c = true;
- break;
- case 'f':
- opt_f = true;
- break;
- case 'I':
- opt_I = true;
- break;
- case 'i':
- opt_i = true;
- break;
- case 'j':
- opt_j = jail_getid(optarg);
- if (opt_j < 0)
- xo_errx(1, "jail_getid: %s", jail_errmsg);
- break;
- case 'L':
- opt_L = true;
- break;
- case 'l':
- opt_l = true;
- break;
- case 'n':
- opt_n = true;
- break;
- case 'p':
- parse_ports(optarg);
- break;
- case 'P':
- protos_defined = parse_protos(optarg);
- break;
- case 'q':
- opt_q = true;
- break;
- case 'S':
- opt_S = true;
- break;
- case 's':
- opt_s = true;
- break;
- case 'U':
- opt_U = true;
- break;
- case 'u':
- opt_u = true;
- break;
- case 'v':
- ++opt_v;
- break;
- case 'w':
- opt_w = true;
- break;
- default:
- usage();
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc > 0)
- usage();
-
- if (opt_j > 0) {
- switch (jail_getvnet(opt_j)) {
- case -1:
- xo_errx(2, "jail_getvnet: %s", jail_errmsg);
- case JAIL_SYS_NEW:
- if (jail_attach(opt_j) < 0)
- xo_err(3, "jail_attach()");
- /* Set back to -1 for normal output in vnet jail. */
- opt_j = -1;
- break;
- default:
- break;
- }
- }
-
- capcas = cap_init();
- if (capcas == NULL)
- xo_err(1, "Unable to contact Casper");
- if (caph_enter_casper() < 0)
- xo_err(1, "Unable to enter capability mode");
- capnet = cap_service_open(capcas, "system.net");
- if (capnet == NULL)
- xo_err(1, "Unable to open system.net service");
- capnetdb = cap_service_open(capcas, "system.netdb");
- if (capnetdb == NULL)
- xo_err(1, "Unable to open system.netdb service");
- capsysctl = cap_service_open(capcas, "system.sysctl");
- if (capsysctl == NULL)
- xo_err(1, "Unable to open system.sysctl service");
- cappwd = cap_service_open(capcas, "system.pwd");
- if (cappwd == NULL)
- xo_err(1, "Unable to open system.pwd service");
- cap_close(capcas);
- limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME);
- if (limit == NULL)
- xo_err(1, "Unable to init cap_net limits");
- if (cap_net_limit(limit) < 0)
- xo_err(1, "Unable to apply limits");
- if (cap_pwd_limit_cmds(cappwd, pwdcmds, nitems(pwdcmds)) < 0)
- xo_err(1, "Unable to apply pwd commands limits");
- if (cap_pwd_limit_fields(cappwd, pwdfields, nitems(pwdfields)) < 0)
- xo_err(1, "Unable to apply pwd commands limits");
-
- if ((!opt_4 && !opt_6) && protos_defined != -1)
- opt_4 = opt_6 = true;
- if (!opt_4 && !opt_6 && !opt_u)
- opt_4 = opt_6 = opt_u = true;
- if ((opt_4 || opt_6) && protos_defined == -1)
- protos_defined = set_default_protos();
- if (!opt_c && !opt_l)
- opt_c = opt_l = true;
-
- if (opt_4 || opt_6) {
- for (i = 0; i < protos_defined; i++)
- if (protos[i] == IPPROTO_SCTP)
- gather_sctp();
- else
- gather_inet(protos[i]);
- }
-
- if (opt_u || (protos_defined == -1 && !opt_4 && !opt_6)) {
- gather_unix(SOCK_STREAM);
- gather_unix(SOCK_DGRAM);
- gather_unix(SOCK_SEQPACKET);
- }
- getfiles();
- display();
- exit(0);
-}