/*
 * dsock.c - SCO OpenServer socket processing functions for lsof
 */


/*
 * Copyright 1995 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 1995 Purdue Research Foundation.\nAll rights reserved.\n";
#endif


#include "lsof.h"


/*
 * process_socket() - process socket
 */

void
process_socket(i)
	struct inode *i;		/* inode pointer */
{
	char *cp;
	struct domain d;
	unsigned char *fa = (unsigned char *)NULL;
	int fam, j, k;
	int fp, lp;
	unsigned char *la = (unsigned char *)NULL;
	int p;
	struct inpcb pcb;
	short pcbs = 0;
	short  udpsf, udpsl;
	struct socket s;
	KA_T sa, spa;
	struct stdata sd;
	struct queue sh;
	short shs = 0;
	struct tcpcb t;
	short ts = 0;
	struct udpdev udp;
	short udptm = 0;

#if	OSRV<500
	struct sockaddr_in *si;
#else	/* OSRV>=500 */
	struct sockaddr_in si;
	struct un_dev ud;
#endif	/* OSRV<500 */

	(void) snpf(Lf->type, sizeof(Lf->type), "sock");
/*
 * Read socket.
 */
	if (!Socktab) {
	    (void) enter_nm("No kernel socket table");
	    return;
	}
	spa = Socktab + (GET_MIN_DEV(i->i_rdev) * sizeof(struct socket *));
	if (kread(spa, (char *)&sa, sizeof(sa))) {
	    (void) snpf(Namech, Namechl, "can't read socket pointer at %s",
		print_kptr(spa, (char *)NULL, 0));
	    enter_nm(Namech);
	}
	if (kread(sa, (char *)&s, sizeof(s))) {
	    (void) snpf(Namech, Namechl, "can't read socket structure at %s",
		print_kptr(sa, (char *)NULL, 0));
	    enter_nm(Namech);
	    return;
	}
/*
 * Read domain structure.
 */
	if (!s.so_proto.pr_domain
	||  kread((KA_T)s.so_proto.pr_domain, (char *)&d, sizeof(d))) {
	    (void) snpf(Namech, Namechl, "can't read protocol domain from %s",
		print_kptr((KA_T)s.so_proto.pr_domain, (char *)NULL, 0));
	    enter_nm(Namech);
	    return;
	}
/*
 * Process by protocol domain.
 */
	switch ((fam = d.dom_family)) {
	case AF_INET:
	    if (Fnet)
		Lf->sf |= SELNET;
	    (void) snpf(Lf->type, sizeof(Lf->type), "inet");
	    printiproto((int)s.so_proto.pr_protocol);
	    Lf->inp_ty = 2;
	/*
	 * Get protocol control block address from stream head queue structure.
	 */
	    if (s.so_stp
	    &&  !readstdata((KA_T)s.so_stp, &sd)
	    &&  !readsthead((KA_T)sd.sd_wrq, &sh))
		shs = 1;
	    if (shs && sh.q_ptr) {
		enter_dev_ch(print_kptr((KA_T)sh.q_ptr, (char *)NULL, 0));
		if (kread((KA_T)sh.q_ptr, (char *)&pcb, sizeof(pcb)) == 0)
		    pcbs = 1;
	    }
	/*
	 * Print local and remote addresses.
	 */
	    if (pcbs) {
		if (pcb.inp_ppcb && strcasecmp(Lf->iproto, "udp") == 0) {

		/*
		 * If this is a UDP socket file, get the udpdev structure
		 * at the PCB's per-protocol control block address.  It
		 * may contain a foreign address.
		 */
		    if (!kread((KA_T)pcb.inp_ppcb, (char *)&udp, sizeof(udp))) {

#if	OSRV>=500
			if (udp.ud_lsin.sin_addr.s_addr != INADDR_ANY
			||  udp.ud_lsin.sin_port != 0)
			    udpsl = 1;
			else
			    udpsl = 0;
#endif	/* OSRV>=500 */

			if (udp.ud_fsin.sin_addr.s_addr != INADDR_ANY
			||  udp.ud_fsin.sin_port != 0)
			    udpsf = 1;
			else
			    udpsf = 0;
		    }
		} else
		    udpsf = udpsl = 0;
	    /*
	     * Print the local address from the PCB.  If there is none, and if
	     * this is a 5.0.0 or greater UDP stream, and if it has a local
	     * address set, use it.
	     */
		la = (unsigned char *)&pcb.inp_laddr;
		lp = (int)ntohs(pcb.inp_lport);

#if     OSRV>=500
		if (((struct in_addr *)la)->s_addr == INADDR_ANY
		&&  lp == 0 && udpsl) {
		    la = (unsigned char *)&udp.ud_lsin.sin_addr;
		    lp = (int)ntohs(udp.ud_lsin.sin_port);
		}

#endif  /* OSRV>=500 */

	    /*
	     * Use the PCB's foreign address if it is set.  If not, and if this
	     * is a UDP socket file, use the udpdev structure's foreign address
	     * if it's set.
	     */
		if (pcb.inp_faddr.s_addr != INADDR_ANY || pcb.inp_fport != 0) {
		    fa = (unsigned char *)&pcb.inp_faddr;
		    fp = (int)ntohs(pcb.inp_fport);
		} else if (udpsf) {
		    fa = (unsigned char *)&udp.ud_fsin.sin_addr;
		    fp = (int)ntohs(udp.ud_fsin.sin_port);
		    udptm = 1;
		}
		if (la || fa) {
		    (void) ent_inaddr(la, lp, fa, fp, AF_INET);
		    if (udptm && !Lf->nma)
			(void)udp_tm(udp.ud_ftime);
		}
		if (pcb.inp_ppcb && strcasecmp(Lf->iproto, "tcp") == 0
		&&  kread((KA_T)pcb.inp_ppcb, (char *)&t, sizeof(t)) == 0) {
		    ts = 1;
		/*
		 * Save the TCP state from its control block.
		 */
		    Lf->lts.type = 0;
		    Lf->lts.state.i = (int)t.t_state;
		}
	    } else {

#if	OSRV<500
		if ((si = (struct sockaddr_in *)&s.so_name)) {
		    la = (unsigned char *)&si->sin_addr;
		    lp = (int)ntohs(si->sin_port);
		}
		if ((si = (struct sockaddr_in *)&s.so_peer)) {
		    if (si->sin_addr.s_addr != INADDR_ANY || si->sin_port != 0)
		    {
			fa = (unsigned char *)&si->sin_addr;
			fp = (int)ntohs(si->sin_port);
		    }
		}
#else	/* OSRV>=500 */
		if (s.so_name
		&&  !kread((KA_T)s.so_name, (char *)&si, sizeof(si))) {
		    la = (unsigned char *)&si.sin_addr;
		    lp = (int)ntohs(si.sin_port);
		}
		if (s.so_peer
		&&  !kread((KA_T)s.so_peer, (char *)&si, sizeof(si))) {
		    if (si.sin_addr.s_addr != INADDR_ANY || si.sin_port != 0) {
			fa = (unsigned char *)&si.sin_addr;
			fp = (int)ntohs(si.sin_port);
		    }
		}
#endif	/* OSRV<500 */

		if (la || fa)
		    (void) ent_inaddr(la, lp, fa, fp, AF_INET);
	    }
	/*
	 * Save options, sizes, states and values.
	 */

#if	defined(HASSOOPT)
	    Lf->lts.ltm = (unsigned int)s.so_linger;
	    Lf->lts.opt = (unsigned int)s.so_options;
	    Lf->lts.qlen = (unsigned int)s.so_qlen;
	    Lf->lts.qlim = (unsigned int)s.so_qlimit;
	    Lf->lts.qlens = Lf->lts.qlims = (unsigned char)1;
	    if (ts && t.t_timer[TCPT_KEEP]) {
		Lf->lts.opt |= SO_KEEPALIVE;
		Lf->lts.kai = (unsigned long)t.t_timer[TCPT_KEEP];
	    }
#endif	/* defined(HASSOOPT) */

#if	defined(HASSOSTATE)
	    Lf->lts.ss = s.so_state;
#endif	/* defined(HASSOSTATE) */


	    if (ts) {

#if	defined(HASTCPOPT)
		Lf->lts.topt = (unsigned int)t.t_flags;
		Lf->lts.mss = (unsigned long)t.t_maxseg;
		Lf->lts.msss = (unsigned char)1;
#endif	/* defined(HASTCPOPT) */

#if	defined(HASTCPTPIQ)
		Lf->lts.rq = (unsigned long)t.t_iqsize;
		Lf->lts.sq = (unsigned long)t.t_qsize;
		Lf->lts.rqs = Lf->lts.sqs = 1;
#endif	/* defined(HASTCPTPIQ) */

		if (Fsize) {
		    if (Lf->access == 'r')
			Lf->sz = (SZOFFTYPE)t.t_iqsize;
		    else if (Lf->access == 'w')
			Lf->sz = (SZOFFTYPE)t.t_qsize;
		    else
			Lf->sz = (SZOFFTYPE)(t.t_iqsize + t.t_qsize);
		    Lf->sz_def = 1;
		} else
		    Lf->off_def = 1;
	    } else if (shs) {
		if (Fsize) {
		    Lf->sz = (SZOFFTYPE)sh.q_count;
		    Lf->sz_def = 1;
		} else
		    Lf->off_def = 1;
	    } else
		Lf->off_def = 1;
	    break;

#if	OSRV>=500
	case AF_UNIX:
	    if (Funix)
		Lf->sf |= SELUNX;
	    (void) snpf(Lf->type, sizeof(Lf->type), "unix");
	/*
	 * Read Unix protocol control block and the Unix address structure.
	 */
	    enter_dev_ch(print_kptr(sa, (char *)NULL, 0));
	    Lf->off_def = 1;
	    if (s.so_stp
	    &&  !readstdata((KA_T)s.so_stp, &sd)
	    &&  !readsthead((KA_T)sd.sd_wrq, &sh)) {
		if (!sh.q_ptr
		||  kread((KA_T)sh.q_ptr, (char *)&ud, sizeof(ud)))
		{
		    (void) snpf(Namech, Namechl, "can't read un_dev from %s",
			print_kptr((KA_T)sh.q_ptr, (char *)NULL, 0));
		    break;
		}
		if (ud.so_rq)
		    enter_dev_ch(print_kptr((KA_T)ud.so_rq, (char *)NULL, 0));
		if (ud.local_addr.sun_family == AF_UNIX) {
		    Lf->inode = (unsigned long)ud.bnd_param.user_addr.inode_no;
		    Lf->inp_ty = 1;
		    ud.local_addr.sun_path[sizeof(ud.local_addr.sun_path) - 1]
			= '\0';
		    if (Sfile && is_file_named(ud.local_addr.sun_path, 0))
			Lf->sf |= SELNM;
		    if (!Namech[0])
			(void) snpf(Namech,Namechl,"%s",ud.local_addr.sun_path);
		} else if (ud.for_addr.sun_family == AF_UNIX) {
		    Lf->inode = (unsigned long)ud.bnd_param.user_addr.inode_no;
		    Lf->inp_ty = 1;
		    ud.for_addr.sun_path[sizeof(ud.for_addr.sun_path) - 1]
			= '\0';
		    if (Sfile && is_file_named(ud.for_addr.sun_path, 0))
			Lf->sf |= SELNM;
		    else
			(void) snpf(Namech,Namechl,"%s",ud.for_addr.sun_path);
		} else if (ud.other_q)
		    (void) snpf(Namech, Namechl, "->%s",
			print_kptr((KA_T)ud.other_q, (char *)NULL, 0));
	    } else
		(void) snpf(Namech, Namechl, "can't get un_dev");
	    break;
#endif	/* OSRV>=500 */

	default:
	    printunkaf(fam, 1);
	}
	enter_nm(Namech);
}


/*
 * udp_tm() - compute time since UDP packet was last sent
 */

void
udp_tm(tm)
	time_t tm;			/* time when packet was sent */
{
	static char buf[32], *cp;
	time_t et, lbolt;
	MALLOC_S len;
	short hr, min, sec;
/*
 * Read the lightning bolt timer and compute the elapsed time.
 * No elapsed time is returned if:
 *	the global clock frequency variable, Hz, is negative;
 *	the lightning bolt timer is unavailable;
 *	the lightning bolt time is less than the UDP send time;
 *	the elapsed time is zero.
 */
	if (!Lbolt)
	    return;
	if (Hz < 0
	||  kread((KA_T)Lbolt, (char *)&lbolt, sizeof(lbolt))
	||  tm >= lbolt
	||  (et = (time_t)((lbolt - tm) / Hz)) == 0)
	    return;
/*
 * If the time is 100 hours or greater, return the elapsed time as seconds.
 */
	if (et >= (100 * 60 * 60)) {
	    (void) snpf(buf, sizeof(buf), "%lds", et);
	    cp = &buf[strlen(buf)];
	} else {

	/*
	 * Convert seconds to hours, minutes and seconds.
	 */
	    hr = (short)(et / (60 * 60));
	    et %= (60 * 60);
	    min = (short)(et / 60);
	    sec = (short)(et % 60);
	    cp = buf;
	/*
 	 * Format the elapsed time and attach single character suffixes to
	 * represent the units:
	 *
	 *    `h' = hours
	 *    `m' = minutes
	 *    `s' = seconds
 	 */
	    if (hr) {
		(void) snpf(cp, sizeof(buf) - (cp - buf), "%dh", hr);
		cp += 2 + ((hr > 9) ? 1 : 0);
	    }
	    if (min) {
		(void) snpf(cp, sizeof(buf) - (cp - buf), "%dm", min);
		cp += 2 + ((min > 9) ? 1 : 0);
	    }
	    if (sec) {
		(void) snpf(cp, sizeof(buf) - (cp - buf), "%ds", sec);
		cp += 2 + ((sec > 9) ? 1 : 0);
	    }
	}
/*
 * Add the `` ago'' trailer.  Return the string's address and length.
 */
	(void) snpf(cp, sizeof(buf) - (cp - buf), " ago");
	len = (MALLOC_S)(strlen(buf) + 1);
	if (len < 2)
	    return;
	if (!(cp = (char *)malloc(len))) {
	    (void) fprintf(stderr, "%s: no space for %d character UDP time\n",
		Pn, len);
	    Error();
	}
	(void) snpf(cp, len, "%s", buf);
	Lf->nma = cp;
}
