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