blob: 0e8736da0c120c1f40a76d7b2579d9fc23557c53 [file] [log] [blame]
/*
* 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);
}