/* * dsock.c -- Darwin socket processing functions for libproc-based lsof */ /* * Portions Copyright 2005 Apple Computer, Inc. All rights reserved. * * Copyright 2005 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Allan Nathanson, Apple Computer, Inc., and Victor A. * Abell, Purdue University. * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors, nor Apple Computer, Inc. nor Purdue University * are responsible for any consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either * by explicit claim or by omission. Credit to the authors, Apple * Computer, Inc. and Purdue University must appear in documentation * and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ #ifndef lint static char copyright[] = "@(#) Copyright 2005 Apple Computer, Inc. and Purdue Research Foundation.\nAll rights reserved.\n"; static char *rcsid = "$Id: dsock.c,v 1.6 2011/08/07 22:52:30 abe Exp $"; #endif #include "lsof.h" /* * IPv6_2_IPv4() -- macro to define the address of an IPv4 address contained * in an IPv6 address */ #define IPv6_2_IPv4(v6) (((uint8_t *)((struct in6_addr *)v6)->s6_addr)+12) /* * process_socket() -- process socket file */ static void process_socket_common(si) struct socket_fdinfo *si; { unsigned char *fa = (unsigned char *)NULL; int fam, fp, lp, unl; unsigned char *la = (unsigned char *)NULL; /* * Enter basic socket values. */ (void) snpf(Lf->type, sizeof(Lf->type), "sock"); Lf->inp_ty = 2; /* * Enter basic file information. */ enter_file_info(&si->pfi); /* * Enable size or offset display. */ if (Fsize) { if (Lf->access == 'r') Lf->sz = (SZOFFTYPE)si->psi.soi_rcv.sbi_cc; else if (Lf->access == 'w') Lf->sz = (SZOFFTYPE)si->psi.soi_snd.sbi_cc; else Lf->sz = (SZOFFTYPE)(si->psi.soi_rcv.sbi_cc + si->psi.soi_snd.sbi_cc); Lf->sz_def = 1; } else Lf->off_def = 1; #if defined(HASTCPTPIQ) /* * Enter send and receive queue sizes. */ Lf->lts.rq = si->psi.soi_rcv.sbi_cc; Lf->lts.sq = si->psi.soi_snd.sbi_cc; Lf->lts.rqs = Lf->lts.sqs = (unsigned char)1; #endif /* defined(HASTCPTPIQ) */ #if defined(HASSOOPT) /* * Enter socket options. */ Lf->lts.ltm = (unsigned int)(si->psi.soi_linger & 0xffff); Lf->lts.opt = (unsigned int)(si->psi.soi_options & 0xffff); Lf->lts.pqlen = (unsigned int)si->psi.soi_incqlen; Lf->lts.qlen = (unsigned int)si->psi.soi_qlen; Lf->lts.qlim = (unsigned int)si->psi.soi_qlimit; Lf->lts.rbsz = (unsigned long)si->psi.soi_rcv.sbi_mbmax; Lf->lts.sbsz = (unsigned long)si->psi.soi_snd.sbi_mbmax; Lf->lts.pqlens = Lf->lts.qlens = Lf->lts.qlims = Lf->lts.rbszs = Lf->lts.sbszs = (unsigned char)1; #endif /* defined(HASSOOPT) */ #if defined(HASSOSTATE) /* * Enter socket state. */ Lf->lts.ss = (unsigned int)si->psi.soi_state; #endif /* defined(HASSOSTATE) */ /* * Process socket by its associated domain family. */ switch ((fam = si->psi.soi_family)) { case AF_INET: case AF_INET6: /* * Process IPv[46] sockets. */ (void) snpf(Lf->type, sizeof(Lf->type), (fam == AF_INET) ? "IPv4" : "IPv6"); if ((si->psi.soi_kind != SOCKINFO_IN) && (si->psi.soi_kind != SOCKINFO_TCP)) { break; } /* * Process TCP state inclusions and exclusions, as required. */ if ((si->psi.soi_kind == SOCKINFO_TCP) && (TcpStXn || TcpStIn)) { int tsnx = (int)si->psi.soi_proto.pri_tcp.tcpsi_state + TcpStOff; if ((tsnx >= 0) && (tsnx < TcpNstates)) { if (TcpStXn) { if (TcpStX[tsnx]) { Lf->sf |= SELEXCLF; return; } } if (TcpStIn) { if (TcpStI[tsnx]) TcpStI[tsnx] = 2; else { Lf->sf |= SELEXCLF; return; } } } } /* * Process an Internet domain socket. */ if (Fnet) { if (!FnetTy || ((FnetTy == 4) && (fam == AF_INET)) || ((FnetTy == 6) && (fam == AF_INET6)) ) Lf->sf |= SELNET; } printiproto(si->psi.soi_protocol); if ((si->psi.soi_kind == SOCKINFO_TCP) && si->psi.soi_proto.pri_tcp.tcpsi_tp) { enter_dev_ch(print_kptr((KA_T)si->psi.soi_proto.pri_tcp.tcpsi_tp, (char *)NULL, 0)); } else enter_dev_ch(print_kptr((KA_T)si->psi.soi_pcb, (char *)NULL, 0)); if (fam == AF_INET) { /* * Enter IPv4 address information. */ if (si->psi.soi_kind == SOCKINFO_TCP) { /* * Enter information for a TCP socket. */ la = (unsigned char *)&si->psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_46.i46a_addr4; lp = (int)ntohs(si->psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport); fa = (unsigned char *)&si->psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_46.i46a_addr4; fp = (int)ntohs(si->psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport); } else { /* * Enter information for a non-TCP socket. */ la = (unsigned char *)&si->psi.soi_proto.pri_in.insi_laddr.ina_46.i46a_addr4; lp = (int)ntohs(si->psi.soi_proto.pri_in.insi_lport); fa = (unsigned char *)&si->psi.soi_proto.pri_in.insi_faddr.ina_46.i46a_addr4; fp = (int)ntohs(si->psi.soi_proto.pri_in.insi_fport); } if ((fa && (*fa == INADDR_ANY)) && !fp) { fa = (unsigned char *)NULL; fp = 0; } } else { /* * Enter IPv6 address information */ int v4mapped = 0; if (si->psi.soi_kind == SOCKINFO_TCP) { /* * Enter TCP socket information. */ la = (unsigned char *)&si->psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_6; lp = (int)ntohs(si->psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport); fa = (unsigned char *)&si->psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_6; fp = (int)ntohs(si->psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport); if ((si->psi.soi_proto.pri_tcp.tcpsi_ini.insi_vflag & INI_IPV4) != 0) v4mapped = 1; } else { /* * Enter non-TCP socket information. */ la = (unsigned char *)&si->psi.soi_proto.pri_in.insi_laddr.ina_6; lp = (int)ntohs(si->psi.soi_proto.pri_in.insi_lport); fa = (unsigned char *)&si->psi.soi_proto.pri_in.insi_faddr.ina_6; fp = (int)ntohs(si->psi.soi_proto.pri_in.insi_fport); if ((si->psi.soi_proto.pri_in.insi_vflag & INI_IPV4) != 0) v4mapped = 1; } if (IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *)fa) && !fp) { fa = (unsigned char *)NULL; fp = 0; } if (v4mapped) { /* * Adjust IPv4 addresses mapped in IPv6 addresses. */ fam = AF_INET; if (la) la = (unsigned char *)IPv6_2_IPv4(la); if (fa) fa = (unsigned char *)IPv6_2_IPv4(fa); } } /* * Enter local and remote addresses by address family. */ if (fa || la) (void) ent_inaddr(la, lp, fa, fp, fam); if (si->psi.soi_kind == SOCKINFO_TCP) { /* * Enter a TCP socket definition and its state. */ Lf->lts.type = 0; Lf->lts.state.i = (int)si->psi.soi_proto.pri_tcp.tcpsi_state; /* * Enter TCP options. */ #if defined(HASSOOPT) Lf->lts.kai = (unsigned int)si->psi.soi_proto.pri_tcp.tcpsi_timer[TCPT_KEEP]; #endif /* defined(HASSOOPT) */ #if defined(HASTCPOPT) Lf->lts.mss = (unsigned long)si->psi.soi_proto.pri_tcp.tcpsi_mss; Lf->lts.msss = (unsigned char)1; Lf->lts.topt = (unsigned int)si->psi.soi_proto.pri_tcp.tcpsi_flags; #endif /* defined(HASTCPOPT) */ } break; case AF_UNIX: /* * Process a UNIX domain socket. */ (void) snpf(Lf->type, sizeof(Lf->type), "unix"); if (si->psi.soi_kind != SOCKINFO_UN) break; if (Funix) Lf->sf |= SELUNX; enter_dev_ch(print_kptr((KA_T)si->psi.soi_pcb, (char *)NULL, 0)); /* * Enter information on a UNIX domain socket that has no address bound * to it, although it may be connected to another UNIX domain socket * as a pipe. */ if (si->psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_family != AF_UNIX) { if (si->psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_family == AF_UNSPEC) { if (si->psi.soi_proto.pri_un.unsi_conn_pcb) { (void) snpf(Namech, Namechl, "->%s", print_kptr((KA_T)si->psi.soi_proto.pri_un.unsi_conn_pcb, (char *)NULL, 0)); } else (void) snpf(Namech, Namechl, "->(none)"); } else (void) snpf(Namech, Namechl, "unknown sun_family (%d)", si->psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_family); break; } if (si->psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path[0]) { unl = si->psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_len - offsetof(struct sockaddr_un, sun_path); if ((unl < 0) || (unl >= sizeof(si->psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path))) unl = sizeof(si->psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path) - 1; si->psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path[unl] = '\0'; if (si->psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path[0] && Sfile && is_file_named(si->psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path, 0)) Lf->sf |= SELNM; if (si->psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path[0] && !Namech[0]) (void) snpf(Namech, Namechl, "%s", si->psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path); } else (void) snpf(Namech, Namechl, "no address"); break; case AF_ROUTE: /* * Process a ROUTE domain socket. */ (void) snpf(Lf->type, sizeof(Lf->type), "rte"); if (!Fsize) Lf->off_def = 1; break; case AF_NDRV: /* * Process an NDRV domain socket. */ (void) snpf(Lf->type, sizeof(Lf->type), "ndrv"); if (si->psi.soi_kind != SOCKINFO_NDRV) break; enter_dev_ch(print_kptr((KA_T)si->psi.soi_pcb, (char *)NULL, 0)); si->psi.soi_proto.pri_ndrv.ndrvsi_if_name[sizeof(si->psi.soi_proto.pri_ndrv.ndrvsi_if_name) - 1] = '\0'; (void) snpf(Namech, Namechl, "-> %s%d", si->psi.soi_proto.pri_ndrv.ndrvsi_if_name, si->psi.soi_proto.pri_ndrv.ndrvsi_if_unit); break; case pseudo_AF_KEY: /* * Process an [internal] key-management function socket. */ (void) snpf(Lf->type, sizeof(Lf->type), "key"); enter_dev_ch(print_kptr((KA_T)si->psi.soi_pcb, (char *)NULL, 0)); break; case AF_SYSTEM: /* * Process a SYSTEM domain socket. */ (void) snpf(Lf->type, sizeof(Lf->type), "systm"); if (si->psi.soi_kind != SOCKINFO_KERN_EVENT) break; enter_dev_ch(print_kptr((KA_T)si->psi.soi_pcb, (char *)NULL, 0)); (void) snpf(Namech, Namechl, "[%x:%x:%x]", si->psi.soi_proto.pri_kern_event.kesi_vendor_code_filter, si->psi.soi_proto.pri_kern_event.kesi_class_filter, si->psi.soi_proto.pri_kern_event.kesi_subclass_filter); break; case AF_PPP: /* * Process a PPP domain socket. */ (void) snpf(Lf->type, sizeof(Lf->type), "ppp"); enter_dev_ch(print_kptr((KA_T)si->psi.soi_pcb, (char *)NULL, 0)); break; default: printunkaf(fam, 1); } /* * If there are NAME column characters, enter them. */ if (Namech[0]) enter_nm(Namech); } void process_socket(pid, fd) int pid; /* PID */ int32_t fd; /* FD */ { int nb; struct socket_fdinfo si; /* * Get socket information. */ nb = proc_pidfdinfo(pid, fd, PROC_PIDFDSOCKETINFO, &si, sizeof(si)); if (nb <= 0) { (void) err2nm("socket"); return; } else if (nb < sizeof(si)) { (void) fprintf(stderr, "%s: PID %d, FD %d: proc_pidfdinfo(PROC_PIDFDSOCKETINFO);\n", Pn, pid, fd); (void) fprintf(stderr, " too few bytes; expected %ld, got %d\n", sizeof(si), nb); Exit(1); } process_socket_common(&si); }