Fix various grammatical issues in our messages, documentation etc.
[dragonfly.git] / usr.bin / sockstat / sockstat.c
1 /*-
2  * Copyright (c) 2005 Joerg Sonnenberger <joerg@bec.de>.  All rights reserved.
3  * Copyright (c) 2002 Dag-Erling Coïdan Smørgrav
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer
11  *    in this position and unchanged.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * $FreeBSD: src/usr.bin/sockstat/sockstat.c,v 1.12 2004/12/06 09:28:05 ru Exp $
30  */
31
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <sys/socketvar.h>
35 #include <sys/sysctl.h>
36 #include <sys/file.h>
37
38 #include <sys/un.h>
39 #include <sys/unpcb.h>
40
41 #include <net/route.h>
42
43 #include <netinet/in.h>
44 #include <netinet/in_pcb.h>
45 #include <netinet/tcp.h>
46 #include <netinet/tcp_seq.h>
47 #include <netinet/tcp_var.h>
48 #include <arpa/inet.h>
49
50 #include <ctype.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <kinfo.h>
54 #include <netdb.h>
55 #include <pwd.h>
56 #include <stdarg.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61
62 static int       opt_4;         /* Show IPv4 sockets */
63 static int       opt_6;         /* Show IPv6 sockets */
64 static int       opt_c;         /* Show connected sockets */
65 static int       opt_l;         /* Show listening sockets */
66 static int       opt_u;         /* Show Unix domain sockets */
67 static int       opt_v;         /* Verbose mode */
68
69 static int      *ports;
70
71 #define INT_BIT (sizeof(int)*CHAR_BIT)
72 #define SET_PORT(p) do { ports[p / INT_BIT] |= 1 << (p % INT_BIT); } while (0)
73 #define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT)))
74
75 struct sock {
76         void *socket;
77         void *pcb;
78         int family;
79         int proto;
80         const char *protoname;
81         struct sockaddr_storage laddr;
82         struct sockaddr_storage faddr;
83         struct sock *next;
84 };
85
86 static int xprintf(const char *, ...) __printflike(1, 2);
87
88 #define HASHSIZE 1009
89 static struct sock *sockhash[HASHSIZE];
90
91 static struct kinfo_file *xfiles;
92 static size_t nxfiles;
93
94 static int
95 xprintf(const char *fmt, ...)
96 {
97         va_list ap;
98         int len;
99
100         va_start(ap, fmt);
101         len = vprintf(fmt, ap);
102         va_end(ap);
103         if (len < 0)
104                 err(1, "printf()");
105         return (len);
106 }
107
108 static void
109 parse_ports(const char *portspec)
110 {
111         const char *p, *q;
112         int port, end;
113
114         if (ports == NULL)
115                 if ((ports = calloc(65536 / INT_BIT, sizeof(int))) == NULL)
116                         err(1, "calloc()");
117         p = portspec;
118         while (*p != '\0') {
119                 if (!isdigit(*p))
120                         errx(1, "syntax error in port range");
121                 for (q = p; *q != '\0' && isdigit(*q); ++q)
122                         /* nothing */ ;
123                 for (port = 0; p < q; ++p)
124                         port = port * 10 + (*p - '0');
125                 if (port < 0 || port > 65535)
126                         errx(1, "invalid port number");
127                 SET_PORT(port);
128                 switch (*p) {
129                 case '-':
130                         ++p;
131                         break;
132                 case ',':
133                         ++p;
134                         /* fall through */
135                 case '\0':
136                 default:
137                         continue;
138                 }
139                 for (q = p; *q != '\0' && isdigit(*q); ++q)
140                         /* nothing */ ;
141                 for (end = 0; p < q; ++p)
142                         end = end * 10 + (*p - '0');
143                 if (end < port || end > 65535)
144                         errx(1, "invalid port number");
145                 while (port++ < end)
146                         SET_PORT(port);
147                 if (*p == ',')
148                         ++p;
149         }
150 }
151
152 static void
153 sockaddr(struct sockaddr_storage *sa, int af, void *addr, int port)
154 {
155         struct sockaddr_in *sin4;
156         struct sockaddr_in6 *sin6;
157
158         bzero(sa, sizeof *sa);
159         switch (af) {
160         case AF_INET:
161                 sin4 = (struct sockaddr_in *)sa;
162                 sin4->sin_len = sizeof *sin4;
163                 sin4->sin_family = af;
164                 sin4->sin_port = port;
165                 sin4->sin_addr = *(struct in_addr *)addr;
166                 break;
167         case AF_INET6:
168                 sin6 = (struct sockaddr_in6 *)sa;
169                 sin6->sin6_len = sizeof *sin6;
170                 sin6->sin6_family = af;
171                 sin6->sin6_port = port;
172                 sin6->sin6_addr = *(struct in6_addr *)addr;
173                 break;
174         default:
175                 abort();
176         }
177 }
178
179 static void
180 gather_inet(int proto)
181 {
182         void *so_begin, *so_end;
183         struct xinpcb *xip;
184         struct xtcpcb *xtp;
185         struct inpcb *inp;
186         struct xsocket *so;
187         struct sock *sock;
188         const char *varname, *protoname;
189         size_t len;
190         void *buf;
191         int hash;
192
193         switch (proto) {
194         case IPPROTO_TCP:
195                 varname = "net.inet.tcp.pcblist";
196                 protoname = "tcp";
197                 break;
198         case IPPROTO_UDP:
199                 varname = "net.inet.udp.pcblist";
200                 protoname = "udp";
201                 break;
202         case IPPROTO_DIVERT:
203                 varname = "net.inet.divert.pcblist";
204                 protoname = "div";
205                 break;
206         default:
207                 abort();
208         }
209
210         buf = NULL;
211         len = 0;
212
213         if (sysctlbyname(varname, NULL, &len, NULL, 0)) {
214                 if (errno == ENOENT)
215                         goto out;
216                 err(1, "fetching %s", varname);
217         }
218         if ((buf = malloc(len)) == NULL)
219                 err(1, "malloc()");
220         if (sysctlbyname(varname, buf, &len, NULL, 0)) {
221                 if (errno == ENOENT)
222                         goto out;
223                 err(1, "fetching %s", varname);
224         }
225
226         so_begin = buf;
227         so_end = (uint8_t *)buf + len;
228
229         for (so_begin = buf, so_end = (uint8_t *)so_begin + len;
230              (uint8_t *)so_begin + sizeof(size_t) < (uint8_t *)so_end &&
231              (uint8_t *)so_begin + *(size_t *)so_begin <= (uint8_t *)so_end;
232              so_begin = (uint8_t *)so_begin + *(size_t *)so_begin) {
233                 switch (proto) {
234                 case IPPROTO_TCP:
235                         xtp = (struct xtcpcb *)so_begin;
236                         if (xtp->xt_len != sizeof *xtp) {
237                                 warnx("struct xtcpcb size mismatch");
238                                 goto out;
239                         }
240                         inp = &xtp->xt_inp;
241                         so = &xtp->xt_socket;
242                         break;
243                 case IPPROTO_UDP:
244                 case IPPROTO_DIVERT:
245                         xip = (struct xinpcb *)so_begin;
246                         if (xip->xi_len != sizeof *xip) {
247                                 warnx("struct xinpcb size mismatch");
248                                 goto out;
249                         }
250                         inp = &xip->xi_inp;
251                         so = &xip->xi_socket;
252                         break;
253                 default:
254                         abort();
255                 }
256                 if ((INP_ISIPV4(inp) && !opt_4) || (INP_ISIPV6(inp) && !opt_6))
257                         continue;
258                 if (INP_ISIPV4(inp)) {
259                         if ((inp->inp_fport == 0 && !opt_l) ||
260                             (inp->inp_fport != 0 && !opt_c))
261                                 continue;
262                 } else if (INP_ISIPV6(inp)) {
263                         if ((inp->in6p_fport == 0 && !opt_l) ||
264                             (inp->in6p_fport != 0 && !opt_c))
265                                 continue;
266                 } else {
267                         if (opt_v)
268                                 warnx("invalid af 0x%x", inp->inp_af);
269                         continue;
270                 }
271                 if ((sock = calloc(1, sizeof *sock)) == NULL)
272                         err(1, "malloc()");
273                 sock->socket = so->xso_so;
274                 sock->proto = proto;
275                 if (INP_ISIPV4(inp)) {
276                         sock->family = AF_INET;
277                         sockaddr(&sock->laddr, sock->family,
278                             &inp->inp_laddr, inp->inp_lport);
279                         sockaddr(&sock->faddr, sock->family,
280                             &inp->inp_faddr, inp->inp_fport);
281                 } else if (INP_ISIPV6(inp)) {
282                         sock->family = AF_INET6;
283                         sockaddr(&sock->laddr, sock->family,
284                             &inp->in6p_laddr, inp->in6p_lport);
285                         sockaddr(&sock->faddr, sock->family,
286                             &inp->in6p_faddr, inp->in6p_fport);
287                 }
288                 sock->protoname = protoname;
289                 hash = (int)((uintptr_t)sock->socket % HASHSIZE);
290                 sock->next = sockhash[hash];
291                 sockhash[hash] = sock;
292         }
293 out:
294         free(buf);
295 }
296
297 static void
298 gather_unix(int proto)
299 {
300         void *so_begin, *so_end;
301         struct xunpcb *xup;
302         struct sock *sock;
303         const char *varname, *protoname;
304         size_t len;
305         void *buf;
306         int hash;
307
308         switch (proto) {
309         case SOCK_STREAM:
310                 varname = "net.local.stream.pcblist";
311                 protoname = "stream";
312                 break;
313         case SOCK_DGRAM:
314                 varname = "net.local.dgram.pcblist";
315                 protoname = "dgram";
316                 break;
317         default:
318                 abort();
319         }
320
321         buf = NULL;
322         len = 0;
323
324         if (sysctlbyname(varname, NULL, &len, NULL, 0))
325                 err(1, "fetching %s", varname);
326
327         if ((buf = malloc(len)) == NULL)
328                 err(1, "malloc()");
329         if (sysctlbyname(varname, buf, &len, NULL, 0))
330                 err(1, "fetching %s", varname);
331
332         for (so_begin = buf, so_end = (uint8_t *)buf + len;
333              (uint8_t *)so_begin + sizeof(size_t) < (uint8_t *)so_end &&
334              (uint8_t *)so_begin + *(size_t *)so_begin <= (uint8_t *)so_end;
335              so_begin = (uint8_t *)so_begin + *(size_t *)so_begin) {
336                 xup = so_begin;
337                 if (xup->xu_len != sizeof *xup) {
338                         warnx("struct xunpcb size mismatch");
339                         goto out;
340                 }
341                 if ((xup->xu_unp.unp_conn == NULL && !opt_l) ||
342                     (xup->xu_unp.unp_conn != NULL && !opt_c))
343                         continue;
344                 if ((sock = calloc(1, sizeof *sock)) == NULL)
345                         err(1, "malloc()");
346                 sock->socket = xup->xu_socket.xso_so;
347                 sock->pcb = xup->xu_unpp;
348                 sock->proto = proto;
349                 sock->family = AF_UNIX;
350                 sock->protoname = protoname;
351                 if (xup->xu_unp.unp_addr != NULL)
352                         sock->laddr =
353                             *(struct sockaddr_storage *)(void *)&xup->xu_addr;
354                 else if (xup->xu_unp.unp_conn != NULL)
355                         *(void **)&sock->faddr = xup->xu_unp.unp_conn;
356                 hash = (int)((uintptr_t)sock->socket % HASHSIZE);
357                 sock->next = sockhash[hash];
358                 sockhash[hash] = sock;
359         }
360 out:
361         free(buf);
362 }
363
364 static void
365 getfiles(void)
366 {
367         if (kinfo_get_files(&xfiles, &nxfiles))
368                 err(1, "kinfo_get_files");
369 }
370
371 static int
372 printaddr(int af, struct sockaddr_storage *ss)
373 {
374         char addrstr[INET6_ADDRSTRLEN] = { '\0', '\0' };
375         struct sockaddr_un *sun;
376         void *addr;
377         int off, port;
378
379         switch (af) {
380         case AF_INET:
381                 addr = &((struct sockaddr_in *)ss)->sin_addr;
382                 if (inet_lnaof(*(struct in_addr *)addr) == INADDR_ANY)
383                         addrstr[0] = '*';
384                 port = ntohs(((struct sockaddr_in *)ss)->sin_port);
385                 break;
386         case AF_INET6:
387                 addr = &((struct sockaddr_in6 *)ss)->sin6_addr;
388                 if (IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *)addr))
389                         addrstr[0] = '*';
390                 port = ntohs(((struct sockaddr_in6 *)ss)->sin6_port);
391                 break;
392         case AF_UNIX:
393                 sun = (struct sockaddr_un *)ss;
394                 off = (int)((char *)&sun->sun_path - (char *)sun);
395                 return (xprintf("%.*s", sun->sun_len - off, sun->sun_path));
396         default:
397                 abort();
398         }
399         if (addrstr[0] == '\0')
400                 inet_ntop(af, addr, addrstr, sizeof addrstr);
401         if (port == 0)
402                 return xprintf("%s:*", addrstr);
403         else
404                 return xprintf("%s:%d", addrstr, port);
405 }
406
407 static const char *
408 getprocname(pid_t pid)
409 {
410         static struct kinfo_proc proc;
411         size_t len;
412         int mib[4];
413
414         mib[0] = CTL_KERN;
415         mib[1] = KERN_PROC;
416         mib[2] = KERN_PROC_PID;
417         mib[3] = (int)pid;
418         len = sizeof proc;
419         if (sysctl(mib, 4, &proc, &len, NULL, 0) == -1) {
420                 warn("sysctl()");
421                 return ("??");
422         }
423         return (proc.kp_comm);
424 }
425
426 static int
427 check_ports(struct sock *s)
428 {
429         int port;
430
431         if (ports == NULL)
432                 return (1);
433         if ((s->family != AF_INET) && (s->family != AF_INET6))
434                 return (1);
435         if (s->family == AF_INET)
436                 port = ntohs(((struct sockaddr_in *)(&s->laddr))->sin_port);
437         else
438                 port = ntohs(((struct sockaddr_in6 *)(&s->laddr))->sin6_port);
439         if (CHK_PORT(port))
440                 return (1);
441         if (s->family == AF_INET)
442                 port = ntohs(((struct sockaddr_in *)(&s->faddr))->sin_port);
443         else
444                 port = ntohs(((struct sockaddr_in6 *)(&s->faddr))->sin6_port);
445         if (CHK_PORT(port))
446                 return (1);
447         return (0);
448 }
449
450 static void
451 display(void)
452 {
453         struct passwd *pwd;
454         struct kinfo_file *xf;
455         struct sock *s;
456         void *p;
457         int hash, n, pos;
458
459         printf("%-8s %-10s %-5s %-2s %-6s %-21s %-21s\n",
460             "USER", "COMMAND", "PID", "FD", "PROTO",
461             "LOCAL ADDRESS", "FOREIGN ADDRESS");
462         setpassent(1);
463         for (xf = xfiles, n = 0; n < (int)nxfiles; ++n, ++xf) {
464                 if (xf->f_data == NULL)
465                         continue;
466                 hash = (int)((uintptr_t)xf->f_data % HASHSIZE);
467                 for (s = sockhash[hash]; s != NULL; s = s->next)
468                         if ((void *)s->socket == xf->f_data)
469                                 break;
470                 if (s == NULL)
471                         continue;
472                 if (!check_ports(s))
473                         continue;
474                 pos = 0;
475                 if ((pwd = getpwuid(xf->f_uid)) == NULL)
476                         pos += xprintf("%lu", (u_long)xf->f_uid);
477                 else
478                         pos += xprintf("%s", pwd->pw_name);
479                 while (pos < 9)
480                         pos += xprintf(" ");
481                 pos += xprintf("%.10s", getprocname(xf->f_pid));
482                 while (pos < 20)
483                         pos += xprintf(" ");
484                 pos += xprintf("%lu", (u_long)xf->f_pid);
485                 while (pos < 26)
486                         pos += xprintf(" ");
487                 pos += xprintf("%d", xf->f_fd);
488                 while (pos < 29)
489                         pos += xprintf(" ");
490                 pos += xprintf("%s", s->protoname);
491                 if (s->family == AF_INET)
492                         pos += xprintf("4");
493                 if (s->family == AF_INET6)
494                         pos += xprintf("6");
495                 while (pos < 36)
496                         pos += xprintf(" ");
497                 switch (s->family) {
498                 case AF_INET:
499                 case AF_INET6:
500                         pos += printaddr(s->family, &s->laddr);
501                         while (pos < 57)
502                                 pos += xprintf(" ");
503                         pos += xprintf(" ");
504                         pos += printaddr(s->family, &s->faddr);
505                         break;
506                 case AF_UNIX:
507                         /* server */
508                         if (s->laddr.ss_len > 0) {
509                                 pos += printaddr(s->family, &s->laddr);
510                                 break;
511                         }
512                         /* client */
513                         p = *(void **)&s->faddr;
514                         if (p == NULL) {
515                                 pos += xprintf("(not connected)");
516                                 break;
517                         }
518                         pos += xprintf("-> ");
519                         for (hash = 0; hash < HASHSIZE; ++hash) {
520                                 for (s = sockhash[hash]; s != NULL; s = s->next)
521                                         if (s->pcb == p)
522                                                 break;
523                                 if (s != NULL)
524                                         break;
525                         }
526                         if (s == NULL || s->laddr.ss_len == 0)
527                                 pos += xprintf("??");
528                         else
529                                 pos += printaddr(s->family, &s->laddr);
530                         break;
531                 default:
532                         abort();
533                 }
534                 xprintf("\n");
535         }
536 }
537
538 static void
539 usage(void)
540 {
541         fprintf(stderr, "Usage: sockstat [-46clu] [-p ports]\n");
542         exit(1);
543 }
544
545 int
546 main(int argc, char *argv[])
547 {
548         int o;
549
550         while ((o = getopt(argc, argv, "46clp:uv")) != -1)
551                 switch (o) {
552                 case '4':
553                         opt_4 = 1;
554                         break;
555                 case '6':
556                         opt_6 = 1;
557                         break;
558                 case 'c':
559                         opt_c = 1;
560                         break;
561                 case 'l':
562                         opt_l = 1;
563                         break;
564                 case 'p':
565                         parse_ports(optarg);
566                         break;
567                 case 'u':
568                         opt_u = 1;
569                         break;
570                 case 'v':
571                         ++opt_v;
572                         break;
573                 default:
574                         usage();
575                 }
576
577         argc -= optind;
578         argv += optind;
579
580         if (argc > 0)
581                 usage();
582
583         if (!opt_4 && !opt_6 && !opt_u)
584                 opt_4 = opt_6 = opt_u = 1;
585         if (!opt_c && !opt_l)
586                 opt_c = opt_l = 1;
587
588         if (opt_4 || opt_6) {
589                 gather_inet(IPPROTO_TCP);
590                 gather_inet(IPPROTO_UDP);
591                 gather_inet(IPPROTO_DIVERT);
592         }
593         if (opt_u) {
594                 gather_unix(SOCK_STREAM);
595                 gather_unix(SOCK_DGRAM);
596         }
597         getfiles();
598         display();
599
600         exit(0);
601 }