/*
 * dsock.c - HP-UX 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.13 96/01/11 12:54:38 abe Exp $";
#endif


#include "lsof.h"


#if     _HPUXV>=800 && defined(HPUX_CCITT)
#include <x25/x25addrstr.h>
#include <x25/x25stat.h>
#include <x25/x25str.h>
#include <x25/x25config.h>
#include <x25/x25L3.h>
#endif	/* _HPUXV>=800 && defined(HPUX_CCITT) */


#if	defined(DTYPE_LLA)
/*
 * process_lla() - process link level access socket file
 */

void
process_lla(la)
	caddr_t la;			/* link level CB address in kernel */
{
	char dev_ch[32];
	struct lla_cb lcb;

	(void) strcpy(Lf->type, "lla");
	Lf->inp_ty = 2;
	(void) sprintf(dev_ch, "0x%08x", la);
	enter_dev_ch(dev_ch);
/*
 * Read link level access control block.
 */
	if (la == NULL
	||  kread((KA_T)la, (char *)&lcb, sizeof(lcb))) {
		(void) sprintf(Namech, "can't read LLA CB (%#x)", la);
		enter_nm(Namech);
		return;
	}
/*
 * Determine access mode.
 */
	if ((lcb.lla_flags & LLA_FWRITE | LLA_FREAD) == LLA_FWRITE)
		Lf->access = 'w';
	else if ((lcb.lla_flags & LLA_FWRITE | LLA_FREAD) == LLA_FREAD)
		Lf->access = 'r';
	else if (lcb.lla_flags & LLA_FWRITE | LLA_FREAD)
		Lf->access = 'u';
/*
 * Determine the open mode, if possible.
 */
	if (lcb.lla_flags & LLA_IS_ETHER)
		(void) strcpy(Lf->iproto, "Ether");
	else if (lcb.lla_flags & (LLA_IS_8025|LLA_IS_SNAP8025|LLA_IS_FA8025)) {
		(void) strcpy(Lf->iproto, "802.5");
		if (lcb.lla_flags & LLA_IS_SNAP8025)
			(void) strcpy(Namech, "SNAP");
		else if (lcb.lla_flags & LLA_IS_FA8025)
			(void) strcpy(Namech, "function address");
	}
/*
 * Add any significant flags.
 */
	if (lcb.lla_flags & ~(LLA_FWRITE | LLA_FREAD))
		(void) sprintf(endnm(), "%s(flags = %#x)",
			(Namech[0] == '\0') ? "" : " ", lcb.lla_flags);
	if (Namech[0])
		enter_nm(Namech);
}
#endif	/* DTYPE_LLA */


/*
 * process_socket() - process socket
 */

void
process_socket(sa)
	caddr_t sa;			/* socket address in kernel */
{
	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;

#if	_HPUXV>=800
	struct domain d;

# if	defined(HPUX_CCITT)
	char *cp;
	int i;
	struct x25pcb xp;
	struct x25pcb_extension xpe;
# endif	/* defined(HPUX_CCITT) */
#endif	/* _HPUXV>=800 */

        (void) strcpy(Lf->type, "sock");
	Lf->inp_ty = 2;
/*
 * Read socket structure.
 */
	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;
        }
/*
 * Read protocol switch and domain structure (HP-UX 8 and above).
 */
	if ( ! s.so_type) {
                (void) strcpy(Namech, "no socket type");
                enter_nm(Namech);
                return;
	}
        if (s.so_proto == NULL
	||  kread((KA_T) s.so_proto, (char *) &p, sizeof(p))) {
                (void) strcpy(Namech, "no protocol switch");
                enter_nm(Namech);
                return;
        }

#if	_HPUXV>=800
        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;
        }
#endif	/* _HPUXV>=800 */

/*
 * 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.
 */

#if	_HPUXV>=800
	switch ((fam = d.dom_family))
#else	/* _HPUXV<800 */
	switch ((fam = p.pr_family))
#endif	/* _HPUXV>=800 */

	{

# if	_HPUXV>=800 && defined(HPUX_CCITT)
/*
 * Process an HP-UX [89].x CCITT X25 domain socket.
 */
	case AF_CCITT:
		if (Fnet)
			Lf->sf |= SELNET;
		(void) strcpy(Lf->type, "x.25");
		(void) sprintf(Lf->iproto, "%.*s", IPROTOL, "CCITT");
	/*
	 * Get the X25 PCB and its extension.
	 */
		if (s.so_pcb == NULL) {
			enter_nm("no x.25 protocol control block");
			return;
		}
		if (kread((KA_T)s.so_pcb, (char *)&xp, sizeof(xp))) {
			(void) sprintf(Namech, "can't read x.25 pcb at %#x",
				s.so_pcb);
			enter_nm(Namech);
			return;
		}
		(void) sprintf(dev_ch, "0x%08x", s.so_pcb);
		enter_dev_ch(dev_ch);
		if (xp.x25pcb_extend == NULL
		||  kread((KA_T)xp.x25pcb_extend, (char *)&xpe, sizeof(xpe))) {
			(void) sprintf(Namech,
				"can't read x.25 pcb (%#x) extension at %#x",
				s.so_pcb, xp.x25pcb_extend);
			enter_nm(Namech);
			return;
		}
	/*
	 * Format local address.
	 */
		for (i = 0, cp = Namech;
		     i < xpe.x25pcbx_local_addr.x25hostlen/2;
		     i++, cp += 2)
		{
			(void) sprintf(cp, "%02x",
				xpe.x25pcbx_local_addr.x25_host[i]);
		}
		if (i*2 != xpe.x25pcbx_local_addr.x25hostlen) {
			(void) sprintf(cp, "%01x",
				xpe.x25pcbx_local_addr.x25_host[i] >> 4);
			cp++;
		}
	/*
	 * Display the virtual connection number, if it's defined.
	 */
		if (xp.x25pcb_vcn >= 0)
			(void) sprintf(cp, ":%d", xp.x25pcb_vcn + 1);
	/*
	 * Format peer address, if there is one.
	 */
		if (xpe.x25pcbx_peer_addr.x25hostlen > 0) {
			(void) strcpy(endnm(), "->");
			for (i = 0, cp = endnm();
			     i < xpe.x25pcbx_peer_addr.x25hostlen/2;
			     i++, cp += 2)
			{
				(void) sprintf(cp, "%02x",
					xpe.x25pcbx_peer_addr.x25_host[i]);
			}
			if (i*2 != xpe.x25pcbx_peer_addr.x25hostlen) {
				(void) sprintf(cp, "%01x",
					xpe.x25pcbx_peer_addr.x25_host[i] >> 4);
			}
		}
		enter_nm(Namech);
		break;
# endif	/* _HPUXV>=800 && defined(HPUX_CCITT) */

/*
 * 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, 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);
}
