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 /* 70 * Default protocols to use if no -P was defined. 71 */ 72 static const char *default_protos[] = {"tcp", "udp", "divert" }; 73 static size_t default_numprotos = 74 sizeof(default_protos) / sizeof(default_protos[0]); 75 76 static int *protos; /* protocols to use */ 77 static size_t numprotos; /* allocated size of protos[] */ 78 79 static int *ports; 80 81 #define INT_BIT (sizeof(int)*CHAR_BIT) 82 #define SET_PORT(p) do { ports[p / INT_BIT] |= 1 << (p % INT_BIT); } while (0) 83 #define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT))) 84 85 struct sock { 86 void *socket; 87 void *pcb; 88 int vflag; 89 int family; 90 int proto; 91 const char *protoname; 92 struct sockaddr_storage laddr; 93 struct sockaddr_storage faddr; 94 struct sock *next; 95 }; 96 97 #define HASHSIZE 1009 98 static struct sock *sockhash[HASHSIZE]; 99 100 static struct xfile *xfiles; 101 static int nxfiles; 102 103 static int 104 xprintf(const char *fmt, ...) 105 { 106 va_list ap; 107 int len; 108 109 va_start(ap, fmt); 110 len = vprintf(fmt, ap); 111 va_end(ap); 112 if (len < 0) 113 err(1, "printf()"); 114 return (len); 115 } 116 117 118 static int 119 get_proto_type(const char *proto) 120 { 121 struct protoent *pent; 122 123 if (strlen(proto) == 0) 124 return (0); 125 pent = getprotobyname(proto); 126 if (pent == NULL) { 127 warn("getprotobyname"); 128 return (-1); 129 } 130 return (pent->p_proto); 131 } 132 133 134 static void init_protos(int num) 135 { 136 int proto_count = 0; 137 138 if (num > 0) { 139 proto_count = num; 140 } else { 141 /* Find the maximum number of possible protocols. */ 142 while (getprotoent() != NULL) 143 proto_count++; 144 endprotoent(); 145 } 146 147 if ((protos = malloc(sizeof(int) * proto_count)) == NULL) 148 err(1, "malloc"); 149 numprotos = proto_count; 150 } 151 152 153 static int 154 parse_protos(char *protospec) 155 { 156 char *prot; 157 char *tmp = protospec; 158 int proto_type, proto_index; 159 160 if (protospec == NULL) 161 return (-1); 162 163 init_protos(0); 164 proto_index = 0; 165 while ((prot = strsep(&tmp, ",")) != NULL) { 166 if (strlen(prot) == 0) 167 continue; 168 proto_type = get_proto_type(prot); 169 if (proto_type != -1) 170 protos[proto_index++] = proto_type; 171 } 172 numprotos = proto_index; 173 return (proto_index); 174 } 175 176 177 static void 178 parse_ports(const char *portspec) 179 { 180 const char *p, *q; 181 int port, end; 182 183 if (ports == NULL) 184 if ((ports = calloc(65536 / INT_BIT, sizeof(int))) == NULL) 185 err(1, "calloc()"); 186 p = portspec; 187 while (*p != '\0') { 188 if (!isdigit(*p)) 189 errx(1, "syntax error in port range"); 190 for (q = p; *q != '\0' && isdigit(*q); ++q) 191 /* nothing */ ; 192 for (port = 0; p < q; ++p) 193 port = port * 10 + digittoint(*p); 194 if (port < 0 || port > 65535) 195 errx(1, "invalid port number"); 196 SET_PORT(port); 197 switch (*p) { 198 case '-': 199 ++p; 200 break; 201 case ',': 202 ++p; 203 /* fall through */ 204 case '\0': 205 default: 206 continue; 207 } 208 for (q = p; *q != '\0' && isdigit(*q); ++q) 209 /* nothing */ ; 210 for (end = 0; p < q; ++p) 211 end = end * 10 + digittoint(*p); 212 if (end < port || end > 65535) 213 errx(1, "invalid port number"); 214 while (port++ < end) 215 SET_PORT(port); 216 if (*p == ',') 217 ++p; 218 } 219 } 220 221 static void 222 sockaddr(struct sockaddr_storage *sa, int af, void *addr, int port) 223 { 224 struct sockaddr_in *sin4; 225 struct sockaddr_in6 *sin6; 226 227 bzero(sa, sizeof *sa); 228 switch (af) { 229 case AF_INET: 230 sin4 = (struct sockaddr_in *)sa; 231 sin4->sin_len = sizeof *sin4; 232 sin4->sin_family = af; 233 sin4->sin_port = port; 234 sin4->sin_addr = *(struct in_addr *)addr; 235 break; 236 case AF_INET6: 237 sin6 = (struct sockaddr_in6 *)sa; 238 sin6->sin6_len = sizeof *sin6; 239 sin6->sin6_family = af; 240 sin6->sin6_port = port; 241 sin6->sin6_addr = *(struct in6_addr *)addr; 242 break; 243 default: 244 abort(); 245 } 246 } 247 248 static void 249 gather_inet(int proto) 250 { 251 struct xinpgen *xig, *exig; 252 struct xinpcb *xip; 253 struct xtcpcb *xtp; 254 struct inpcb *inp; 255 struct xsocket *so; 256 struct sock *sock; 257 const char *varname, *protoname; 258 size_t len, bufsize; 259 void *buf; 260 int hash, retry, vflag; 261 262 vflag = 0; 263 if (opt_4) 264 vflag |= INP_IPV4; 265 if (opt_6) 266 vflag |= INP_IPV6; 267 268 switch (proto) { 269 case IPPROTO_TCP: 270 varname = "net.inet.tcp.pcblist"; 271 protoname = "tcp"; 272 break; 273 case IPPROTO_UDP: 274 varname = "net.inet.udp.pcblist"; 275 protoname = "udp"; 276 break; 277 case IPPROTO_DIVERT: 278 varname = "net.inet.divert.pcblist"; 279 protoname = "div"; 280 break; 281 default: 282 errx(1, "protocol %d not supported", proto); 283 } 284 285 buf = NULL; 286 bufsize = 8192; 287 retry = 5; 288 do { 289 for (;;) { 290 if ((buf = realloc(buf, bufsize)) == NULL) 291 err(1, "realloc()"); 292 len = bufsize; 293 if (sysctlbyname(varname, buf, &len, NULL, 0) == 0) 294 break; 295 if (errno == ENOENT) 296 goto out; 297 if (errno != ENOMEM) 298 err(1, "sysctlbyname()"); 299 bufsize *= 2; 300 } 301 xig = (struct xinpgen *)buf; 302 exig = (struct xinpgen *)(void *) 303 ((char *)buf + len - sizeof *exig); 304 if (xig->xig_len != sizeof *xig || 305 exig->xig_len != sizeof *exig) 306 errx(1, "struct xinpgen size mismatch"); 307 } while (xig->xig_gen != exig->xig_gen && retry--); 308 309 if (xig->xig_gen != exig->xig_gen && opt_v) 310 warnx("warning: data may be inconsistent"); 311 312 for (;;) { 313 xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len); 314 if (xig >= exig) 315 break; 316 switch (proto) { 317 case IPPROTO_TCP: 318 xtp = (struct xtcpcb *)xig; 319 if (xtp->xt_len != sizeof *xtp) { 320 warnx("struct xtcpcb size mismatch"); 321 goto out; 322 } 323 inp = &xtp->xt_inp; 324 so = &xtp->xt_socket; 325 break; 326 case IPPROTO_UDP: 327 case IPPROTO_DIVERT: 328 xip = (struct xinpcb *)xig; 329 if (xip->xi_len != sizeof *xip) { 330 warnx("struct xinpcb size mismatch"); 331 goto out; 332 } 333 inp = &xip->xi_inp; 334 so = &xip->xi_socket; 335 break; 336 default: 337 errx(1, "protocol %d not supported", proto); 338 } 339 if ((inp->inp_vflag & vflag) == 0) 340 continue; 341 if (inp->inp_vflag & INP_IPV4) { 342 if ((inp->inp_fport == 0 && !opt_l) || 343 (inp->inp_fport != 0 && !opt_c)) 344 continue; 345 } else if (inp->inp_vflag & INP_IPV6) { 346 if ((inp->in6p_fport == 0 && !opt_l) || 347 (inp->in6p_fport != 0 && !opt_c)) 348 continue; 349 } else { 350 if (opt_v) 351 warnx("invalid vflag 0x%x", inp->inp_vflag); 352 continue; 353 } 354 if ((sock = calloc(1, sizeof *sock)) == NULL) 355 err(1, "malloc()"); 356 sock->socket = so->xso_so; 357 sock->proto = proto; 358 if (inp->inp_vflag & INP_IPV4) { 359 sock->family = AF_INET; 360 sockaddr(&sock->laddr, sock->family, 361 &inp->inp_laddr, inp->inp_lport); 362 sockaddr(&sock->faddr, sock->family, 363 &inp->inp_faddr, inp->inp_fport); 364 } else if (inp->inp_vflag & INP_IPV6) { 365 sock->family = AF_INET6; 366 sockaddr(&sock->laddr, sock->family, 367 &inp->in6p_laddr, inp->in6p_lport); 368 sockaddr(&sock->faddr, sock->family, 369 &inp->in6p_faddr, inp->in6p_fport); 370 } 371 sock->vflag = inp->inp_vflag; 372 sock->protoname = protoname; 373 hash = (int)((uintptr_t)sock->socket % HASHSIZE); 374 sock->next = sockhash[hash]; 375 sockhash[hash] = sock; 376 } 377 out: 378 free(buf); 379 } 380 381 static void 382 gather_unix(int proto) 383 { 384 struct xunpgen *xug, *exug; 385 struct xunpcb *xup; 386 struct sock *sock; 387 const char *varname, *protoname; 388 size_t len, bufsize; 389 void *buf; 390 int hash, retry; 391 392 switch (proto) { 393 case SOCK_STREAM: 394 varname = "net.local.stream.pcblist"; 395 protoname = "stream"; 396 break; 397 case SOCK_DGRAM: 398 varname = "net.local.dgram.pcblist"; 399 protoname = "dgram"; 400 break; 401 default: 402 abort(); 403 } 404 buf = NULL; 405 bufsize = 8192; 406 retry = 5; 407 do { 408 for (;;) { 409 if ((buf = realloc(buf, bufsize)) == NULL) 410 err(1, "realloc()"); 411 len = bufsize; 412 if (sysctlbyname(varname, buf, &len, NULL, 0) == 0) 413 break; 414 if (errno != ENOMEM) 415 err(1, "sysctlbyname()"); 416 bufsize *= 2; 417 } 418 xug = (struct xunpgen *)buf; 419 exug = (struct xunpgen *)(void *) 420 ((char *)buf + len - sizeof *exug); 421 if (xug->xug_len != sizeof *xug || 422 exug->xug_len != sizeof *exug) { 423 warnx("struct xinpgen size mismatch"); 424 goto out; 425 } 426 } while (xug->xug_gen != exug->xug_gen && retry--); 427 428 if (xug->xug_gen != exug->xug_gen && opt_v) 429 warnx("warning: data may be inconsistent"); 430 431 for (;;) { 432 xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len); 433 if (xug >= exug) 434 break; 435 xup = (struct xunpcb *)xug; 436 if (xup->xu_len != sizeof *xup) { 437 warnx("struct xunpcb size mismatch"); 438 goto out; 439 } 440 if ((xup->xu_unp.unp_conn == NULL && !opt_l) || 441 (xup->xu_unp.unp_conn != NULL && !opt_c)) 442 continue; 443 if ((sock = calloc(1, sizeof *sock)) == NULL) 444 err(1, "malloc()"); 445 sock->socket = xup->xu_socket.xso_so; 446 sock->pcb = xup->xu_unpp; 447 sock->proto = proto; 448 sock->family = AF_UNIX; 449 sock->protoname = protoname; 450 if (xup->xu_unp.unp_addr != NULL) 451 sock->laddr = 452 *(struct sockaddr_storage *)(void *)&xup->xu_addr; 453 else if (xup->xu_unp.unp_conn != NULL) 454 *(void **)&sock->faddr = xup->xu_unp.unp_conn; 455 hash = (int)((uintptr_t)sock->socket % HASHSIZE); 456 sock->next = sockhash[hash]; 457 sockhash[hash] = sock; 458 } 459 out: 460 free(buf); 461 } 462 463 static void 464 getfiles(void) 465 { 466 size_t len; 467 468 if ((xfiles = malloc(len = sizeof *xfiles)) == NULL) 469 err(1, "malloc()"); 470 while (sysctlbyname("kern.file", xfiles, &len, 0, 0) == -1) { 471 if (errno != ENOMEM) 472 err(1, "sysctlbyname()"); 473 len *= 2; 474 if ((xfiles = realloc(xfiles, len)) == NULL) 475 err(1, "realloc()"); 476 } 477 if (len > 0 && xfiles->xf_size != sizeof *xfiles) 478 errx(1, "struct xfile size mismatch"); 479 nxfiles = len / sizeof *xfiles; 480 } 481 482 static int 483 printaddr(int af, struct sockaddr_storage *ss) 484 { 485 char addrstr[INET6_ADDRSTRLEN] = { '\0', '\0' }; 486 struct sockaddr_un *sun; 487 void *addr; 488 int off, port; 489 490 switch (af) { 491 case AF_INET: 492 addr = &((struct sockaddr_in *)ss)->sin_addr; 493 if (inet_lnaof(*(struct in_addr *)addr) == INADDR_ANY) 494 addrstr[0] = '*'; 495 port = ntohs(((struct sockaddr_in *)ss)->sin_port); 496 break; 497 case AF_INET6: 498 addr = &((struct sockaddr_in6 *)ss)->sin6_addr; 499 if (IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *)addr)) 500 addrstr[0] = '*'; 501 port = ntohs(((struct sockaddr_in6 *)ss)->sin6_port); 502 break; 503 case AF_UNIX: 504 sun = (struct sockaddr_un *)ss; 505 off = (int)((char *)&sun->sun_path - (char *)sun); 506 return (xprintf("%.*s", sun->sun_len - off, sun->sun_path)); 507 } 508 if (addrstr[0] == '\0') 509 inet_ntop(af, addr, addrstr, sizeof addrstr); 510 if (port == 0) 511 return xprintf("%s:*", addrstr); 512 else 513 return xprintf("%s:%d", addrstr, port); 514 } 515 516 static const char * 517 getprocname(pid_t pid) 518 { 519 static struct kinfo_proc proc; 520 size_t len; 521 int mib[4]; 522 523 mib[0] = CTL_KERN; 524 mib[1] = KERN_PROC; 525 mib[2] = KERN_PROC_PID; 526 mib[3] = (int)pid; 527 len = sizeof proc; 528 if (sysctl(mib, 4, &proc, &len, NULL, 0) == -1) { 529 /* Do not warn if the process exits before we get its name. */ 530 if (errno != ESRCH) 531 warn("sysctl()"); 532 return ("??"); 533 } 534 return (proc.ki_comm); 535 } 536 537 static int 538 check_ports(struct sock *s) 539 { 540 int port; 541 542 if (ports == NULL) 543 return (1); 544 if ((s->family != AF_INET) && (s->family != AF_INET6)) 545 return (1); 546 if (s->family == AF_INET) 547 port = ntohs(((struct sockaddr_in *)(&s->laddr))->sin_port); 548 else 549 port = ntohs(((struct sockaddr_in6 *)(&s->laddr))->sin6_port); 550 if (CHK_PORT(port)) 551 return (1); 552 if (s->family == AF_INET) 553 port = ntohs(((struct sockaddr_in *)(&s->faddr))->sin_port); 554 else 555 port = ntohs(((struct sockaddr_in6 *)(&s->faddr))->sin6_port); 556 if (CHK_PORT(port)) 557 return (1); 558 return (0); 559 } 560 561 static void 562 display(void) 563 { 564 struct passwd *pwd; 565 struct xfile *xf; 566 struct sock *s; 567 void *p; 568 int hash, n, pos; 569 570 printf("%-8s %-10s %-5s %-2s %-6s %-21s %-21s\n", 571 "USER", "COMMAND", "PID", "FD", "PROTO", 572 "LOCAL ADDRESS", "FOREIGN ADDRESS"); 573 setpassent(1); 574 for (xf = xfiles, n = 0; n < nxfiles; ++n, ++xf) { 575 if (xf->xf_data == NULL) 576 continue; 577 hash = (int)((uintptr_t)xf->xf_data % HASHSIZE); 578 for (s = sockhash[hash]; s != NULL; s = s->next) 579 if ((void *)s->socket == xf->xf_data) 580 break; 581 if (s == NULL) 582 continue; 583 if (!check_ports(s)) 584 continue; 585 pos = 0; 586 if ((pwd = getpwuid(xf->xf_uid)) == NULL) 587 pos += xprintf("%lu", (u_long)xf->xf_uid); 588 else 589 pos += xprintf("%s", pwd->pw_name); 590 while (pos < 9) 591 pos += xprintf(" "); 592 pos += xprintf("%.10s", getprocname(xf->xf_pid)); 593 while (pos < 20) 594 pos += xprintf(" "); 595 pos += xprintf("%lu", (u_long)xf->xf_pid); 596 while (pos < 26) 597 pos += xprintf(" "); 598 pos += xprintf("%d", xf->xf_fd); 599 while (pos < 29) 600 pos += xprintf(" "); 601 pos += xprintf("%s", s->protoname); 602 if (s->vflag & INP_IPV4) 603 pos += xprintf("4"); 604 if (s->vflag & INP_IPV6) 605 pos += xprintf("6"); 606 while (pos < 36) 607 pos += xprintf(" "); 608 switch (s->family) { 609 case AF_INET: 610 case AF_INET6: 611 pos += printaddr(s->family, &s->laddr); 612 while (pos < 58) 613 pos += xprintf(" "); 614 pos += printaddr(s->family, &s->faddr); 615 break; 616 case AF_UNIX: 617 /* server */ 618 if (s->laddr.ss_len > 0) { 619 pos += printaddr(s->family, &s->laddr); 620 break; 621 } 622 /* client */ 623 p = *(void **)&s->faddr; 624 if (p == NULL) { 625 pos += xprintf("(not connected)"); 626 break; 627 } 628 pos += xprintf("-> "); 629 for (hash = 0; hash < HASHSIZE; ++hash) { 630 for (s = sockhash[hash]; s != NULL; s = s->next) 631 if (s->pcb == p) 632 break; 633 if (s != NULL) 634 break; 635 } 636 if (s == NULL || s->laddr.ss_len == 0) 637 pos += xprintf("??"); 638 else 639 pos += printaddr(s->family, &s->laddr); 640 break; 641 default: 642 abort(); 643 } 644 xprintf("\n"); 645 } 646 } 647 648 static int set_default_protos(void) 649 { 650 struct protoent *prot; 651 const char *pname; 652 size_t pindex; 653 654 init_protos(default_numprotos); 655 656 for (pindex = 0; pindex < default_numprotos; pindex++) { 657 pname = default_protos[pindex]; 658 prot = getprotobyname(pname); 659 if (prot == NULL) 660 err(1, "getprotobyname: %s", pname); 661 protos[pindex] = prot->p_proto; 662 } 663 numprotos = pindex; 664 return (pindex); 665 } 666 667 668 static void 669 usage(void) 670 { 671 fprintf(stderr, 672 "Usage: sockstat [-46clu] [-p ports] [-P protocols]\n"); 673 exit(1); 674 } 675 676 int 677 main(int argc, char *argv[]) 678 { 679 int protos_defined = -1; 680 int o, i; 681 682 while ((o = getopt(argc, argv, "46clp:P:uv")) != -1) 683 switch (o) { 684 case '4': 685 opt_4 = 1; 686 break; 687 case '6': 688 opt_6 = 1; 689 break; 690 case 'c': 691 opt_c = 1; 692 break; 693 case 'l': 694 opt_l = 1; 695 break; 696 case 'p': 697 parse_ports(optarg); 698 break; 699 case 'P': 700 protos_defined = parse_protos(optarg); 701 break; 702 case 'u': 703 opt_u = 1; 704 break; 705 case 'v': 706 ++opt_v; 707 break; 708 default: 709 usage(); 710 } 711 712 argc -= optind; 713 argv += optind; 714 715 if (argc > 0) 716 usage(); 717 718 if ((!opt_4 && !opt_6) && protos_defined != -1) 719 opt_4 = opt_6 = 1; 720 if (!opt_4 && !opt_6 && !opt_u) 721 opt_4 = opt_6 = opt_u = 1; 722 if ((opt_4 || opt_6) && protos_defined == -1) 723 protos_defined = set_default_protos(); 724 if (!opt_c && !opt_l) 725 opt_c = opt_l = 1; 726 727 if (opt_4 || opt_6) { 728 for (i = 0; i < protos_defined; i++) 729 gather_inet(protos[i]); 730 } 731 732 if (opt_u || (protos_defined == -1 && !opt_4 && !opt_6)) { 733 gather_unix(SOCK_STREAM); 734 gather_unix(SOCK_DGRAM); 735 } 736 getfiles(); 737 display(); 738 exit(0); 739 } 740