/*
 * dsock.c - Linux socket processing functions for lsof
 */


/*
 * Copyright 1994 Purdue Research Foundation, West Lafayette, Indiana
 * 47907.  All rights reserved.
 *
 * Written by Victor A. Abell
 *
 * 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 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 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 1994 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id: dsock.c,v 1.9 96/01/11 12:54:45 abe Exp $";
#endif


#include "lsof.h"


#if	defined(HASUNMINSOCK)
_PROTOTYPE(static void getunixnm,(struct sock *sp, char *bp, char *lp));


/*
 * getunixnm() - get Unix domain socket name from sock structure
 */

static void
getunixnm(sp, bp, lp)
	struct sock *sp;		/* sock structure pointer */
	char *bp;			/* receiving buffer pointer */
	char *lp;			/* receiving buffer limita pointer */
{
	char buf[UNIX_PATH_MAX+1];
	int len;

	if (!sp->protinfo.af_unix.name
	||  kread((KA_T)sp->protinfo.af_unix.name, buf, UNIX_PATH_MAX))
		return;
	buf[UNIX_PATH_MAX] = '\0';
	len = strlen(buf);
	if ((bp + len) > lp) {
		if ((len = lp - bp) < 1)
			return;
	}
	(void) strncpy(bp, buf, len);
	*(bp + len) = '\0';
}
#endif	/* defined(HASUNMINSOCK) */


/*
 * process_isock() -- process inode-based socket
 */

void
process_isock(i)
	struct inode *i;		/* socket's inode pointer */
{
	int af, len;
	char *cp;
	char dev_ch[32];
	struct socket *sp;
	struct sock sk;

#if	LINUXV<1175
	struct socket s;
#endif	/* LINUXV<1175 */

#if	defined(HASUNMINSOCK)
	struct sock ud;
#else	/* !defined(HASUNMINSOCK) */
	struct unix_proto_data ud;
#endif	/* defined(HASUNMINSOCK) */

	(void) strcpy(Lf->type, "sock");
	Lf->inp_ty = 2;

#if	LINUXV<1175
/*
 * Read the socket.
 */
	if (i->i_socket == (struct socket *)NULL) {
		enter_nm("no socket address");
		return;
	}
	if (kread((KA_T)i->i_socket, (char *)&s, sizeof(s))) {
		(void) sprintf(Namech, "can't read socket at %#x",
			i->i_socket);
		enter_nm(Namech);
		return;
	}
	sp = &s;
#else	/* LINUXV>=1175 */
/*
 * Make sure there is a socket structure inside the inode.
 */
	if (!i->i_sock) {
		(void) strcpy(Namech, "no socket in inode");
		enter_nm(Namech);
		return;
	}
	sp = &i->u.socket_i;
#endif	/* LINUXV<1175 */

/*
 * Determine the address family.
 */
	if (Nl[X_INPOPS].n_type
	&&  (unsigned long)sp->ops == Nl[X_INPOPS].n_value)
		af = AF_INET;
	else if (Nl[X_UXPOPS].n_type
	     &&  (unsigned long)sp->ops == Nl[X_UXPOPS].n_value)
		af = AF_UNIX;
	else {
		enter_nm("unknown address family");
		return;
	}
/*
 * Process by address family.
 */
	switch (af) {
	case AF_INET:

	/*
	 * Process Internet socket.
	 */
		if (Fnet)
			Lf->sf |= SELNET;
		(void) strcpy(Lf->type, "inet");
		if (sp->data == (void *)NULL) {
			enter_nm("no further information");
			return;
		}
		if (kread((KA_T)sp->data, (char *)&sk, sizeof(sk))) {
			(void) sprintf(Namech, "can't read sock at %#x",
				sp->data);
			enter_nm(Namech);
			return;
		}
		(void) sprintf(dev_ch, "0x%08x", sp->data);
		enter_dev_ch(dev_ch);
		if (sk.wmem_alloc || sk.rmem_alloc) {
			Lf->sz = (unsigned long)(sk.wmem_alloc + sk.rmem_alloc);
			Lf->sz_def = 1;
			Lf->off_def = 0;
		} else {
			if (sk.protocol == IPPROTO_TCP)
				Lf->off = (unsigned long)sk.sent_seq;
			Lf->off_def = 1;
		}
		printiproto((int)sk.protocol);
		printinaddr((struct in_addr *)&sk.saddr,
			(int)ntohs(sk.dummy_th.source));
		if (sk.daddr != (unsigned long)NULL
		||  sk.dummy_th.dest != 0) {
			(void) strcat(endnm(), "->");
			printinaddr((struct in_addr *)&sk.daddr,
				    (int)ntohs(sk.dummy_th.dest));
		}
		break;
	case AF_UNIX:

	/*
	 * Process Unix socket.
	 */
		if (Funix)
			Lf->sf |= SELUNX;
		(void) strcpy(Lf->type, "unix");
		if (sp->data == (void *)NULL) {
			enter_nm("no further information");
			return;
		}
		if (kread((KA_T)sp->data, (char *)&ud, sizeof(ud))) {
			(void) sprintf(Namech, "can't read unix data at %#x",
				sp->data);
			enter_nm(Namech);
			return;
		}
		(void) sprintf(dev_ch, "0x%08x", sp->data);
		enter_dev_ch(dev_ch);
		Lf->off_def = 1;

#if	defined(HASUNMINSOCK)
		(void) getunixnm(&ud, Namech, &Namech[MAXPATHLEN - 1]);
#else	/* !defined(HASUNMINSOCK) */
		if ((len = ud.sockaddr_len)) {
			if (len >= MAXPATHLEN)
				len = MAXPATHLEN - 1;
			(void) sprintf(Namech, "%.*s", len,
				(char *)&ud.sockaddr_un.sun_path);
		}
#endif	/* defined(HASUNMINSOCK) */

		if (Namech[0] && Sfile) {
			if (is_file_named(Namech, i->i_mode & S_IFMT))
				Lf->sf |= SELNM;
		}

#if	defined(HASUNMINSOCK)
		if (ud.pair
		&&  kread((KA_T)ud.pair, (char *)&ud, sizeof(ud)) == 0) {
			cp = endnm();
			if ((&Namech[MAXPATHLEN - 1] - cp) >= 2) {
				(void) strcpy(cp, "->");
				cp += 2;
			}
			(void) getunixnm(&ud, cp, &Namech[MAXPATHLEN - 1]);
		}
#else	/* !defined(HASUNMINSOCK) */
		if (ud.peerupd
		&&  kread((KA_T)ud.peerupd, (char *)&ud, sizeof(ud)) == 0) {
			cp = endnm();
			len = &Namech[MAXPATHLEN - 1] - cp;
			if (ud.sockaddr_len < len)
				len = ud.sockaddr_len;
			if (len)
				(void) sprintf(cp, "->%.*s", len,
					(char *)&ud.sockaddr_un.sun_path);
		}
#endif	/* defined(HASUNMINSOCK) */

		break;
	}
	if (Namech[0])
		enter_nm(Namech);
}
