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 free(sock); 283 continue; 284 } 285 if ((sock = calloc(1, sizeof *sock)) == NULL) 286 err(1, "malloc()"); 287 sock->socket = so->xso_so; 288 sock->proto = proto; 289 if (inp->inp_vflag & INP_IPV4) { 290 sock->family = AF_INET; 291 sockaddr(&sock->laddr, sock->family, 292 &inp->inp_laddr, inp->inp_lport); 293 sockaddr(&sock->faddr, sock->family, 294 &inp->inp_faddr, inp->inp_fport); 295 } else if (inp->inp_vflag & INP_IPV6) { 296 sock->family = AF_INET6; 297 sockaddr(&sock->laddr, sock->family, 298 &inp->in6p_laddr, inp->in6p_lport); 299 sockaddr(&sock->faddr, sock->family, 300 &inp->in6p_faddr, inp->in6p_fport); 301 } 302 sock->vflag = inp->inp_vflag; 303 sock->protoname = protoname; 304 hash = (int)((uintptr_t)sock->socket % HASHSIZE); 305 sock->next = sockhash[hash]; 306 sockhash[hash] = sock; 307 } 308 out: 309 free(buf); 310 } 311 312 static void 313 gather_unix(int proto) 314 { 315 struct xunpgen *xug, *exug; 316 struct xunpcb *xup; 317 struct sock *sock; 318 const char *varname, *protoname; 319 size_t len, bufsize; 320 void *buf; 321 int hash, retry; 322 323 switch (proto) { 324 case SOCK_STREAM: 325 varname = "net.local.stream.pcblist"; 326 protoname = "stream"; 327 break; 328 case SOCK_DGRAM: 329 varname = "net.local.dgram.pcblist"; 330 protoname = "dgram"; 331 break; 332 default: 333 abort(); 334 } 335 buf = NULL; 336 bufsize = 8192; 337 retry = 5; 338 do { 339 for (;;) { 340 if ((buf = realloc(buf, bufsize)) == NULL) 341 err(1, "realloc()"); 342 len = bufsize; 343 if (sysctlbyname(varname, buf, &len, NULL, 0) == 0) 344 break; 345 if (errno != ENOMEM) 346 err(1, "sysctlbyname()"); 347 bufsize *= 2; 348 } 349 xug = (struct xunpgen *)buf; 350 exug = (struct xunpgen *)(void *) 351 ((char *)buf + len - sizeof *exug); 352 if (xug->xug_len != sizeof *xug || 353 exug->xug_len != sizeof *exug) { 354 warnx("struct xinpgen size mismatch"); 355 goto out; 356 } 357 } while (xug->xug_gen != exug->xug_gen && retry--); 358 359 if (xug->xug_gen != exug->xug_gen && opt_v) 360 warnx("warning: data may be inconsistent"); 361 362 for (;;) { 363 xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len); 364 if (xug >= exug) 365 break; 366 xup = (struct xunpcb *)xug; 367 if (xup->xu_len != sizeof *xup) { 368 warnx("struct xunpcb size mismatch"); 369 goto out; 370 } 371 if ((xup->xu_unp.unp_conn == NULL && !opt_l) || 372 (xup->xu_unp.unp_conn != NULL && !opt_c)) 373 continue; 374 if ((sock = calloc(1, sizeof *sock)) == NULL) 375 err(1, "malloc()"); 376 sock->socket = xup->xu_socket.xso_so; 377 sock->pcb = xup->xu_unpp; 378 sock->proto = proto; 379 sock->family = AF_UNIX; 380 sock->protoname = protoname; 381 if (xup->xu_unp.unp_addr != NULL) 382 sock->laddr = 383 *(struct sockaddr_storage *)(void *)&xup->xu_addr; 384 else if (xup->xu_unp.unp_conn != NULL) 385 *(void **)&sock->faddr = xup->xu_unp.unp_conn; 386 hash = (int)((uintptr_t)sock->socket % HASHSIZE); 387 sock->next = sockhash[hash]; 388 sockhash[hash] = sock; 389 } 390 out: 391 free(buf); 392 } 393 394 static void 395 getfiles(void) 396 { 397 size_t len; 398 399 if ((xfiles = malloc(len = sizeof *xfiles)) == NULL) 400 err(1, "malloc()"); 401 while (sysctlbyname("kern.file", xfiles, &len, 0, 0) == -1) { 402 if (errno != ENOMEM) 403 err(1, "sysctlbyname()"); 404 len *= 2; 405 if ((xfiles = realloc(xfiles, len)) == NULL) 406 err(1, "realloc()"); 407 } 408 if (len > 0 && xfiles->xf_size != sizeof *xfiles) 409 errx(1, "struct xfile size mismatch"); 410 nxfiles = len / sizeof *xfiles; 411 } 412 413 static int 414 printaddr(int af, struct sockaddr_storage *ss) 415 { 416 char addrstr[INET6_ADDRSTRLEN] = { '\0', '\0' }; 417 struct sockaddr_un *sun; 418 void *addr; 419 int off, port; 420 421 switch (af) { 422 case AF_INET: 423 addr = &((struct sockaddr_in *)ss)->sin_addr; 424 if (inet_lnaof(*(struct in_addr *)addr) == INADDR_ANY) 425 addrstr[0] = '*'; 426 port = ntohs(((struct sockaddr_in *)ss)->sin_port); 427 break; 428 case AF_INET6: 429 addr = &((struct sockaddr_in6 *)ss)->sin6_addr; 430 if (IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *)addr)) 431 addrstr[0] = '*'; 432 port = ntohs(((struct sockaddr_in6 *)ss)->sin6_port); 433 break; 434 case AF_UNIX: 435 sun = (struct sockaddr_un *)ss; 436 off = (int)((char *)&sun->sun_path - (char *)sun); 437 return (xprintf("%.*s", sun->sun_len - off, sun->sun_path)); 438 } 439 if (addrstr[0] == '\0') 440 inet_ntop(af, addr, addrstr, sizeof addrstr); 441 if (port == 0) 442 return xprintf("%s:*", addrstr); 443 else 444 return xprintf("%s:%d", addrstr, port); 445 } 446 447 static const char * 448 getprocname(pid_t pid) 449 { 450 static struct kinfo_proc proc; 451 size_t len; 452 int mib[4]; 453 454 mib[0] = CTL_KERN; 455 mib[1] = KERN_PROC; 456 mib[2] = KERN_PROC_PID; 457 mib[3] = (int)pid; 458 len = sizeof proc; 459 if (sysctl(mib, 4, &proc, &len, NULL, 0) == -1) { 460 warn("sysctl()"); 461 return ("??"); 462 } 463 return (proc.ki_ocomm); 464 } 465 466 static int 467 check_ports(struct sock *s) 468 { 469 int port; 470 471 if (ports == NULL) 472 return (1); 473 if ((s->family != AF_INET) && (s->family != AF_INET6)) 474 return (1); 475 if (s->family == AF_INET) 476 port = ntohs(((struct sockaddr_in *)(&s->laddr))->sin_port); 477 else 478 port = ntohs(((struct sockaddr_in6 *)(&s->laddr))->sin6_port); 479 if (CHK_PORT(port)) 480 return (1); 481 if (s->family == AF_INET) 482 port = ntohs(((struct sockaddr_in *)(&s->faddr))->sin_port); 483 else 484 port = ntohs(((struct sockaddr_in6 *)(&s->faddr))->sin6_port); 485 if (CHK_PORT(port)) 486 return (1); 487 return (0); 488 } 489 490 static void 491 display(void) 492 { 493 struct passwd *pwd; 494 struct xfile *xf; 495 struct sock *s; 496 void *p; 497 int hash, n, pos; 498 499 printf("%-8s %-10s %-5s %-2s %-6s %-21s %-21s\n", 500 "USER", "COMMAND", "PID", "FD", "PROTO", 501 "LOCAL ADDRESS", "FOREIGN ADDRESS"); 502 setpassent(1); 503 for (xf = xfiles, n = 0; n < nxfiles; ++n, ++xf) { 504 if (xf->xf_data == NULL) 505 continue; 506 hash = (int)((uintptr_t)xf->xf_data % HASHSIZE); 507 for (s = sockhash[hash]; s != NULL; s = s->next) 508 if ((void *)s->socket == xf->xf_data) 509 break; 510 if (s == NULL) 511 continue; 512 if (!check_ports(s)) 513 continue; 514 pos = 0; 515 if ((pwd = getpwuid(xf->xf_uid)) == NULL) 516 pos += xprintf("%lu", (u_long)xf->xf_uid); 517 else 518 pos += xprintf("%s", pwd->pw_name); 519 while (pos < 9) 520 pos += xprintf(" "); 521 pos += xprintf("%.10s", getprocname(xf->xf_pid)); 522 while (pos < 20) 523 pos += xprintf(" "); 524 pos += xprintf("%lu", (u_long)xf->xf_pid); 525 while (pos < 26) 526 pos += xprintf(" "); 527 pos += xprintf("%d", xf->xf_fd); 528 while (pos < 29) 529 pos += xprintf(" "); 530 pos += xprintf("%s", s->protoname); 531 if (s->vflag & INP_IPV4) 532 pos += xprintf("4"); 533 if (s->vflag & INP_IPV6) 534 pos += xprintf("6"); 535 while (pos < 36) 536 pos += xprintf(" "); 537 switch (s->family) { 538 case AF_INET: 539 case AF_INET6: 540 pos += printaddr(s->family, &s->laddr); 541 while (pos < 58) 542 pos += xprintf(" "); 543 pos += printaddr(s->family, &s->faddr); 544 break; 545 case AF_UNIX: 546 /* server */ 547 if (s->laddr.ss_len > 0) { 548 pos += printaddr(s->family, &s->laddr); 549 break; 550 } 551 /* client */ 552 p = *(void **)&s->faddr; 553 if (p == NULL) { 554 pos += xprintf("(not connected)"); 555 break; 556 } 557 pos += xprintf("-> "); 558 for (hash = 0; hash < HASHSIZE; ++hash) { 559 for (s = sockhash[hash]; s != NULL; s = s->next) 560 if (s->pcb == p) 561 break; 562 if (s != NULL) 563 break; 564 } 565 if (s == NULL || s->laddr.ss_len == 0) 566 pos += xprintf("??"); 567 else 568 pos += printaddr(s->family, &s->laddr); 569 break; 570 default: 571 abort(); 572 } 573 xprintf("\n"); 574 } 575 } 576 577 static void 578 usage(void) 579 { 580 fprintf(stderr, "Usage: sockstat [-46clu] [-p ports]\n"); 581 exit(1); 582 } 583 584 int 585 main(int argc, char *argv[]) 586 { 587 int o; 588 589 while ((o = getopt(argc, argv, "46clp:uv")) != -1) 590 switch (o) { 591 case '4': 592 opt_4 = 1; 593 break; 594 case '6': 595 opt_6 = 1; 596 break; 597 case 'c': 598 opt_c = 1; 599 break; 600 case 'l': 601 opt_l = 1; 602 break; 603 case 'p': 604 parse_ports(optarg); 605 break; 606 case 'u': 607 opt_u = 1; 608 break; 609 case 'v': 610 ++opt_v; 611 break; 612 default: 613 usage(); 614 } 615 616 argc -= optind; 617 argv += optind; 618 619 if (argc > 0) 620 usage(); 621 622 if (!opt_4 && !opt_6 && !opt_u) 623 opt_4 = opt_6 = opt_u = 1; 624 if (!opt_c && !opt_l) 625 opt_c = opt_l = 1; 626 627 if (opt_4 || opt_6) { 628 gather_inet(IPPROTO_TCP); 629 gather_inet(IPPROTO_UDP); 630 gather_inet(IPPROTO_DIVERT); 631 } 632 if (opt_u) { 633 gather_unix(SOCK_STREAM); 634 gather_unix(SOCK_DGRAM); 635 } 636 getfiles(); 637 display(); 638 639 exit(0); 640 } 641