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

    Lf->type = LSOF_FILE_SOCKET;
    /*
     * 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(ctx, 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(ctx, 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(ctx, (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;
        Lf->type = LSOF_FILE_INET;
        printiproto(ctx, (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(ctx, (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(ctx, (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(ctx, (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(ctx, (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(ctx, (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 (Lf->access == LSOF_FILE_ACCESS_READ)
                Lf->sz = (SZOFFTYPE)t.t_iqsize;
            else if (Lf->access == LSOF_FILE_ACCESS_WRITE)
                Lf->sz = (SZOFFTYPE)t.t_qsize;
            else
                Lf->sz = (SZOFFTYPE)(t.t_iqsize + t.t_qsize);
            Lf->sz_def = 1;
        } else if (shs) {
            Lf->sz = (SZOFFTYPE)sh.q_count;
            Lf->sz_def = 1;
        }
        break;

#if OSRV >= 500
    case AF_UNIX:
        if (Funix)
            Lf->sf |= SELUNX;
        Lf->type = LSOF_FILE_UNIX;
        /*
         * Read Unix protocol control block and the Unix address structure.
         */
        enter_dev_ch(print_kptr(sa, (char *)NULL, 0));
        if (s.so_stp && !readstdata((KA_T)s.so_stp, &sd) &&
            !readsthead((KA_T)sd.sd_wrq, &sh)) {
            if (!sh.q_ptr || kread(ctx, (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(ctx, (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(ctx);
    }
    (void)snpf(cp, len, "%s", buf);
    Lf->nma = cp;
}
