1101043Sdes/*-
2228991Suqs * Copyright (c) 2002 Dag-Erling Co��dan Sm��rgrav
3101043Sdes * All rights reserved.
4101043Sdes *
5101043Sdes * Redistribution and use in source and binary forms, with or without
6101043Sdes * modification, are permitted provided that the following conditions
7101043Sdes * are met:
8101043Sdes * 1. Redistributions of source code must retain the above copyright
9101043Sdes *    notice, this list of conditions and the following disclaimer
10101043Sdes *    in this position and unchanged.
11101043Sdes * 2. Redistributions in binary form must reproduce the above copyright
12101043Sdes *    notice, this list of conditions and the following disclaimer in the
13101043Sdes *    documentation and/or other materials provided with the distribution.
14101043Sdes * 3. The name of the author may not be used to endorse or promote products
15101043Sdes *    derived from this software without specific prior written permission.
16101043Sdes *
17101043Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18101043Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19101043Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20101043Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21101043Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22101043Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23101043Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24101043Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25101043Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26101043Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27101043Sdes */
28101043Sdes
29101043Sdes#include <sys/cdefs.h>
30101043Sdes__FBSDID("$FreeBSD$");
31101043Sdes
32101043Sdes#include <sys/param.h>
33101043Sdes#include <sys/socket.h>
34101043Sdes#include <sys/socketvar.h>
35101043Sdes#include <sys/sysctl.h>
36101043Sdes#include <sys/file.h>
37101043Sdes#include <sys/user.h>
38101043Sdes
39101043Sdes#include <sys/un.h>
40101043Sdes#include <sys/unpcb.h>
41101043Sdes
42101163Sdes#include <net/route.h>
43101163Sdes
44101043Sdes#include <netinet/in.h>
45101043Sdes#include <netinet/in_pcb.h>
46101043Sdes#include <netinet/tcp.h>
47101043Sdes#include <netinet/tcp_seq.h>
48101043Sdes#include <netinet/tcp_var.h>
49101043Sdes#include <arpa/inet.h>
50101043Sdes
51101043Sdes#include <ctype.h>
52101043Sdes#include <err.h>
53101043Sdes#include <errno.h>
54101043Sdes#include <netdb.h>
55101043Sdes#include <pwd.h>
56101043Sdes#include <stdarg.h>
57101043Sdes#include <stdio.h>
58101043Sdes#include <stdlib.h>
59101043Sdes#include <string.h>
60101043Sdes#include <unistd.h>
61101043Sdes
62101043Sdesstatic int	 opt_4;		/* Show IPv4 sockets */
63101043Sdesstatic int	 opt_6;		/* Show IPv6 sockets */
64101043Sdesstatic int	 opt_c;		/* Show connected sockets */
65235870Sthompsastatic int	 opt_j;		/* Show specified jail */
66179115Sbmsstatic int	 opt_L;		/* Don't show IPv4 or IPv6 loopback sockets */
67101043Sdesstatic int	 opt_l;		/* Show listening sockets */
68101043Sdesstatic int	 opt_u;		/* Show Unix domain sockets */
69101043Sdesstatic int	 opt_v;		/* Verbose mode */
70101043Sdes
71164201Skeramida/*
72164201Skeramida * Default protocols to use if no -P was defined.
73164201Skeramida */
74164201Skeramidastatic const char *default_protos[] = {"tcp", "udp", "divert" };
75164201Skeramidastatic size_t	   default_numprotos =
76164201Skeramida    sizeof(default_protos) / sizeof(default_protos[0]);
77164201Skeramida
78164201Skeramidastatic int	*protos;	/* protocols to use */
79164201Skeramidastatic size_t	 numprotos;	/* allocated size of protos[] */
80164201Skeramida
81101043Sdesstatic int	*ports;
82101043Sdes
83101043Sdes#define INT_BIT (sizeof(int)*CHAR_BIT)
84101043Sdes#define SET_PORT(p) do { ports[p / INT_BIT] |= 1 << (p % INT_BIT); } while (0)
85101043Sdes#define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT)))
86101043Sdes
87101043Sdesstruct sock {
88101043Sdes	void *socket;
89101043Sdes	void *pcb;
90230512Sjilles	int shown;
91101043Sdes	int vflag;
92101043Sdes	int family;
93101043Sdes	int proto;
94101043Sdes	const char *protoname;
95101043Sdes	struct sockaddr_storage laddr;
96101043Sdes	struct sockaddr_storage faddr;
97101043Sdes	struct sock *next;
98101043Sdes};
99101043Sdes
100101043Sdes#define HASHSIZE 1009
101101043Sdesstatic struct sock *sockhash[HASHSIZE];
102101043Sdes
103101043Sdesstatic struct xfile *xfiles;
104101043Sdesstatic int nxfiles;
105101043Sdes
106101043Sdesstatic int
107101043Sdesxprintf(const char *fmt, ...)
108101043Sdes{
109101043Sdes	va_list ap;
110101043Sdes	int len;
111101043Sdes
112101043Sdes	va_start(ap, fmt);
113101043Sdes	len = vprintf(fmt, ap);
114101043Sdes	va_end(ap);
115101043Sdes	if (len < 0)
116101043Sdes		err(1, "printf()");
117101043Sdes	return (len);
118101043Sdes}
119101043Sdes
120164201Skeramida
121164201Skeramidastatic int
122164201Skeramidaget_proto_type(const char *proto)
123164201Skeramida{
124164201Skeramida	struct protoent *pent;
125164201Skeramida
126164201Skeramida	if (strlen(proto) == 0)
127164201Skeramida		return (0);
128164201Skeramida	pent = getprotobyname(proto);
129164201Skeramida	if (pent == NULL) {
130164201Skeramida		warn("getprotobyname");
131164201Skeramida		return (-1);
132164201Skeramida	}
133164201Skeramida	return (pent->p_proto);
134164201Skeramida}
135164201Skeramida
136164201Skeramida
137164201Skeramidastatic void init_protos(int num)
138164201Skeramida{
139164201Skeramida	int proto_count = 0;
140164201Skeramida
141164201Skeramida	if (num > 0) {
142164201Skeramida		proto_count = num;
143164201Skeramida	} else {
144164201Skeramida		/* Find the maximum number of possible protocols. */
145164201Skeramida		while (getprotoent() != NULL)
146164201Skeramida			proto_count++;
147164201Skeramida		endprotoent();
148164201Skeramida	}
149164201Skeramida
150164201Skeramida	if ((protos = malloc(sizeof(int) * proto_count)) == NULL)
151164201Skeramida		err(1, "malloc");
152164201Skeramida	numprotos = proto_count;
153164201Skeramida}
154164201Skeramida
155164201Skeramida
156164201Skeramidastatic int
157164201Skeramidaparse_protos(char *protospec)
158164201Skeramida{
159164201Skeramida	char *prot;
160164201Skeramida	char *tmp = protospec;
161164201Skeramida	int proto_type, proto_index;
162164201Skeramida
163164201Skeramida	if (protospec == NULL)
164164201Skeramida		return (-1);
165164201Skeramida
166164201Skeramida	init_protos(0);
167164201Skeramida	proto_index = 0;
168164201Skeramida	while ((prot = strsep(&tmp, ",")) != NULL) {
169164201Skeramida		if (strlen(prot) == 0)
170164201Skeramida			continue;
171164201Skeramida		proto_type = get_proto_type(prot);
172164201Skeramida		if (proto_type != -1)
173164201Skeramida			protos[proto_index++] = proto_type;
174164201Skeramida	}
175164201Skeramida	numprotos = proto_index;
176164201Skeramida	return (proto_index);
177164201Skeramida}
178164201Skeramida
179164201Skeramida
180101043Sdesstatic void
181101043Sdesparse_ports(const char *portspec)
182101043Sdes{
183101043Sdes	const char *p, *q;
184101043Sdes	int port, end;
185101043Sdes
186101043Sdes	if (ports == NULL)
187114853Srobert		if ((ports = calloc(65536 / INT_BIT, sizeof(int))) == NULL)
188101043Sdes			err(1, "calloc()");
189101043Sdes	p = portspec;
190101043Sdes	while (*p != '\0') {
191101043Sdes		if (!isdigit(*p))
192101043Sdes			errx(1, "syntax error in port range");
193101043Sdes		for (q = p; *q != '\0' && isdigit(*q); ++q)
194101043Sdes			/* nothing */ ;
195101043Sdes		for (port = 0; p < q; ++p)
196101043Sdes			port = port * 10 + digittoint(*p);
197101043Sdes		if (port < 0 || port > 65535)
198101043Sdes			errx(1, "invalid port number");
199101043Sdes		SET_PORT(port);
200101043Sdes		switch (*p) {
201101043Sdes		case '-':
202101043Sdes			++p;
203101043Sdes			break;
204101043Sdes		case ',':
205101043Sdes			++p;
206101043Sdes			/* fall through */
207101043Sdes		case '\0':
208101043Sdes		default:
209101043Sdes			continue;
210101043Sdes		}
211101043Sdes		for (q = p; *q != '\0' && isdigit(*q); ++q)
212101043Sdes			/* nothing */ ;
213101043Sdes		for (end = 0; p < q; ++p)
214101043Sdes			end = end * 10 + digittoint(*p);
215101043Sdes		if (end < port || end > 65535)
216101043Sdes			errx(1, "invalid port number");
217101043Sdes		while (port++ < end)
218101043Sdes			SET_PORT(port);
219101043Sdes		if (*p == ',')
220101043Sdes			++p;
221101043Sdes	}
222101043Sdes}
223101043Sdes
224101043Sdesstatic void
225101043Sdessockaddr(struct sockaddr_storage *sa, int af, void *addr, int port)
226101043Sdes{
227101043Sdes	struct sockaddr_in *sin4;
228101043Sdes	struct sockaddr_in6 *sin6;
229101043Sdes
230101043Sdes	bzero(sa, sizeof *sa);
231101043Sdes	switch (af) {
232101043Sdes	case AF_INET:
233101043Sdes		sin4 = (struct sockaddr_in *)sa;
234101043Sdes		sin4->sin_len = sizeof *sin4;
235101043Sdes		sin4->sin_family = af;
236101043Sdes		sin4->sin_port = port;
237101043Sdes		sin4->sin_addr = *(struct in_addr *)addr;
238101043Sdes		break;
239101043Sdes	case AF_INET6:
240101043Sdes		sin6 = (struct sockaddr_in6 *)sa;
241101043Sdes		sin6->sin6_len = sizeof *sin6;
242101043Sdes		sin6->sin6_family = af;
243101043Sdes		sin6->sin6_port = port;
244101043Sdes		sin6->sin6_addr = *(struct in6_addr *)addr;
245101043Sdes		break;
246101043Sdes	default:
247101043Sdes		abort();
248101043Sdes	}
249101043Sdes}
250101043Sdes
251101043Sdesstatic void
252101043Sdesgather_inet(int proto)
253101043Sdes{
254101043Sdes	struct xinpgen *xig, *exig;
255101043Sdes	struct xinpcb *xip;
256101043Sdes	struct xtcpcb *xtp;
257101043Sdes	struct inpcb *inp;
258101043Sdes	struct xsocket *so;
259101043Sdes	struct sock *sock;
260101043Sdes	const char *varname, *protoname;
261101043Sdes	size_t len, bufsize;
262101043Sdes	void *buf;
263101043Sdes	int hash, retry, vflag;
264101043Sdes
265101043Sdes	vflag = 0;
266101043Sdes	if (opt_4)
267101043Sdes		vflag |= INP_IPV4;
268101043Sdes	if (opt_6)
269101043Sdes		vflag |= INP_IPV6;
270101043Sdes
271101043Sdes	switch (proto) {
272101043Sdes	case IPPROTO_TCP:
273101043Sdes		varname = "net.inet.tcp.pcblist";
274101043Sdes		protoname = "tcp";
275101043Sdes		break;
276101043Sdes	case IPPROTO_UDP:
277101043Sdes		varname = "net.inet.udp.pcblist";
278101043Sdes		protoname = "udp";
279101043Sdes		break;
280138391Sru	case IPPROTO_DIVERT:
281138391Sru		varname = "net.inet.divert.pcblist";
282138391Sru		protoname = "div";
283138391Sru		break;
284101043Sdes	default:
285164201Skeramida		errx(1, "protocol %d not supported", proto);
286101043Sdes	}
287101043Sdes
288101043Sdes	buf = NULL;
289101043Sdes	bufsize = 8192;
290101043Sdes	retry = 5;
291101043Sdes	do {
292101043Sdes		for (;;) {
293101043Sdes			if ((buf = realloc(buf, bufsize)) == NULL)
294101043Sdes				err(1, "realloc()");
295101043Sdes			len = bufsize;
296101043Sdes			if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
297101043Sdes				break;
298138437Sru			if (errno == ENOENT)
299138437Sru				goto out;
300230874Strociny			if (errno != ENOMEM || len != bufsize)
301101043Sdes				err(1, "sysctlbyname()");
302101043Sdes			bufsize *= 2;
303101043Sdes		}
304101043Sdes		xig = (struct xinpgen *)buf;
305101220Srobert		exig = (struct xinpgen *)(void *)
306101220Srobert		    ((char *)buf + len - sizeof *exig);
307101043Sdes		if (xig->xig_len != sizeof *xig ||
308101043Sdes		    exig->xig_len != sizeof *exig)
309101043Sdes			errx(1, "struct xinpgen size mismatch");
310101043Sdes	} while (xig->xig_gen != exig->xig_gen && retry--);
311101043Sdes
312101043Sdes	if (xig->xig_gen != exig->xig_gen && opt_v)
313101043Sdes		warnx("warning: data may be inconsistent");
314101043Sdes
315101043Sdes	for (;;) {
316101220Srobert		xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len);
317101043Sdes		if (xig >= exig)
318101043Sdes			break;
319101043Sdes		switch (proto) {
320101043Sdes		case IPPROTO_TCP:
321101043Sdes			xtp = (struct xtcpcb *)xig;
322101043Sdes			if (xtp->xt_len != sizeof *xtp) {
323101043Sdes				warnx("struct xtcpcb size mismatch");
324101043Sdes				goto out;
325101043Sdes			}
326101043Sdes			inp = &xtp->xt_inp;
327101043Sdes			so = &xtp->xt_socket;
328237263Snp			protoname = xtp->xt_tp.t_flags & TF_TOE ? "toe" : "tcp";
329101043Sdes			break;
330101043Sdes		case IPPROTO_UDP:
331138391Sru		case IPPROTO_DIVERT:
332101043Sdes			xip = (struct xinpcb *)xig;
333101043Sdes			if (xip->xi_len != sizeof *xip) {
334101043Sdes				warnx("struct xinpcb size mismatch");
335101043Sdes				goto out;
336101043Sdes			}
337101043Sdes			inp = &xip->xi_inp;
338101043Sdes			so = &xip->xi_socket;
339101043Sdes			break;
340101043Sdes		default:
341164201Skeramida			errx(1, "protocol %d not supported", proto);
342101043Sdes		}
343101043Sdes		if ((inp->inp_vflag & vflag) == 0)
344101043Sdes			continue;
345101144Sdes		if (inp->inp_vflag & INP_IPV4) {
346101144Sdes			if ((inp->inp_fport == 0 && !opt_l) ||
347101144Sdes			    (inp->inp_fport != 0 && !opt_c))
348101144Sdes				continue;
349179115Sbms#define __IN_IS_ADDR_LOOPBACK(pina) \
350179115Sbms	((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
351179115Sbms			if (opt_L &&
352179115Sbms			    (__IN_IS_ADDR_LOOPBACK(&inp->inp_faddr) ||
353179115Sbms			     __IN_IS_ADDR_LOOPBACK(&inp->inp_laddr)))
354179115Sbms				continue;
355179115Sbms#undef __IN_IS_ADDR_LOOPBACK
356101144Sdes		} else if (inp->inp_vflag & INP_IPV6) {
357189637Srwatson			if ((inp->inp_fport == 0 && !opt_l) ||
358189637Srwatson			    (inp->inp_fport != 0 && !opt_c))
359101144Sdes				continue;
360179115Sbms			if (opt_L &&
361179115Sbms			    (IN6_IS_ADDR_LOOPBACK(&inp->in6p_faddr) ||
362179115Sbms			     IN6_IS_ADDR_LOOPBACK(&inp->in6p_laddr)))
363179115Sbms				continue;
364101144Sdes		} else {
365101144Sdes			if (opt_v)
366101144Sdes				warnx("invalid vflag 0x%x", inp->inp_vflag);
367101144Sdes			continue;
368101144Sdes		}
369101043Sdes		if ((sock = calloc(1, sizeof *sock)) == NULL)
370101043Sdes			err(1, "malloc()");
371101043Sdes		sock->socket = so->xso_so;
372101043Sdes		sock->proto = proto;
373101043Sdes		if (inp->inp_vflag & INP_IPV4) {
374101043Sdes			sock->family = AF_INET;
375101043Sdes			sockaddr(&sock->laddr, sock->family,
376101043Sdes			    &inp->inp_laddr, inp->inp_lport);
377101043Sdes			sockaddr(&sock->faddr, sock->family,
378101043Sdes			    &inp->inp_faddr, inp->inp_fport);
379101043Sdes		} else if (inp->inp_vflag & INP_IPV6) {
380101043Sdes			sock->family = AF_INET6;
381101043Sdes			sockaddr(&sock->laddr, sock->family,
382189637Srwatson			    &inp->in6p_laddr, inp->inp_lport);
383101043Sdes			sockaddr(&sock->faddr, sock->family,
384189637Srwatson			    &inp->in6p_faddr, inp->inp_fport);
385101043Sdes		}
386101043Sdes		sock->vflag = inp->inp_vflag;
387101043Sdes		sock->protoname = protoname;
388101043Sdes		hash = (int)((uintptr_t)sock->socket % HASHSIZE);
389101043Sdes		sock->next = sockhash[hash];
390101043Sdes		sockhash[hash] = sock;
391101043Sdes	}
392101043Sdesout:
393101043Sdes	free(buf);
394101043Sdes}
395101043Sdes
396101043Sdesstatic void
397101043Sdesgather_unix(int proto)
398101043Sdes{
399101043Sdes	struct xunpgen *xug, *exug;
400101043Sdes	struct xunpcb *xup;
401101043Sdes	struct sock *sock;
402101043Sdes	const char *varname, *protoname;
403101043Sdes	size_t len, bufsize;
404101043Sdes	void *buf;
405101043Sdes	int hash, retry;
406101043Sdes
407101043Sdes	switch (proto) {
408101043Sdes	case SOCK_STREAM:
409101043Sdes		varname = "net.local.stream.pcblist";
410101043Sdes		protoname = "stream";
411101043Sdes		break;
412101043Sdes	case SOCK_DGRAM:
413101043Sdes		varname = "net.local.dgram.pcblist";
414101043Sdes		protoname = "dgram";
415101043Sdes		break;
416101043Sdes	default:
417101043Sdes		abort();
418101043Sdes	}
419101043Sdes	buf = NULL;
420101043Sdes	bufsize = 8192;
421101043Sdes	retry = 5;
422101043Sdes	do {
423101043Sdes		for (;;) {
424101043Sdes			if ((buf = realloc(buf, bufsize)) == NULL)
425101043Sdes				err(1, "realloc()");
426101043Sdes			len = bufsize;
427101043Sdes			if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
428101043Sdes				break;
429230874Strociny			if (errno != ENOMEM || len != bufsize)
430101043Sdes				err(1, "sysctlbyname()");
431101043Sdes			bufsize *= 2;
432101043Sdes		}
433101043Sdes		xug = (struct xunpgen *)buf;
434101220Srobert		exug = (struct xunpgen *)(void *)
435101220Srobert		    ((char *)buf + len - sizeof *exug);
436101043Sdes		if (xug->xug_len != sizeof *xug ||
437101043Sdes		    exug->xug_len != sizeof *exug) {
438101043Sdes			warnx("struct xinpgen size mismatch");
439101043Sdes			goto out;
440101043Sdes		}
441101043Sdes	} while (xug->xug_gen != exug->xug_gen && retry--);
442101043Sdes
443101043Sdes	if (xug->xug_gen != exug->xug_gen && opt_v)
444101043Sdes		warnx("warning: data may be inconsistent");
445101043Sdes
446101043Sdes	for (;;) {
447101220Srobert		xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len);
448101043Sdes		if (xug >= exug)
449101043Sdes			break;
450101043Sdes		xup = (struct xunpcb *)xug;
451101043Sdes		if (xup->xu_len != sizeof *xup) {
452101043Sdes			warnx("struct xunpcb size mismatch");
453101043Sdes			goto out;
454101043Sdes		}
455101144Sdes		if ((xup->xu_unp.unp_conn == NULL && !opt_l) ||
456101144Sdes		    (xup->xu_unp.unp_conn != NULL && !opt_c))
457101144Sdes			continue;
458101043Sdes		if ((sock = calloc(1, sizeof *sock)) == NULL)
459101043Sdes			err(1, "malloc()");
460101043Sdes		sock->socket = xup->xu_socket.xso_so;
461101043Sdes		sock->pcb = xup->xu_unpp;
462101043Sdes		sock->proto = proto;
463101043Sdes		sock->family = AF_UNIX;
464101043Sdes		sock->protoname = protoname;
465101043Sdes		if (xup->xu_unp.unp_addr != NULL)
466101220Srobert			sock->laddr =
467101220Srobert			    *(struct sockaddr_storage *)(void *)&xup->xu_addr;
468101043Sdes		else if (xup->xu_unp.unp_conn != NULL)
469101043Sdes			*(void **)&sock->faddr = xup->xu_unp.unp_conn;
470101043Sdes		hash = (int)((uintptr_t)sock->socket % HASHSIZE);
471101043Sdes		sock->next = sockhash[hash];
472101043Sdes		sockhash[hash] = sock;
473101043Sdes	}
474101043Sdesout:
475101043Sdes	free(buf);
476101043Sdes}
477101043Sdes
478101043Sdesstatic void
479101043Sdesgetfiles(void)
480101043Sdes{
481230874Strociny	size_t len, olen;
482101043Sdes
483230874Strociny	olen = len = sizeof *xfiles;
484230874Strociny	if ((xfiles = malloc(len)) == NULL)
485101043Sdes		err(1, "malloc()");
486101043Sdes	while (sysctlbyname("kern.file", xfiles, &len, 0, 0) == -1) {
487230874Strociny		if (errno != ENOMEM || len != olen)
488101043Sdes			err(1, "sysctlbyname()");
489230874Strociny		olen = len *= 2;
490101043Sdes		if ((xfiles = realloc(xfiles, len)) == NULL)
491101043Sdes			err(1, "realloc()");
492101043Sdes	}
493101043Sdes	if (len > 0 && xfiles->xf_size != sizeof *xfiles)
494101043Sdes		errx(1, "struct xfile size mismatch");
495101043Sdes	nxfiles = len / sizeof *xfiles;
496101043Sdes}
497101043Sdes
498101043Sdesstatic int
499101043Sdesprintaddr(int af, struct sockaddr_storage *ss)
500101043Sdes{
501101043Sdes	char addrstr[INET6_ADDRSTRLEN] = { '\0', '\0' };
502101043Sdes	struct sockaddr_un *sun;
503201385Sed	void *addr = NULL; /* Keep compiler happy. */
504201385Sed	int off, port = 0;
505101043Sdes
506101043Sdes	switch (af) {
507101043Sdes	case AF_INET:
508101043Sdes		addr = &((struct sockaddr_in *)ss)->sin_addr;
509101043Sdes		if (inet_lnaof(*(struct in_addr *)addr) == INADDR_ANY)
510101043Sdes			addrstr[0] = '*';
511101043Sdes		port = ntohs(((struct sockaddr_in *)ss)->sin_port);
512101043Sdes		break;
513101043Sdes	case AF_INET6:
514101043Sdes		addr = &((struct sockaddr_in6 *)ss)->sin6_addr;
515101043Sdes		if (IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *)addr))
516101043Sdes			addrstr[0] = '*';
517101043Sdes		port = ntohs(((struct sockaddr_in6 *)ss)->sin6_port);
518101043Sdes		break;
519101043Sdes	case AF_UNIX:
520101043Sdes		sun = (struct sockaddr_un *)ss;
521101043Sdes		off = (int)((char *)&sun->sun_path - (char *)sun);
522101043Sdes		return (xprintf("%.*s", sun->sun_len - off, sun->sun_path));
523101043Sdes	}
524101043Sdes	if (addrstr[0] == '\0')
525101043Sdes		inet_ntop(af, addr, addrstr, sizeof addrstr);
526101043Sdes	if (port == 0)
527101043Sdes		return xprintf("%s:*", addrstr);
528101043Sdes	else
529101043Sdes		return xprintf("%s:%d", addrstr, port);
530101043Sdes}
531101043Sdes
532101043Sdesstatic const char *
533101043Sdesgetprocname(pid_t pid)
534101043Sdes{
535101043Sdes	static struct kinfo_proc proc;
536101043Sdes	size_t len;
537101043Sdes	int mib[4];
538101043Sdes
539101043Sdes	mib[0] = CTL_KERN;
540101043Sdes	mib[1] = KERN_PROC;
541101043Sdes	mib[2] = KERN_PROC_PID;
542101043Sdes	mib[3] = (int)pid;
543101043Sdes	len = sizeof proc;
544101043Sdes	if (sysctl(mib, 4, &proc, &len, NULL, 0) == -1) {
545170833Smaxim		/* Do not warn if the process exits before we get its name. */
546170833Smaxim		if (errno != ESRCH)
547170833Smaxim			warn("sysctl()");
548101043Sdes		return ("??");
549101043Sdes	}
550167810Semaste	return (proc.ki_comm);
551101043Sdes}
552101043Sdes
553115955Smuxstatic int
554235870Sthompsagetprocjid(pid_t pid)
555235870Sthompsa{
556235870Sthompsa	static struct kinfo_proc proc;
557235870Sthompsa	size_t len;
558235870Sthompsa	int mib[4];
559235870Sthompsa
560235870Sthompsa	mib[0] = CTL_KERN;
561235870Sthompsa	mib[1] = KERN_PROC;
562235870Sthompsa	mib[2] = KERN_PROC_PID;
563235870Sthompsa	mib[3] = (int)pid;
564235870Sthompsa	len = sizeof proc;
565235870Sthompsa	if (sysctl(mib, 4, &proc, &len, NULL, 0) == -1) {
566235870Sthompsa		/* Do not warn if the process exits before we get its jid. */
567235870Sthompsa		if (errno != ESRCH)
568235870Sthompsa			warn("sysctl()");
569235870Sthompsa		return (-1);
570235870Sthompsa	}
571235870Sthompsa	return (proc.ki_jid);
572235870Sthompsa}
573235870Sthompsa
574235870Sthompsastatic int
575115955Smuxcheck_ports(struct sock *s)
576115955Smux{
577115955Smux	int port;
578115955Smux
579115955Smux	if (ports == NULL)
580115955Smux		return (1);
581115955Smux	if ((s->family != AF_INET) && (s->family != AF_INET6))
582115955Smux		return (1);
583115955Smux	if (s->family == AF_INET)
584115955Smux		port = ntohs(((struct sockaddr_in *)(&s->laddr))->sin_port);
585115955Smux	else
586115955Smux		port = ntohs(((struct sockaddr_in6 *)(&s->laddr))->sin6_port);
587115955Smux	if (CHK_PORT(port))
588115955Smux		return (1);
589115955Smux	if (s->family == AF_INET)
590115955Smux		port = ntohs(((struct sockaddr_in *)(&s->faddr))->sin_port);
591115955Smux	else
592115955Smux		port = ntohs(((struct sockaddr_in6 *)(&s->faddr))->sin6_port);
593115955Smux	if (CHK_PORT(port))
594115955Smux		return (1);
595115955Smux	return (0);
596115955Smux}
597115955Smux
598101043Sdesstatic void
599230512Sjillesdisplaysock(struct sock *s, int pos)
600230512Sjilles{
601230512Sjilles	void *p;
602230512Sjilles	int hash;
603230512Sjilles
604230512Sjilles	while (pos < 29)
605230512Sjilles		pos += xprintf(" ");
606230512Sjilles	pos += xprintf("%s", s->protoname);
607230512Sjilles	if (s->vflag & INP_IPV4)
608230512Sjilles		pos += xprintf("4 ");
609230512Sjilles	if (s->vflag & INP_IPV6)
610230512Sjilles		pos += xprintf("6 ");
611230512Sjilles	while (pos < 36)
612230512Sjilles		pos += xprintf(" ");
613230512Sjilles	switch (s->family) {
614230512Sjilles	case AF_INET:
615230512Sjilles	case AF_INET6:
616230512Sjilles		pos += printaddr(s->family, &s->laddr);
617230512Sjilles		if (s->family == AF_INET6 && pos >= 58)
618230512Sjilles			pos += xprintf(" ");
619230512Sjilles		while (pos < 58)
620230512Sjilles			pos += xprintf(" ");
621230512Sjilles		pos += printaddr(s->family, &s->faddr);
622230512Sjilles		break;
623230512Sjilles	case AF_UNIX:
624230512Sjilles		/* server */
625230512Sjilles		if (s->laddr.ss_len > 0) {
626230512Sjilles			pos += printaddr(s->family, &s->laddr);
627230512Sjilles			break;
628230512Sjilles		}
629230512Sjilles		/* client */
630230512Sjilles		p = *(void **)&s->faddr;
631230512Sjilles		if (p == NULL) {
632230512Sjilles			pos += xprintf("(not connected)");
633230512Sjilles			break;
634230512Sjilles		}
635230512Sjilles		pos += xprintf("-> ");
636230512Sjilles		for (hash = 0; hash < HASHSIZE; ++hash) {
637230512Sjilles			for (s = sockhash[hash]; s != NULL; s = s->next)
638230512Sjilles				if (s->pcb == p)
639230512Sjilles					break;
640230512Sjilles			if (s != NULL)
641230512Sjilles				break;
642230512Sjilles		}
643230512Sjilles		if (s == NULL || s->laddr.ss_len == 0)
644230512Sjilles			pos += xprintf("??");
645230512Sjilles		else
646230512Sjilles			pos += printaddr(s->family, &s->laddr);
647230512Sjilles		break;
648230512Sjilles	default:
649230512Sjilles		abort();
650230512Sjilles	}
651230512Sjilles	xprintf("\n");
652230512Sjilles}
653230512Sjilles
654230512Sjillesstatic void
655101043Sdesdisplay(void)
656101043Sdes{
657101043Sdes	struct passwd *pwd;
658101043Sdes	struct xfile *xf;
659101043Sdes	struct sock *s;
660101043Sdes	int hash, n, pos;
661101043Sdes
662101043Sdes	printf("%-8s %-10s %-5s %-2s %-6s %-21s %-21s\n",
663101043Sdes	    "USER", "COMMAND", "PID", "FD", "PROTO",
664101043Sdes	    "LOCAL ADDRESS", "FOREIGN ADDRESS");
665101043Sdes	setpassent(1);
666101043Sdes	for (xf = xfiles, n = 0; n < nxfiles; ++n, ++xf) {
667134295Sroam		if (xf->xf_data == NULL)
668134295Sroam			continue;
669235870Sthompsa		if (opt_j >= 0 && opt_j != getprocjid(xf->xf_pid))
670235870Sthompsa			continue;
671109153Sdillon		hash = (int)((uintptr_t)xf->xf_data % HASHSIZE);
672101043Sdes		for (s = sockhash[hash]; s != NULL; s = s->next)
673109153Sdillon			if ((void *)s->socket == xf->xf_data)
674101043Sdes				break;
675101043Sdes		if (s == NULL)
676101043Sdes			continue;
677115955Smux		if (!check_ports(s))
678115955Smux			continue;
679230512Sjilles		s->shown = 1;
680101043Sdes		pos = 0;
681101043Sdes		if ((pwd = getpwuid(xf->xf_uid)) == NULL)
682187915Sdes			pos += xprintf("%lu ", (u_long)xf->xf_uid);
683101043Sdes		else
684187915Sdes			pos += xprintf("%s ", pwd->pw_name);
685101043Sdes		while (pos < 9)
686101043Sdes			pos += xprintf(" ");
687101043Sdes		pos += xprintf("%.10s", getprocname(xf->xf_pid));
688101043Sdes		while (pos < 20)
689101043Sdes			pos += xprintf(" ");
690187915Sdes		pos += xprintf("%lu ", (u_long)xf->xf_pid);
691101043Sdes		while (pos < 26)
692101043Sdes			pos += xprintf(" ");
693187915Sdes		pos += xprintf("%d ", xf->xf_fd);
694230512Sjilles		displaysock(s, pos);
695230512Sjilles	}
696235870Sthompsa	if (opt_j >= 0)
697235870Sthompsa		return;
698230512Sjilles	for (hash = 0; hash < HASHSIZE; hash++) {
699230512Sjilles		for (s = sockhash[hash]; s != NULL; s = s->next) {
700230512Sjilles			if (s->shown)
701230512Sjilles				continue;
702230512Sjilles			if (!check_ports(s))
703230512Sjilles				continue;
704230512Sjilles			pos = 0;
705230512Sjilles			pos += xprintf("%-8s %-10s %-5s %-2s ",
706230512Sjilles			    "?", "?", "?", "?");
707230512Sjilles			displaysock(s, pos);
708101043Sdes		}
709101043Sdes	}
710101043Sdes}
711101043Sdes
712164201Skeramidastatic int set_default_protos(void)
713164201Skeramida{
714164201Skeramida	struct protoent *prot;
715164201Skeramida	const char *pname;
716164201Skeramida	size_t pindex;
717164201Skeramida
718164201Skeramida	init_protos(default_numprotos);
719164201Skeramida
720164201Skeramida	for (pindex = 0; pindex < default_numprotos; pindex++) {
721164201Skeramida		pname = default_protos[pindex];
722164201Skeramida		prot = getprotobyname(pname);
723164201Skeramida		if (prot == NULL)
724164201Skeramida			err(1, "getprotobyname: %s", pname);
725164201Skeramida		protos[pindex] = prot->p_proto;
726164201Skeramida	}
727164201Skeramida	numprotos = pindex;
728164201Skeramida	return (pindex);
729164201Skeramida}
730164201Skeramida
731164201Skeramida
732101043Sdesstatic void
733101043Sdesusage(void)
734101043Sdes{
735164201Skeramida	fprintf(stderr,
736237674Sthompsa	    "Usage: sockstat [-46cLlu] [-j jid] [-p ports] [-P protocols]\n");
737101043Sdes	exit(1);
738101043Sdes}
739101043Sdes
740101043Sdesint
741101043Sdesmain(int argc, char *argv[])
742101043Sdes{
743164201Skeramida	int protos_defined = -1;
744164201Skeramida	int o, i;
745101043Sdes
746235870Sthompsa	opt_j = -1;
747235870Sthompsa	while ((o = getopt(argc, argv, "46cj:Llp:P:uv")) != -1)
748101043Sdes		switch (o) {
749101043Sdes		case '4':
750101043Sdes			opt_4 = 1;
751101043Sdes			break;
752101043Sdes		case '6':
753101043Sdes			opt_6 = 1;
754101043Sdes			break;
755101043Sdes		case 'c':
756101043Sdes			opt_c = 1;
757101043Sdes			break;
758235870Sthompsa		case 'j':
759235870Sthompsa			opt_j = atoi(optarg);
760235870Sthompsa			break;
761179115Sbms		case 'L':
762179115Sbms			opt_L = 1;
763179115Sbms			break;
764101043Sdes		case 'l':
765101043Sdes			opt_l = 1;
766101043Sdes			break;
767101043Sdes		case 'p':
768101043Sdes			parse_ports(optarg);
769101043Sdes			break;
770164201Skeramida		case 'P':
771164201Skeramida			protos_defined = parse_protos(optarg);
772164201Skeramida			break;
773101043Sdes		case 'u':
774101043Sdes			opt_u = 1;
775101043Sdes			break;
776101043Sdes		case 'v':
777101043Sdes			++opt_v;
778101043Sdes			break;
779101043Sdes		default:
780101043Sdes			usage();
781101043Sdes		}
782101043Sdes
783101043Sdes	argc -= optind;
784101043Sdes	argv += optind;
785101043Sdes
786101043Sdes	if (argc > 0)
787101043Sdes		usage();
788101043Sdes
789164226Skeramida	if ((!opt_4 && !opt_6) && protos_defined != -1)
790164226Skeramida		opt_4 = opt_6 = 1;
791164226Skeramida	if (!opt_4 && !opt_6 && !opt_u)
792164226Skeramida		opt_4 = opt_6 = opt_u = 1;
793164226Skeramida	if ((opt_4 || opt_6) && protos_defined == -1)
794164201Skeramida		protos_defined = set_default_protos();
795101043Sdes	if (!opt_c && !opt_l)
796101043Sdes		opt_c = opt_l = 1;
797101043Sdes
798101043Sdes	if (opt_4 || opt_6) {
799164201Skeramida		for (i = 0; i < protos_defined; i++)
800164201Skeramida			gather_inet(protos[i]);
801101043Sdes	}
802164201Skeramida
803164201Skeramida	if (opt_u || (protos_defined == -1 && !opt_4 && !opt_6)) {
804101043Sdes		gather_unix(SOCK_STREAM);
805101043Sdes		gather_unix(SOCK_DGRAM);
806101043Sdes	}
807101043Sdes	getfiles();
808101043Sdes	display();
809101043Sdes	exit(0);
810101043Sdes}
811