/*
 * dsock.c - RISCos 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.3 96/01/11 12:55:17 abe Exp $";
#endif


#include "lsof.h"


/*
 * process_socket() - process 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 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;
        }
	if ( ! s.so_type) {
                enter_nm("no socket type");
                return;
	}
/*
 * Read protocol switch and domain structure.
 */
        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 (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", s.so_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);
}
