xref: /dflybsd-src/usr.bin/sockstat/sockstat.c (revision 70bb9b563d99342f2f0a3c77d4451d0257dc06c5)
180cc79e1SJoerg Sonnenberger /*-
280cc79e1SJoerg Sonnenberger  * Copyright (c) 2005 Joerg Sonnenberger <joerg@bec.de>.  All rights reserved.
3f6e8a0a1SImre Vadasz  * Copyright (c) 2002 Dag-Erling Coïdan Smørgrav
480cc79e1SJoerg Sonnenberger  * All rights reserved.
580cc79e1SJoerg Sonnenberger  *
680cc79e1SJoerg Sonnenberger  * Redistribution and use in source and binary forms, with or without
780cc79e1SJoerg Sonnenberger  * modification, are permitted provided that the following conditions
880cc79e1SJoerg Sonnenberger  * are met:
980cc79e1SJoerg Sonnenberger  * 1. Redistributions of source code must retain the above copyright
1080cc79e1SJoerg Sonnenberger  *    notice, this list of conditions and the following disclaimer
1180cc79e1SJoerg Sonnenberger  *    in this position and unchanged.
1280cc79e1SJoerg Sonnenberger  * 2. Redistributions in binary form must reproduce the above copyright
1380cc79e1SJoerg Sonnenberger  *    notice, this list of conditions and the following disclaimer in the
1480cc79e1SJoerg Sonnenberger  *    documentation and/or other materials provided with the distribution.
1580cc79e1SJoerg Sonnenberger  * 3. The name of the author may not be used to endorse or promote products
1680cc79e1SJoerg Sonnenberger  *    derived from this software without specific prior written permission.
1780cc79e1SJoerg Sonnenberger  *
1880cc79e1SJoerg Sonnenberger  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1980cc79e1SJoerg Sonnenberger  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2080cc79e1SJoerg Sonnenberger  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2180cc79e1SJoerg Sonnenberger  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2280cc79e1SJoerg Sonnenberger  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2380cc79e1SJoerg Sonnenberger  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2480cc79e1SJoerg Sonnenberger  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2580cc79e1SJoerg Sonnenberger  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2680cc79e1SJoerg Sonnenberger  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2780cc79e1SJoerg Sonnenberger  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2880cc79e1SJoerg Sonnenberger  *
2980cc79e1SJoerg Sonnenberger  * $FreeBSD: src/usr.bin/sockstat/sockstat.c,v 1.12 2004/12/06 09:28:05 ru Exp $
3080cc79e1SJoerg Sonnenberger  */
3180cc79e1SJoerg Sonnenberger 
3280cc79e1SJoerg Sonnenberger #include <sys/param.h>
3380cc79e1SJoerg Sonnenberger #include <sys/socket.h>
3480cc79e1SJoerg Sonnenberger #include <sys/socketvar.h>
3580cc79e1SJoerg Sonnenberger #include <sys/sysctl.h>
3680cc79e1SJoerg Sonnenberger #include <sys/file.h>
3780cc79e1SJoerg Sonnenberger 
3880cc79e1SJoerg Sonnenberger #include <sys/un.h>
3980cc79e1SJoerg Sonnenberger #include <sys/unpcb.h>
4080cc79e1SJoerg Sonnenberger 
4180cc79e1SJoerg Sonnenberger #include <net/route.h>
4280cc79e1SJoerg Sonnenberger 
4380cc79e1SJoerg Sonnenberger #include <netinet/in.h>
4480cc79e1SJoerg Sonnenberger #include <netinet/in_pcb.h>
4580cc79e1SJoerg Sonnenberger #include <netinet/tcp.h>
4680cc79e1SJoerg Sonnenberger #include <netinet/tcp_seq.h>
4780cc79e1SJoerg Sonnenberger #include <netinet/tcp_var.h>
4880cc79e1SJoerg Sonnenberger #include <arpa/inet.h>
4980cc79e1SJoerg Sonnenberger 
5080cc79e1SJoerg Sonnenberger #include <ctype.h>
5180cc79e1SJoerg Sonnenberger #include <err.h>
5280cc79e1SJoerg Sonnenberger #include <errno.h>
5380cc79e1SJoerg Sonnenberger #include <kinfo.h>
5480cc79e1SJoerg Sonnenberger #include <netdb.h>
5580cc79e1SJoerg Sonnenberger #include <pwd.h>
5680cc79e1SJoerg Sonnenberger #include <stdarg.h>
57*70bb9b56SAaron LI #include <stddef.h>
5880cc79e1SJoerg Sonnenberger #include <stdio.h>
5980cc79e1SJoerg Sonnenberger #include <stdlib.h>
6080cc79e1SJoerg Sonnenberger #include <string.h>
6180cc79e1SJoerg Sonnenberger #include <unistd.h>
6280cc79e1SJoerg Sonnenberger 
6380cc79e1SJoerg Sonnenberger static int	 opt_4;		/* Show IPv4 sockets */
6480cc79e1SJoerg Sonnenberger static int	 opt_6;		/* Show IPv6 sockets */
6580cc79e1SJoerg Sonnenberger static int	 opt_c;		/* Show connected sockets */
6680cc79e1SJoerg Sonnenberger static int	 opt_l;		/* Show listening sockets */
6780cc79e1SJoerg Sonnenberger static int	 opt_u;		/* Show Unix domain sockets */
6880cc79e1SJoerg Sonnenberger static int	 opt_v;		/* Verbose mode */
6980cc79e1SJoerg Sonnenberger 
7080cc79e1SJoerg Sonnenberger static int	*ports;
7180cc79e1SJoerg Sonnenberger 
7280cc79e1SJoerg Sonnenberger #define INT_BIT (sizeof(int) * CHAR_BIT)
7380cc79e1SJoerg Sonnenberger #define SET_PORT(p) do { ports[p / INT_BIT] |= 1 << (p % INT_BIT); } while (0)
7480cc79e1SJoerg Sonnenberger #define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT)))
7580cc79e1SJoerg Sonnenberger 
7680cc79e1SJoerg Sonnenberger struct sock {
7780cc79e1SJoerg Sonnenberger 	void *socket;
7880cc79e1SJoerg Sonnenberger 	void *pcb;
7980cc79e1SJoerg Sonnenberger 	int family;
8080cc79e1SJoerg Sonnenberger 	int proto;
8180cc79e1SJoerg Sonnenberger 	const char *protoname;
8280cc79e1SJoerg Sonnenberger 	struct sockaddr_storage laddr;
8380cc79e1SJoerg Sonnenberger 	struct sockaddr_storage faddr;
8480cc79e1SJoerg Sonnenberger 	struct sock *next;
8580cc79e1SJoerg Sonnenberger };
8680cc79e1SJoerg Sonnenberger 
8780cc79e1SJoerg Sonnenberger #define HASHSIZE 1009
8880cc79e1SJoerg Sonnenberger static struct sock *sockhash[HASHSIZE];
8980cc79e1SJoerg Sonnenberger 
9080cc79e1SJoerg Sonnenberger static struct kinfo_file *xfiles;
91fd282d19SSascha Wildner static size_t nxfiles;
9280cc79e1SJoerg Sonnenberger 
93*70bb9b56SAaron LI __printflike(1, 2)
9480cc79e1SJoerg Sonnenberger static int
xprintf(const char * fmt,...)9580cc79e1SJoerg Sonnenberger xprintf(const char *fmt, ...)
9680cc79e1SJoerg Sonnenberger {
9780cc79e1SJoerg Sonnenberger 	va_list ap;
9880cc79e1SJoerg Sonnenberger 	int len;
9980cc79e1SJoerg Sonnenberger 
10080cc79e1SJoerg Sonnenberger 	va_start(ap, fmt);
10180cc79e1SJoerg Sonnenberger 	len = vprintf(fmt, ap);
10280cc79e1SJoerg Sonnenberger 	va_end(ap);
10380cc79e1SJoerg Sonnenberger 	if (len < 0)
10480cc79e1SJoerg Sonnenberger 		err(1, "printf()");
10580cc79e1SJoerg Sonnenberger 	return (len);
10680cc79e1SJoerg Sonnenberger }
10780cc79e1SJoerg Sonnenberger 
10880cc79e1SJoerg Sonnenberger static void
parse_ports(const char * portspec)10980cc79e1SJoerg Sonnenberger parse_ports(const char *portspec)
11080cc79e1SJoerg Sonnenberger {
11180cc79e1SJoerg Sonnenberger 	const char *p, *q;
11280cc79e1SJoerg Sonnenberger 	int port, end;
11380cc79e1SJoerg Sonnenberger 
11480cc79e1SJoerg Sonnenberger 	if (ports == NULL)
11580cc79e1SJoerg Sonnenberger 		if ((ports = calloc(65536 / INT_BIT, sizeof(int))) == NULL)
11680cc79e1SJoerg Sonnenberger 			err(1, "calloc()");
11780cc79e1SJoerg Sonnenberger 	p = portspec;
11880cc79e1SJoerg Sonnenberger 	while (*p != '\0') {
11980cc79e1SJoerg Sonnenberger 		if (!isdigit(*p))
12080cc79e1SJoerg Sonnenberger 			errx(1, "syntax error in port range");
12180cc79e1SJoerg Sonnenberger 		for (q = p; *q != '\0' && isdigit(*q); ++q)
12280cc79e1SJoerg Sonnenberger 			/* nothing */ ;
12380cc79e1SJoerg Sonnenberger 		for (port = 0; p < q; ++p)
124c4f3b183SJoerg Sonnenberger 			port = port * 10 + (*p - '0');
12580cc79e1SJoerg Sonnenberger 		if (port < 0 || port > 65535)
12680cc79e1SJoerg Sonnenberger 			errx(1, "invalid port number");
12780cc79e1SJoerg Sonnenberger 		SET_PORT(port);
12880cc79e1SJoerg Sonnenberger 		switch (*p) {
12980cc79e1SJoerg Sonnenberger 		case '-':
13080cc79e1SJoerg Sonnenberger 			++p;
13180cc79e1SJoerg Sonnenberger 			break;
13280cc79e1SJoerg Sonnenberger 		case ',':
13380cc79e1SJoerg Sonnenberger 			++p;
13480cc79e1SJoerg Sonnenberger 			/* fall through */
13580cc79e1SJoerg Sonnenberger 		case '\0':
13680cc79e1SJoerg Sonnenberger 		default:
13780cc79e1SJoerg Sonnenberger 			continue;
13880cc79e1SJoerg Sonnenberger 		}
13980cc79e1SJoerg Sonnenberger 		for (q = p; *q != '\0' && isdigit(*q); ++q)
14080cc79e1SJoerg Sonnenberger 			/* nothing */ ;
14180cc79e1SJoerg Sonnenberger 		for (end = 0; p < q; ++p)
142c4f3b183SJoerg Sonnenberger 			end = end * 10 + (*p - '0');
14380cc79e1SJoerg Sonnenberger 		if (end < port || end > 65535)
14480cc79e1SJoerg Sonnenberger 			errx(1, "invalid port number");
14580cc79e1SJoerg Sonnenberger 		while (port++ < end)
14680cc79e1SJoerg Sonnenberger 			SET_PORT(port);
14780cc79e1SJoerg Sonnenberger 		if (*p == ',')
14880cc79e1SJoerg Sonnenberger 			++p;
14980cc79e1SJoerg Sonnenberger 	}
15080cc79e1SJoerg Sonnenberger }
15180cc79e1SJoerg Sonnenberger 
15280cc79e1SJoerg Sonnenberger static void
sockaddr(struct sockaddr_storage * sa,int af,void * addr,int port)15380cc79e1SJoerg Sonnenberger sockaddr(struct sockaddr_storage *sa, int af, void *addr, int port)
15480cc79e1SJoerg Sonnenberger {
15580cc79e1SJoerg Sonnenberger 	struct sockaddr_in *sin4;
15680cc79e1SJoerg Sonnenberger 	struct sockaddr_in6 *sin6;
15780cc79e1SJoerg Sonnenberger 
15880cc79e1SJoerg Sonnenberger 	bzero(sa, sizeof *sa);
15980cc79e1SJoerg Sonnenberger 	switch (af) {
16080cc79e1SJoerg Sonnenberger 	case AF_INET:
16180cc79e1SJoerg Sonnenberger 		sin4 = (struct sockaddr_in *)sa;
16280cc79e1SJoerg Sonnenberger 		sin4->sin_len = sizeof *sin4;
16380cc79e1SJoerg Sonnenberger 		sin4->sin_family = af;
16480cc79e1SJoerg Sonnenberger 		sin4->sin_port = port;
16580cc79e1SJoerg Sonnenberger 		sin4->sin_addr = *(struct in_addr *)addr;
16680cc79e1SJoerg Sonnenberger 		break;
16780cc79e1SJoerg Sonnenberger 	case AF_INET6:
16880cc79e1SJoerg Sonnenberger 		sin6 = (struct sockaddr_in6 *)sa;
16980cc79e1SJoerg Sonnenberger 		sin6->sin6_len = sizeof *sin6;
17080cc79e1SJoerg Sonnenberger 		sin6->sin6_family = af;
17180cc79e1SJoerg Sonnenberger 		sin6->sin6_port = port;
17280cc79e1SJoerg Sonnenberger 		sin6->sin6_addr = *(struct in6_addr *)addr;
17380cc79e1SJoerg Sonnenberger 		break;
17480cc79e1SJoerg Sonnenberger 	default:
17580cc79e1SJoerg Sonnenberger 		abort();
17680cc79e1SJoerg Sonnenberger 	}
17780cc79e1SJoerg Sonnenberger }
17880cc79e1SJoerg Sonnenberger 
17980cc79e1SJoerg Sonnenberger static void
gather_inet(int proto)18080cc79e1SJoerg Sonnenberger gather_inet(int proto)
18180cc79e1SJoerg Sonnenberger {
182*70bb9b56SAaron LI 	uint8_t *buf, *so_begin, *so_end;
18380cc79e1SJoerg Sonnenberger 	struct xinpcb *xip;
18480cc79e1SJoerg Sonnenberger 	struct xtcpcb *xtp;
18580cc79e1SJoerg Sonnenberger 	struct inpcb *inp;
18680cc79e1SJoerg Sonnenberger 	struct xsocket *so;
18780cc79e1SJoerg Sonnenberger 	struct sock *sock;
18880cc79e1SJoerg Sonnenberger 	const char *varname, *protoname;
18980cc79e1SJoerg Sonnenberger 	size_t len;
190727ccde8SSepherosa Ziehau 	int hash;
19180cc79e1SJoerg Sonnenberger 
19280cc79e1SJoerg Sonnenberger 	switch (proto) {
19380cc79e1SJoerg Sonnenberger 	case IPPROTO_TCP:
19480cc79e1SJoerg Sonnenberger 		varname = "net.inet.tcp.pcblist";
19580cc79e1SJoerg Sonnenberger 		protoname = "tcp";
19680cc79e1SJoerg Sonnenberger 		break;
19780cc79e1SJoerg Sonnenberger 	case IPPROTO_UDP:
19880cc79e1SJoerg Sonnenberger 		varname = "net.inet.udp.pcblist";
19980cc79e1SJoerg Sonnenberger 		protoname = "udp";
20080cc79e1SJoerg Sonnenberger 		break;
20180cc79e1SJoerg Sonnenberger 	case IPPROTO_DIVERT:
20280cc79e1SJoerg Sonnenberger 		varname = "net.inet.divert.pcblist";
20380cc79e1SJoerg Sonnenberger 		protoname = "div";
20480cc79e1SJoerg Sonnenberger 		break;
20580cc79e1SJoerg Sonnenberger 	default:
20680cc79e1SJoerg Sonnenberger 		abort();
20780cc79e1SJoerg Sonnenberger 	}
20880cc79e1SJoerg Sonnenberger 
20980cc79e1SJoerg Sonnenberger 	buf = NULL;
21080cc79e1SJoerg Sonnenberger 	len = 0;
21180cc79e1SJoerg Sonnenberger 
21280cc79e1SJoerg Sonnenberger 	if (sysctlbyname(varname, NULL, &len, NULL, 0)) {
21380cc79e1SJoerg Sonnenberger 		if (errno == ENOENT)
21480cc79e1SJoerg Sonnenberger 			goto out;
21580cc79e1SJoerg Sonnenberger 		err(1, "fetching %s", varname);
21680cc79e1SJoerg Sonnenberger 	}
21780cc79e1SJoerg Sonnenberger 	if ((buf = malloc(len)) == NULL)
21880cc79e1SJoerg Sonnenberger 		err(1, "malloc()");
21980cc79e1SJoerg Sonnenberger 	if (sysctlbyname(varname, buf, &len, NULL, 0)) {
22080cc79e1SJoerg Sonnenberger 		if (errno == ENOENT)
22180cc79e1SJoerg Sonnenberger 			goto out;
22280cc79e1SJoerg Sonnenberger 		err(1, "fetching %s", varname);
22380cc79e1SJoerg Sonnenberger 	}
22480cc79e1SJoerg Sonnenberger 
225*70bb9b56SAaron LI 	for (so_begin = buf, so_end = buf + len;
226*70bb9b56SAaron LI 	     so_begin + sizeof(size_t) < so_end &&
227*70bb9b56SAaron LI 	     so_begin + *(size_t *)so_begin <= so_end;
228*70bb9b56SAaron LI 	     so_begin = so_begin + *(size_t *)so_begin) {
22980cc79e1SJoerg Sonnenberger 		switch (proto) {
23080cc79e1SJoerg Sonnenberger 		case IPPROTO_TCP:
23180cc79e1SJoerg Sonnenberger 			xtp = (struct xtcpcb *)so_begin;
23280cc79e1SJoerg Sonnenberger 			if (xtp->xt_len != sizeof *xtp) {
23380cc79e1SJoerg Sonnenberger 				warnx("struct xtcpcb size mismatch");
23480cc79e1SJoerg Sonnenberger 				goto out;
23580cc79e1SJoerg Sonnenberger 			}
23680cc79e1SJoerg Sonnenberger 			inp = &xtp->xt_inp;
23780cc79e1SJoerg Sonnenberger 			so = &xtp->xt_socket;
23880cc79e1SJoerg Sonnenberger 			break;
23980cc79e1SJoerg Sonnenberger 		case IPPROTO_UDP:
24080cc79e1SJoerg Sonnenberger 		case IPPROTO_DIVERT:
24180cc79e1SJoerg Sonnenberger 			xip = (struct xinpcb *)so_begin;
24280cc79e1SJoerg Sonnenberger 			if (xip->xi_len != sizeof *xip) {
24380cc79e1SJoerg Sonnenberger 				warnx("struct xinpcb size mismatch");
24480cc79e1SJoerg Sonnenberger 				goto out;
24580cc79e1SJoerg Sonnenberger 			}
24680cc79e1SJoerg Sonnenberger 			inp = &xip->xi_inp;
24780cc79e1SJoerg Sonnenberger 			so = &xip->xi_socket;
24880cc79e1SJoerg Sonnenberger 			break;
24980cc79e1SJoerg Sonnenberger 		default:
25080cc79e1SJoerg Sonnenberger 			abort();
25180cc79e1SJoerg Sonnenberger 		}
252727ccde8SSepherosa Ziehau 		if ((INP_ISIPV4(inp) && !opt_4) || (INP_ISIPV6(inp) && !opt_6))
25380cc79e1SJoerg Sonnenberger 			continue;
254727ccde8SSepherosa Ziehau 		if (INP_ISIPV4(inp)) {
25580cc79e1SJoerg Sonnenberger 			if ((inp->inp_fport == 0 && !opt_l) ||
25680cc79e1SJoerg Sonnenberger 			    (inp->inp_fport != 0 && !opt_c))
25780cc79e1SJoerg Sonnenberger 				continue;
258727ccde8SSepherosa Ziehau 		} else if (INP_ISIPV6(inp)) {
25980cc79e1SJoerg Sonnenberger 			if ((inp->in6p_fport == 0 && !opt_l) ||
26080cc79e1SJoerg Sonnenberger 			    (inp->in6p_fport != 0 && !opt_c))
26180cc79e1SJoerg Sonnenberger 				continue;
26280cc79e1SJoerg Sonnenberger 		} else {
26380cc79e1SJoerg Sonnenberger 			if (opt_v)
264727ccde8SSepherosa Ziehau 				warnx("invalid af 0x%x", inp->inp_af);
26580cc79e1SJoerg Sonnenberger 			continue;
26680cc79e1SJoerg Sonnenberger 		}
26780cc79e1SJoerg Sonnenberger 		if ((sock = calloc(1, sizeof *sock)) == NULL)
26880cc79e1SJoerg Sonnenberger 			err(1, "malloc()");
26980cc79e1SJoerg Sonnenberger 		sock->socket = so->xso_so;
27080cc79e1SJoerg Sonnenberger 		sock->proto = proto;
271727ccde8SSepherosa Ziehau 		if (INP_ISIPV4(inp)) {
27280cc79e1SJoerg Sonnenberger 			sock->family = AF_INET;
27380cc79e1SJoerg Sonnenberger 			sockaddr(&sock->laddr, sock->family,
27480cc79e1SJoerg Sonnenberger 			    &inp->inp_laddr, inp->inp_lport);
27580cc79e1SJoerg Sonnenberger 			sockaddr(&sock->faddr, sock->family,
27680cc79e1SJoerg Sonnenberger 			    &inp->inp_faddr, inp->inp_fport);
277727ccde8SSepherosa Ziehau 		} else if (INP_ISIPV6(inp)) {
27880cc79e1SJoerg Sonnenberger 			sock->family = AF_INET6;
27980cc79e1SJoerg Sonnenberger 			sockaddr(&sock->laddr, sock->family,
28080cc79e1SJoerg Sonnenberger 			    &inp->in6p_laddr, inp->in6p_lport);
28180cc79e1SJoerg Sonnenberger 			sockaddr(&sock->faddr, sock->family,
28280cc79e1SJoerg Sonnenberger 			    &inp->in6p_faddr, inp->in6p_fport);
28380cc79e1SJoerg Sonnenberger 		}
28480cc79e1SJoerg Sonnenberger 		sock->protoname = protoname;
28580cc79e1SJoerg Sonnenberger 		hash = (int)((uintptr_t)sock->socket % HASHSIZE);
28680cc79e1SJoerg Sonnenberger 		sock->next = sockhash[hash];
28780cc79e1SJoerg Sonnenberger 		sockhash[hash] = sock;
28880cc79e1SJoerg Sonnenberger 	}
28980cc79e1SJoerg Sonnenberger out:
29080cc79e1SJoerg Sonnenberger 	free(buf);
29180cc79e1SJoerg Sonnenberger }
29280cc79e1SJoerg Sonnenberger 
29380cc79e1SJoerg Sonnenberger static void
gather_unix(int proto)29480cc79e1SJoerg Sonnenberger gather_unix(int proto)
29580cc79e1SJoerg Sonnenberger {
296*70bb9b56SAaron LI 	uint8_t *buf, *so_begin, *so_end;
29780cc79e1SJoerg Sonnenberger 	struct xunpcb *xup;
29880cc79e1SJoerg Sonnenberger 	struct sock *sock;
29980cc79e1SJoerg Sonnenberger 	const char *varname, *protoname;
30080cc79e1SJoerg Sonnenberger 	size_t len;
30180cc79e1SJoerg Sonnenberger 	int hash;
30280cc79e1SJoerg Sonnenberger 
30380cc79e1SJoerg Sonnenberger 	switch (proto) {
30480cc79e1SJoerg Sonnenberger 	case SOCK_STREAM:
30580cc79e1SJoerg Sonnenberger 		varname = "net.local.stream.pcblist";
30680cc79e1SJoerg Sonnenberger 		protoname = "stream";
30780cc79e1SJoerg Sonnenberger 		break;
30880cc79e1SJoerg Sonnenberger 	case SOCK_DGRAM:
30980cc79e1SJoerg Sonnenberger 		varname = "net.local.dgram.pcblist";
31080cc79e1SJoerg Sonnenberger 		protoname = "dgram";
31180cc79e1SJoerg Sonnenberger 		break;
31280cc79e1SJoerg Sonnenberger 	default:
31380cc79e1SJoerg Sonnenberger 		abort();
31480cc79e1SJoerg Sonnenberger 	}
31580cc79e1SJoerg Sonnenberger 
31680cc79e1SJoerg Sonnenberger 	buf = NULL;
31780cc79e1SJoerg Sonnenberger 	len = 0;
31880cc79e1SJoerg Sonnenberger 
31980cc79e1SJoerg Sonnenberger 	if (sysctlbyname(varname, NULL, &len, NULL, 0))
32080cc79e1SJoerg Sonnenberger 		err(1, "fetching %s", varname);
32180cc79e1SJoerg Sonnenberger 
32280cc79e1SJoerg Sonnenberger 	if ((buf = malloc(len)) == NULL)
32380cc79e1SJoerg Sonnenberger 		err(1, "malloc()");
32480cc79e1SJoerg Sonnenberger 	if (sysctlbyname(varname, buf, &len, NULL, 0))
32580cc79e1SJoerg Sonnenberger 		err(1, "fetching %s", varname);
32680cc79e1SJoerg Sonnenberger 
327*70bb9b56SAaron LI 	for (so_begin = buf, so_end = buf + len;
328*70bb9b56SAaron LI 	     so_begin + sizeof(size_t) < so_end &&
329*70bb9b56SAaron LI 	     so_begin + *(size_t *)so_begin <= so_end;
330*70bb9b56SAaron LI 	     so_begin = so_begin + *(size_t *)so_begin) {
331*70bb9b56SAaron LI 		xup = (struct xunpcb *)so_begin;
33280cc79e1SJoerg Sonnenberger 		if (xup->xu_len != sizeof *xup) {
33380cc79e1SJoerg Sonnenberger 			warnx("struct xunpcb size mismatch");
33480cc79e1SJoerg Sonnenberger 			goto out;
33580cc79e1SJoerg Sonnenberger 		}
33680cc79e1SJoerg Sonnenberger 		if ((xup->xu_unp.unp_conn == NULL && !opt_l) ||
33780cc79e1SJoerg Sonnenberger 		    (xup->xu_unp.unp_conn != NULL && !opt_c))
33880cc79e1SJoerg Sonnenberger 			continue;
33980cc79e1SJoerg Sonnenberger 		if ((sock = calloc(1, sizeof *sock)) == NULL)
34080cc79e1SJoerg Sonnenberger 			err(1, "malloc()");
34180cc79e1SJoerg Sonnenberger 		sock->socket = xup->xu_socket.xso_so;
34280cc79e1SJoerg Sonnenberger 		sock->pcb = xup->xu_unpp;
34380cc79e1SJoerg Sonnenberger 		sock->proto = proto;
34480cc79e1SJoerg Sonnenberger 		sock->family = AF_UNIX;
34580cc79e1SJoerg Sonnenberger 		sock->protoname = protoname;
34680cc79e1SJoerg Sonnenberger 		if (xup->xu_unp.unp_addr != NULL)
347*70bb9b56SAaron LI 			sock->laddr = *(struct sockaddr_storage *)&xup->xu_addr;
34880cc79e1SJoerg Sonnenberger 		else if (xup->xu_unp.unp_conn != NULL)
34980cc79e1SJoerg Sonnenberger 			*(void **)&sock->faddr = xup->xu_unp.unp_conn;
35080cc79e1SJoerg Sonnenberger 		hash = (int)((uintptr_t)sock->socket % HASHSIZE);
35180cc79e1SJoerg Sonnenberger 		sock->next = sockhash[hash];
35280cc79e1SJoerg Sonnenberger 		sockhash[hash] = sock;
35380cc79e1SJoerg Sonnenberger 	}
35480cc79e1SJoerg Sonnenberger out:
35580cc79e1SJoerg Sonnenberger 	free(buf);
35680cc79e1SJoerg Sonnenberger }
35780cc79e1SJoerg Sonnenberger 
35880cc79e1SJoerg Sonnenberger static void
getfiles(void)35980cc79e1SJoerg Sonnenberger getfiles(void)
36080cc79e1SJoerg Sonnenberger {
36180cc79e1SJoerg Sonnenberger 	if (kinfo_get_files(&xfiles, &nxfiles))
36280cc79e1SJoerg Sonnenberger 		err(1, "kinfo_get_files");
36380cc79e1SJoerg Sonnenberger }
36480cc79e1SJoerg Sonnenberger 
365*70bb9b56SAaron LI static void
printproto(int width,int af,const char * protoname)366*70bb9b56SAaron LI printproto(int width, int af, const char *protoname)
367*70bb9b56SAaron LI {
368*70bb9b56SAaron LI 	int n;
369*70bb9b56SAaron LI 
370*70bb9b56SAaron LI 	switch (af) {
371*70bb9b56SAaron LI 	case AF_INET:
372*70bb9b56SAaron LI 	case AF_INET6:
373*70bb9b56SAaron LI 		n = xprintf("%s%c", protoname, af == AF_INET ? '4' : '6');
374*70bb9b56SAaron LI 		break;
375*70bb9b56SAaron LI 	default:
376*70bb9b56SAaron LI 		n = xprintf("%s", protoname);
377*70bb9b56SAaron LI 		break;
378*70bb9b56SAaron LI 	}
379*70bb9b56SAaron LI 
380*70bb9b56SAaron LI 	if (width > 0 && width > n)
381*70bb9b56SAaron LI 		xprintf("%*s", width - n, "");
382*70bb9b56SAaron LI }
383*70bb9b56SAaron LI 
384*70bb9b56SAaron LI static void
printaddr(int width,int af,struct sockaddr_storage * ss)385*70bb9b56SAaron LI printaddr(int width, int af, struct sockaddr_storage *ss)
38680cc79e1SJoerg Sonnenberger {
38780cc79e1SJoerg Sonnenberger 	char addrstr[INET6_ADDRSTRLEN] = { '\0', '\0' };
38880cc79e1SJoerg Sonnenberger 	struct sockaddr_un *sun;
38980cc79e1SJoerg Sonnenberger 	void *addr;
390*70bb9b56SAaron LI 	int port, n;
391*70bb9b56SAaron LI 
392*70bb9b56SAaron LI 	if (af == AF_UNIX) {
393*70bb9b56SAaron LI 		sun = (struct sockaddr_un *)ss;
394*70bb9b56SAaron LI 		n = sun->sun_len - offsetof(struct sockaddr_un, sun_path);
395*70bb9b56SAaron LI 		xprintf("%.*s", n, sun->sun_path);
396*70bb9b56SAaron LI 		if (width > 0 && width > n)
397*70bb9b56SAaron LI 			xprintf("%*s", width - n, "");
398*70bb9b56SAaron LI 		return;
399*70bb9b56SAaron LI 	}
40080cc79e1SJoerg Sonnenberger 
40180cc79e1SJoerg Sonnenberger 	switch (af) {
40280cc79e1SJoerg Sonnenberger 	case AF_INET:
40380cc79e1SJoerg Sonnenberger 		addr = &((struct sockaddr_in *)ss)->sin_addr;
40480cc79e1SJoerg Sonnenberger 		if (inet_lnaof(*(struct in_addr *)addr) == INADDR_ANY)
40580cc79e1SJoerg Sonnenberger 			addrstr[0] = '*';
40680cc79e1SJoerg Sonnenberger 		port = ntohs(((struct sockaddr_in *)ss)->sin_port);
40780cc79e1SJoerg Sonnenberger 		break;
40880cc79e1SJoerg Sonnenberger 	case AF_INET6:
40980cc79e1SJoerg Sonnenberger 		addr = &((struct sockaddr_in6 *)ss)->sin6_addr;
41080cc79e1SJoerg Sonnenberger 		if (IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *)addr))
41180cc79e1SJoerg Sonnenberger 			addrstr[0] = '*';
41280cc79e1SJoerg Sonnenberger 		port = ntohs(((struct sockaddr_in6 *)ss)->sin6_port);
41380cc79e1SJoerg Sonnenberger 		break;
414ae1a9ca5SJoerg Sonnenberger 	default:
415ae1a9ca5SJoerg Sonnenberger 		abort();
41680cc79e1SJoerg Sonnenberger 	}
417*70bb9b56SAaron LI 
41880cc79e1SJoerg Sonnenberger 	if (addrstr[0] == '\0')
41980cc79e1SJoerg Sonnenberger 		inet_ntop(af, addr, addrstr, sizeof addrstr);
42080cc79e1SJoerg Sonnenberger 	if (port == 0)
421*70bb9b56SAaron LI 		n = xprintf("%s:*", addrstr);
42280cc79e1SJoerg Sonnenberger 	else
423*70bb9b56SAaron LI 		n = xprintf("%s:%d", addrstr, port);
424*70bb9b56SAaron LI 	if (width > 0 && width > n)
425*70bb9b56SAaron LI 		xprintf("%*s", width - n, "");
42680cc79e1SJoerg Sonnenberger }
42780cc79e1SJoerg Sonnenberger 
42880cc79e1SJoerg Sonnenberger static const char *
getprocname(pid_t pid)42980cc79e1SJoerg Sonnenberger getprocname(pid_t pid)
43080cc79e1SJoerg Sonnenberger {
43180cc79e1SJoerg Sonnenberger 	static struct kinfo_proc proc;
43280cc79e1SJoerg Sonnenberger 	size_t len;
43380cc79e1SJoerg Sonnenberger 	int mib[4];
43480cc79e1SJoerg Sonnenberger 
43580cc79e1SJoerg Sonnenberger 	mib[0] = CTL_KERN;
43680cc79e1SJoerg Sonnenberger 	mib[1] = KERN_PROC;
43780cc79e1SJoerg Sonnenberger 	mib[2] = KERN_PROC_PID;
43880cc79e1SJoerg Sonnenberger 	mib[3] = (int)pid;
43980cc79e1SJoerg Sonnenberger 	len = sizeof proc;
44080cc79e1SJoerg Sonnenberger 	if (sysctl(mib, 4, &proc, &len, NULL, 0) == -1) {
44180cc79e1SJoerg Sonnenberger 		warn("sysctl()");
44280cc79e1SJoerg Sonnenberger 		return ("??");
44380cc79e1SJoerg Sonnenberger 	}
4445dfd06acSSimon Schubert 	return (proc.kp_comm);
44580cc79e1SJoerg Sonnenberger }
44680cc79e1SJoerg Sonnenberger 
44780cc79e1SJoerg Sonnenberger static int
check_ports(struct sock * s)44880cc79e1SJoerg Sonnenberger check_ports(struct sock *s)
44980cc79e1SJoerg Sonnenberger {
45080cc79e1SJoerg Sonnenberger 	int port;
45180cc79e1SJoerg Sonnenberger 
45280cc79e1SJoerg Sonnenberger 	if (ports == NULL)
45380cc79e1SJoerg Sonnenberger 		return (1);
45480cc79e1SJoerg Sonnenberger 	if ((s->family != AF_INET) && (s->family != AF_INET6))
45580cc79e1SJoerg Sonnenberger 		return (1);
45680cc79e1SJoerg Sonnenberger 	if (s->family == AF_INET)
45780cc79e1SJoerg Sonnenberger 		port = ntohs(((struct sockaddr_in *)(&s->laddr))->sin_port);
45880cc79e1SJoerg Sonnenberger 	else
45980cc79e1SJoerg Sonnenberger 		port = ntohs(((struct sockaddr_in6 *)(&s->laddr))->sin6_port);
46080cc79e1SJoerg Sonnenberger 	if (CHK_PORT(port))
46180cc79e1SJoerg Sonnenberger 		return (1);
46280cc79e1SJoerg Sonnenberger 	if (s->family == AF_INET)
46380cc79e1SJoerg Sonnenberger 		port = ntohs(((struct sockaddr_in *)(&s->faddr))->sin_port);
46480cc79e1SJoerg Sonnenberger 	else
46580cc79e1SJoerg Sonnenberger 		port = ntohs(((struct sockaddr_in6 *)(&s->faddr))->sin6_port);
46680cc79e1SJoerg Sonnenberger 	if (CHK_PORT(port))
46780cc79e1SJoerg Sonnenberger 		return (1);
46880cc79e1SJoerg Sonnenberger 	return (0);
46980cc79e1SJoerg Sonnenberger }
47080cc79e1SJoerg Sonnenberger 
47180cc79e1SJoerg Sonnenberger static void
display(void)47280cc79e1SJoerg Sonnenberger display(void)
47380cc79e1SJoerg Sonnenberger {
47480cc79e1SJoerg Sonnenberger 	struct passwd *pwd;
47580cc79e1SJoerg Sonnenberger 	struct kinfo_file *xf;
47680cc79e1SJoerg Sonnenberger 	struct sock *s;
47780cc79e1SJoerg Sonnenberger 	void *p;
478*70bb9b56SAaron LI 	int hash, n;
47980cc79e1SJoerg Sonnenberger 
480*70bb9b56SAaron LI 	printf("%-8s %-10s %6s %5s %-6s %-21s %-21s\n",
48180cc79e1SJoerg Sonnenberger 	    "USER", "COMMAND", "PID", "FD", "PROTO",
48280cc79e1SJoerg Sonnenberger 	    "LOCAL ADDRESS", "FOREIGN ADDRESS");
48380cc79e1SJoerg Sonnenberger 	setpassent(1);
48418c5de8bSSascha Wildner 	for (xf = xfiles, n = 0; n < (int)nxfiles; ++n, ++xf) {
48580cc79e1SJoerg Sonnenberger 		if (xf->f_data == NULL)
48680cc79e1SJoerg Sonnenberger 			continue;
48780cc79e1SJoerg Sonnenberger 		hash = (int)((uintptr_t)xf->f_data % HASHSIZE);
48880cc79e1SJoerg Sonnenberger 		for (s = sockhash[hash]; s != NULL; s = s->next)
48980cc79e1SJoerg Sonnenberger 			if ((void *)s->socket == xf->f_data)
49080cc79e1SJoerg Sonnenberger 				break;
49180cc79e1SJoerg Sonnenberger 		if (s == NULL)
49280cc79e1SJoerg Sonnenberger 			continue;
49380cc79e1SJoerg Sonnenberger 		if (!check_ports(s))
49480cc79e1SJoerg Sonnenberger 			continue;
495*70bb9b56SAaron LI 		if ((pwd = getpwuid(xf->f_uid)) != NULL)
496*70bb9b56SAaron LI 			xprintf("%-8.8s ", pwd->pw_name);
49780cc79e1SJoerg Sonnenberger 		else
498*70bb9b56SAaron LI 			xprintf("%-8lu ", (u_long)xf->f_uid);
499*70bb9b56SAaron LI 		xprintf("%-10.10s ", getprocname(xf->f_pid));
500*70bb9b56SAaron LI 		xprintf("%6lu ", (u_long)xf->f_pid);
501*70bb9b56SAaron LI 		xprintf("%5d ", xf->f_fd);
502*70bb9b56SAaron LI 		printproto(6, s->family, s->protoname);
503*70bb9b56SAaron LI 		xprintf(" ");
50480cc79e1SJoerg Sonnenberger 		switch (s->family) {
50580cc79e1SJoerg Sonnenberger 		case AF_INET:
50680cc79e1SJoerg Sonnenberger 		case AF_INET6:
507*70bb9b56SAaron LI 			printaddr(21, s->family, &s->laddr);
508*70bb9b56SAaron LI 			xprintf(" ");
509*70bb9b56SAaron LI 			printaddr(0, s->family, &s->faddr);
51080cc79e1SJoerg Sonnenberger 			break;
51180cc79e1SJoerg Sonnenberger 		case AF_UNIX:
51280cc79e1SJoerg Sonnenberger 			/* server */
51380cc79e1SJoerg Sonnenberger 			if (s->laddr.ss_len > 0) {
514*70bb9b56SAaron LI 				printaddr(0, s->family, &s->laddr);
51580cc79e1SJoerg Sonnenberger 				break;
51680cc79e1SJoerg Sonnenberger 			}
51780cc79e1SJoerg Sonnenberger 			/* client */
51880cc79e1SJoerg Sonnenberger 			p = *(void **)&s->faddr;
51980cc79e1SJoerg Sonnenberger 			if (p == NULL) {
520*70bb9b56SAaron LI 				xprintf("(not connected)");
52180cc79e1SJoerg Sonnenberger 				break;
52280cc79e1SJoerg Sonnenberger 			}
523*70bb9b56SAaron LI 			xprintf("-> ");
52480cc79e1SJoerg Sonnenberger 			for (hash = 0; hash < HASHSIZE; ++hash) {
52580cc79e1SJoerg Sonnenberger 				for (s = sockhash[hash]; s != NULL; s = s->next)
52680cc79e1SJoerg Sonnenberger 					if (s->pcb == p)
52780cc79e1SJoerg Sonnenberger 						break;
52880cc79e1SJoerg Sonnenberger 				if (s != NULL)
52980cc79e1SJoerg Sonnenberger 					break;
53080cc79e1SJoerg Sonnenberger 			}
53180cc79e1SJoerg Sonnenberger 			if (s == NULL || s->laddr.ss_len == 0)
532*70bb9b56SAaron LI 				xprintf("??");
53380cc79e1SJoerg Sonnenberger 			else
534*70bb9b56SAaron LI 				printaddr(0, s->family, &s->laddr);
53580cc79e1SJoerg Sonnenberger 			break;
53680cc79e1SJoerg Sonnenberger 		default:
53780cc79e1SJoerg Sonnenberger 			abort();
53880cc79e1SJoerg Sonnenberger 		}
53980cc79e1SJoerg Sonnenberger 		xprintf("\n");
54080cc79e1SJoerg Sonnenberger 	}
54180cc79e1SJoerg Sonnenberger }
54280cc79e1SJoerg Sonnenberger 
54380cc79e1SJoerg Sonnenberger static void
usage(void)54480cc79e1SJoerg Sonnenberger usage(void)
54580cc79e1SJoerg Sonnenberger {
54680cc79e1SJoerg Sonnenberger 	fprintf(stderr, "Usage: sockstat [-46clu] [-p ports]\n");
54780cc79e1SJoerg Sonnenberger 	exit(1);
54880cc79e1SJoerg Sonnenberger }
54980cc79e1SJoerg Sonnenberger 
55080cc79e1SJoerg Sonnenberger int
main(int argc,char * argv[])55180cc79e1SJoerg Sonnenberger main(int argc, char *argv[])
55280cc79e1SJoerg Sonnenberger {
55380cc79e1SJoerg Sonnenberger 	int o;
55480cc79e1SJoerg Sonnenberger 
55580cc79e1SJoerg Sonnenberger 	while ((o = getopt(argc, argv, "46clp:uv")) != -1)
55680cc79e1SJoerg Sonnenberger 		switch (o) {
55780cc79e1SJoerg Sonnenberger 		case '4':
55880cc79e1SJoerg Sonnenberger 			opt_4 = 1;
55980cc79e1SJoerg Sonnenberger 			break;
56080cc79e1SJoerg Sonnenberger 		case '6':
56180cc79e1SJoerg Sonnenberger 			opt_6 = 1;
56280cc79e1SJoerg Sonnenberger 			break;
56380cc79e1SJoerg Sonnenberger 		case 'c':
56480cc79e1SJoerg Sonnenberger 			opt_c = 1;
56580cc79e1SJoerg Sonnenberger 			break;
56680cc79e1SJoerg Sonnenberger 		case 'l':
56780cc79e1SJoerg Sonnenberger 			opt_l = 1;
56880cc79e1SJoerg Sonnenberger 			break;
56980cc79e1SJoerg Sonnenberger 		case 'p':
57080cc79e1SJoerg Sonnenberger 			parse_ports(optarg);
57180cc79e1SJoerg Sonnenberger 			break;
57280cc79e1SJoerg Sonnenberger 		case 'u':
57380cc79e1SJoerg Sonnenberger 			opt_u = 1;
57480cc79e1SJoerg Sonnenberger 			break;
57580cc79e1SJoerg Sonnenberger 		case 'v':
57680cc79e1SJoerg Sonnenberger 			++opt_v;
57780cc79e1SJoerg Sonnenberger 			break;
57880cc79e1SJoerg Sonnenberger 		default:
57980cc79e1SJoerg Sonnenberger 			usage();
58080cc79e1SJoerg Sonnenberger 		}
58180cc79e1SJoerg Sonnenberger 
58280cc79e1SJoerg Sonnenberger 	argc -= optind;
58380cc79e1SJoerg Sonnenberger 	argv += optind;
58480cc79e1SJoerg Sonnenberger 
58580cc79e1SJoerg Sonnenberger 	if (argc > 0)
58680cc79e1SJoerg Sonnenberger 		usage();
58780cc79e1SJoerg Sonnenberger 
58880cc79e1SJoerg Sonnenberger 	if (!opt_4 && !opt_6 && !opt_u)
58980cc79e1SJoerg Sonnenberger 		opt_4 = opt_6 = opt_u = 1;
59080cc79e1SJoerg Sonnenberger 	if (!opt_c && !opt_l)
59180cc79e1SJoerg Sonnenberger 		opt_c = opt_l = 1;
59280cc79e1SJoerg Sonnenberger 
59380cc79e1SJoerg Sonnenberger 	if (opt_4 || opt_6) {
59480cc79e1SJoerg Sonnenberger 		gather_inet(IPPROTO_TCP);
59580cc79e1SJoerg Sonnenberger 		gather_inet(IPPROTO_UDP);
59680cc79e1SJoerg Sonnenberger 		gather_inet(IPPROTO_DIVERT);
59780cc79e1SJoerg Sonnenberger 	}
59880cc79e1SJoerg Sonnenberger 	if (opt_u) {
59980cc79e1SJoerg Sonnenberger 		gather_unix(SOCK_STREAM);
60080cc79e1SJoerg Sonnenberger 		gather_unix(SOCK_DGRAM);
60180cc79e1SJoerg Sonnenberger 	}
60280cc79e1SJoerg Sonnenberger 	getfiles();
60380cc79e1SJoerg Sonnenberger 	display();
60480cc79e1SJoerg Sonnenberger 
60580cc79e1SJoerg Sonnenberger 	exit(0);
60680cc79e1SJoerg Sonnenberger }
607