/*
 * dsock.c - SunOS (Solairs 1.x and 2.x) 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.19 96/01/18 15:05:19 abe Exp $";
#endif


#include "lsof.h"


#if	defined(solaris)
/*
 * process_socket() - process Solaris socket
 */

void
process_socket(sa, ty)
	struct stdata *sa;		/* stream's data address in kernel */
	char *ty;			/* socket type name */
{
	struct in_addr *ap;
	char dev_ch[32];
	int i, *ip;
	struct ipc_s ic;
	u_short p;
	struct queue q, sh;
	int rq, sq;
	u_short *s;
	struct stdata sd;
	struct tcp_s {			/* should come from kernel source
					 * file ../uts/common/inet/tcp.c */

# if	solaris>=20400
		struct tcp_s *d1[8];
# endif	/* solaris>=20400 */

# if	defined(P101318) && P101318>=32
		struct tcp_s *d1[6];
# endif	/* defined(P101318) && P101318>=32 */

		int d2;
		queue_t *d3[2];
		mblk_t *d4[2];
		u_long d5;
		mblk_t *d6;
		u_long d7;
		u_long tcp_snxt;	/* Senders next seq num */
		u_long tcp_suna;	/* Sender unacknowledged */
		u_long d8[6];
		int tcp_hdr_len;	/* combined TCP/IP header length */
		tcph_t *tcp_tcph;	/* pointer to combined header */
		int d9;
		unsigned int d10;
		int d11;
		mblk_t *d12;
		long d13;
		mblk_t *d14;
		u_long d15;

# if	solaris<20400 && (!defined(P101318) || P101318<32)
		mblk_t *d16;
# endif	/* solaris<20400 && (!defined(P101318) || P101318<32) */

		unsigned int d17;
		u_long tcp_rnxt;	/* Seq we expect to recv next */
		u_long d18[2];
		long d19[2];
		mblk_t *d20[4];
		u_long d21[5];
		long d22[3];
		u_long d23[2];
		u_long tcp_rack;	/* Seq # we have acked */
	} tc;
	int tcs;
	tcph_t th;
	struct ud_s {			/* should come from kernel source
					 * file ../uts/common/inet/udp.c */
		uint dummy1;
		u8 dummy2[2];
		u8 udp_port[2];		/* port bound to this stream */
		u8 udp_src[4];		/* source address of this stream */
	} uc;

	if (Fnet)
		Lf->sf |= SELNET;
        (void) strcpy(Lf->type, "inet");
	Lf->inp_ty = 2;
/*
 * Convert type to upper case protocol name.
 */
	if (ty) {
		for (i = 0; ty[i] != '\0' && i < IPROTOL; i++) {
			if (islower(ty[i]))
				Lf->iproto[i] = toupper(ty[i]);
			else
				Lf->iproto[i] = ty[i];
		}
	} else
		i = 0;
	Lf->iproto[i] = '\0';
/*
 * Read stream head queue structure.
 */
	if ( ! sa
	||  readstdata(sa, &sd) || readsthead(sd.sd_wrq, &sh)) {
		enter_nm("no stream head");
		return;
	}
/*
 * Read IP client structure.
 */
	if ( ! sh.q_ptr || kread((KA_T)sh.q_ptr, (char *)&ic, sizeof(ic))) {
		enter_nm("no IP client structure");
		return;
	}
/*
 * Save q_ptr address as protocol control block address.
 */
	(void) sprintf(dev_ch, "0x%08x", sh.q_ptr);
	enter_dev_ch(dev_ch);
	if (strcmp(Lf->iproto, "UDP") == 0) {

	/*
	 * Save UDP address.
	 */
		ap = (struct in_addr *)&ic.ipc_udp_addr;
		p = (u_short)ic.ipc_udp_port;
		if (ap->s_addr == INADDR_ANY && p == 0 && sh.q_nbsrv) {

		/*
		 * If the ipc_s structure has no local address, read
		 * the ud_s structure for the stream one back.
		 */
			if (!kread((KA_T)sh.q_nbsrv, (char *)&q, sizeof(q))
			&&  q.q_ptr
			&&  !kread((KA_T)q.q_ptr, (char *)&uc, sizeof(uc))) {
				s = (u_short *)&uc.udp_port[0];
				p = *s;
			}
		}
		printinaddr(ap, (int)ntohs(p));
		if (!Fsize)
			Lf->off_def = 1;
	}
	else if (strcmp(Lf->iproto, "TCP") == 0) {

	/*
	 * Save TCP address.
	 */

# if	solaris<20400
		ap = (struct in_addr *)&ic.ipc_tcp_addr[0];
		p = (u_short)ic.ipc_tcp_addr[5];
# else	/* solaris>=20400 */
		ap = (struct in_addr *)&ic.ipc_tcp_laddr;
		p = (u_short)((short *)&ic.ipc_tcp_ports)[1];
# endif	/* solaris<20400 */

		if (sh.q_nbsrv) {

		/*
		 * Read the tcp_s structure.
		 */
			if (!kread((KA_T)sh.q_nbsrv, (char *)&q, sizeof(q))
			&&  q.q_ptr
			&&  !kread((KA_T)q.q_ptr, (char *)&tc, sizeof(tc)))
				tcs = 1;
			else
				tcs = 0;
		}
		if (ap->s_addr == INADDR_ANY && p == 0 && tcs) {

		/*
		 * If the ipc_s structure has no local address, use
		 * the addresses in the tcp_s's tcph structure.
		 */
			if (tc.tcp_hdr_len && tc.tcp_tcph
			&&  kread((KA_T)tc.tcp_tcph, (char *)&th, sizeof(th))
			== 0) {
				s = (u_short *)&th.th_lport[0];
				p = *s;
			}
		}
		printinaddr(ap, (int)ntohs(p));

# if	solaris<20400
		if ((int)ic.ipc_tcp_addr[2] != INADDR_ANY
		|| ic.ipc_tcp_addr[4] != 0) {
			(void) strcat(endnm(), "->");
			printinaddr((struct in_addr *)&ic.ipc_tcp_addr[2],
				(int)ntohs(ic.ipc_tcp_addr[4]));
		}
# else	/* solaris>=20400 */
		if ((int)ic.ipc_tcp_faddr != INADDR_ANY
		|| ((u_short *) &ic.ipc_tcp_ports)[0] != 0)
		{
			(void) strcat(endnm(), "->");
			printinaddr((struct in_addr *)&ic.ipc_tcp_faddr,
				(int)ntohs(((u_short *)&ic.ipc_tcp_ports)[0]));
		}
# endif	/* solaris <20400 */

	/*
	 * Save TCP size information.
	 */
		if (tcs && Fsize) {
			if ((rq = (int)tc.tcp_rnxt - (int)tc.tcp_rack) < 0)
				rq = 0;
			if ((sq = (int)tc.tcp_snxt - (int)tc.tcp_suna - 1) < 0)
				sq = 0;
			if (Lf->access == 'r')
				Lf->sz = (unsigned long)rq;
			else if (Lf->access == 'w')
				Lf->sz = (unsigned long)sq;
			else
				Lf->sz = (unsigned long)(rq + sq);
			Lf->sz_def = 1;
		} else
			Lf->off_def = 1;
	} else {
		if (!Fsize)
			Lf->off_def = 1;
	}
/*
 * Enter name charcters if there are some.
 */
	if (Namech[0])
		enter_nm(Namech);
}
#else	/* !solaris */


/*
 * process_socket() - process non-Solaris socket
 */

void
process_socket(sa)
	caddr_t sa;			/* socket address in kernel */
{
	struct domain d;
	char dev_ch[32];
	int fam;
	struct inpcb inp;
	struct mbuf mb;
	struct protosw p;
	struct rawcb raw;
	struct socket s;
	struct unpcb uc, unp;
	struct sockaddr_un *ua = NULL;
	struct sockaddr_un un;

        (void) strcpy(Lf->type, "sock");
	Lf->inp_ty = 2;
/*
 * Read socket, protocol, and domain structures.
 */
	if (sa == NULL) {
		enter_nm("no socket address");
		return;
	}
        if (kread((KA_T) sa, (char *) &s, sizeof(s))) {
                (void) sprintf(Namech, "can't read socket struct from %#x",
			sa);
                enter_nm(Namech);
                return;
        }
	if ( ! s.so_type) {
                enter_nm("no socket type");
                return;
	}
        if (s.so_proto == NULL
	||  kread((KA_T) s.so_proto, (char *) &p, sizeof(p))) {
                enter_nm("no protocol switch");
                return;
        }
        if (kread((KA_T) p.pr_domain, (char *) &d, sizeof(d))) {
                (void) sprintf(Namech, "can't read domain struct from %#x",
                        p.pr_domain);
                enter_nm(Namech);
                return;
        }
/*
 * Set size, based on access type.
 */
	if (Fsize) {
		if (Lf->access == 'r')
			Lf->sz = (unsigned long)s.so_rcv.sb_cc;
		else if (Lf->access == 'w')
			Lf->sz = (unsigned long)s.so_snd.sb_cc;
		else
			Lf->sz = (unsigned long)s.so_rcv.sb_cc + s.so_snd.sb_cc;
		Lf->sz_def = 1;
	} else
		Lf->off_def = 1;
/*
 * Process socket by the associated domain family.
 */
	switch ((fam = d.dom_family)) {
/*
 * Process an Internet domain socket.
 */
	case AF_INET:
		if (Fnet)
			Lf->sf |= SELNET;
		(void) strcpy(Lf->type, "inet");
		printiproto(p.pr_protocol);
	/*
	 * Read protocol control block.
	 */
		if (s.so_pcb == NULL) {
			enter_nm("no protocol control block");
			return;
		}
		if (s.so_type == SOCK_RAW) {

		/*
		 * Print raw socket information.
		 */
		    if (kread((KA_T) s.so_pcb, (char *)&raw, sizeof(raw))
		    ||  (struct socket *)sa != raw.rcb_socket) {
			(void) sprintf(Namech, "can't read rawcb at %#x",
				s.so_pcb);
			enter_nm(Namech);
			return;
		    }
		    (void) sprintf(dev_ch, "0x%08x",
			(raw.rcb_pcb == NULL) ? s.so_pcb : raw.rcb_pcb);
		    enter_dev_ch(dev_ch);
		    if (raw.rcb_laddr.sa_family == AF_INET)
			printinaddr((struct in_addr *)&raw.rcb_laddr.sa_data[2],
				 -1);
		    else if (raw.rcb_laddr.sa_family)
			printrawaddr(&raw.rcb_laddr);
		    if (raw.rcb_faddr.sa_family == AF_INET) {
			(void) strcat(endnm(), "->");
			printinaddr((struct in_addr *)&raw.rcb_faddr.sa_data[2],
				 -1);
		    } else if (raw.rcb_faddr.sa_family) {
			(void) strcat(endnm(), "->");
			printrawaddr(&raw.rcb_faddr);
		    }
		} else {

		/*
		 * Print Internet socket information.
		 */
		    if (kread((KA_T) s.so_pcb, (char *) &inp, sizeof(inp))
		    ||  (struct socket *)sa != inp.inp_socket) {
			(void) sprintf(Namech, "can't read inpcb at %#x",
			    s.so_pcb);
			enter_nm(Namech);
			return;
		    }
		    (void) sprintf(dev_ch, "0x%08x",
			(inp.inp_ppcb == NULL) ? s.so_pcb : inp.inp_ppcb);
		    enter_dev_ch(dev_ch);
		    printinaddr(&inp.inp_laddr, (int)ntohs(inp.inp_lport));
		    if (inp.inp_faddr.s_addr != INADDR_ANY || inp.inp_fport
		    != 0) {
			(void) strcat(endnm(), "->");
			    printinaddr(&inp.inp_faddr,
				(int)ntohs(inp.inp_fport));
		    }
		}
		break;
/*
 * Process a Unix domain socket.
 */
	case AF_UNIX:
		if (Funix)
			Lf->sf |= SELUNX;
		(void) strcpy(Lf->type, "unix");
	/*
	 * Read Unix protocol control block and the Unix address structure.
	 */
		(void) sprintf(dev_ch, "0x%08x", sa);
		enter_dev_ch(dev_ch);
		if (kread((KA_T) s.so_pcb, (char *) &unp, sizeof(unp))) {
			(void) sprintf(Namech, "can't read unpcb at %#x",
				s.so_pcb);
			break;
		}
		if ((struct socket *)sa != unp.unp_socket) {
			(void) sprintf(Namech, "unp_socket (%#x) mismatch",
				unp.unp_socket);
			break;
		}
		if (unp.unp_addr) {
		    if (kread((KA_T) unp.unp_addr, (char *) &mb, sizeof(mb))) {
			(void) sprintf(Namech,
				"can't read unp_addr at %#x",
				unp.unp_addr);
			break;
		    }
		    ua = (struct sockaddr_un *)(((char *)&mb) + mb.m_off);
		}
		if (ua == NULL) {
			ua = &un;
			(void) bzero((char *)ua, sizeof(un));
			ua->sun_family = AF_UNSPEC;
		}
	/*
	 * Print information on Unix socket that has no address bound
	 * to it, although it may be connected to another Unix domain
	 * socket as a pipe.
	 */
		if (ua->sun_family != AF_UNIX) {
			if (ua->sun_family == AF_UNSPEC) {
				if (unp.unp_conn) {
					if (kread((KA_T) unp.unp_conn,
						(char *) &uc, sizeof(uc))) {
					    (void) sprintf(Namech,
						"can't read unp_conn at %#x",
						unp.unp_conn);
					} else {
					    (void) sprintf(Namech,
						"->0x%08x", uc.unp_socket);
					}
				} else
					(void) strcpy(Namech, "->(none)");
			} else
				(void) sprintf(Namech,
					"unknown sun_family (%d)",
					ua->sun_family);
			break;
		}
		if (ua->sun_path[0]) {
			if (mb.m_len >= sizeof(struct sockaddr_un))
				mb.m_len = sizeof(struct sockaddr_un) - 1;
			*((char *)ua + mb.m_len) = '\0';
			if (Sfile && is_file_named(ua->sun_path, Ntype, VSOCK))
				Lf->sf |= SELNM;
			else
				(void) strcpy(Namech, ua->sun_path);
		} else
			(void) strcpy(Namech, "no address");
		break;
	default:
		printunkaf(fam);
	}
	if (Namech[0])
		enter_nm(Namech);
}
#endif	/* solaris */
