root/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_pmap.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1991, 1999 by Sun Microsystems, Inc.
 * All rights reserved.
 */
/*
 * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
 */

#include <sys/types.h>
#include <sys/errno.h>
#include <setjmp.h>
#include <sys/tiuser.h>
#include <string.h>

#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
#include <rpc/clnt.h>
#include <rpc/rpc_msg.h>
#include <rpc/pmap_prot.h>
#include "snoop.h"

/*
 * Number of bytes to display from a string (address, netid, etc.).
 */
#define MAXSTRINGLEN    64

extern char *dlc_header;
extern jmp_buf xdr_err;

static void interpret_pmap_2(int, int, int, int, int, char *, int);
static void interpret_pmap_4(int, int, int, int, int, char *, int);
static void stash_callit(ulong_t, int, int, int, int);

void
interpret_pmap(flags, type, xid, vers, proc, data, len)
        int flags, type, xid, vers, proc;
        char *data;
        int len;
{
        switch (vers) {
        case 2: interpret_pmap_2(flags, type, xid, vers, proc, data, len);
                break;

        /* Version 3 is a subset of version 4 */
        case 3:
        case 4: interpret_pmap_4(flags, type, xid, vers, proc, data, len);
                break;
        }
}

void show_pmap();
char *sum_pmaplist();
void show_pmaplist();

static char *procnames_short_2[] = {
        "Null",         /* 0 */
        "SET",          /* 1 */
        "UNSET",        /* 2 */
        "GETPORT",      /* 3 */
        "DUMP",         /* 4 */
        "CALLIT",       /* 5 */
};

static char *procnames_long_2[] = {
        "Null procedure",       /* 0 */
        "Set port",             /* 1 */
        "Unset port",           /* 2 */
        "Get port number",      /* 3 */
        "Dump the mappings",    /* 4 */
        "Indirect call",        /* 5 */
};

#define MAXPROC_2       5

void
interpret_pmap_2(flags, type, xid, vers, proc, data, len)
        int flags, type, xid, vers, proc;
        char *data;
        int len;
{
        char *line;
        unsigned port, proto;
        unsigned iprog, ivers, iproc, ilen;
        extern int pi_frame;
        struct cache_struct *x, *find_callit();
        int trailer_done = 0;

        if (proc < 0 || proc > MAXPROC_2)
                return;

        if (proc == PMAPPROC_CALLIT) {
                if (type == CALL) {
                        iprog = getxdr_u_long();
                        ivers = getxdr_u_long();
                        iproc = getxdr_u_long();
                        stash_callit(xid, pi_frame, iprog, ivers, iproc);
                } else {
                        x = find_callit(xid);
                }
        }

        if (flags & F_SUM) {
                if (setjmp(xdr_err)) {
                        return;
                }

                line = get_sum_line();

                if (type == CALL) {
                        (void) sprintf(line, "PORTMAP C %s",
                            procnames_short_2[proc]);
                        line += strlen(line);
                        switch (proc) {
                        case PMAPPROC_GETPORT:
                                iprog = getxdr_u_long();
                                ivers = getxdr_u_long();
                                proto = getxdr_u_long();
                                (void) sprintf(line,
                                    " prog=%d (%s) vers=%d proto=%s",
                                    iprog, nameof_prog(iprog),
                                    ivers,
                                    getproto(proto));
                                break;
                        case PMAPPROC_CALLIT:
                                (void) sprintf(line,
                                    " prog=%s vers=%d proc=%d",
                                    nameof_prog(iprog),
                                    ivers, iproc);
                                if (flags & F_ALLSUM) {
                                        (void) getxdr_u_long(); /* length */
                                        data += 16; /* prog+ver+proc+len */
                                        len -= 16;
                                        protoprint(flags, type, xid,
                                            iprog, ivers, iproc,
                                            data, len);
                                }
                                break;
                        default:
                                break;
                        }
                        check_retransmit(line, xid);
                } else {
                        (void) sprintf(line, "PORTMAP R %s ",
                            procnames_short_2[proc]);
                        line += strlen(line);
                        switch (proc) {
                        case PMAPPROC_GETPORT:
                                port = getxdr_u_long();
                                (void) sprintf(line, "port=%d", port);
                                break;
                        case PMAPPROC_DUMP:
                                (void) sprintf(line, "%s", sum_pmaplist());
                                break;
                        case PMAPPROC_CALLIT:
                                port = getxdr_u_long();
                                ilen = getxdr_u_long();
                                (void) sprintf(line, "port=%d len=%d",
                                    port, ilen);
                                if (flags & F_ALLSUM && x != NULL) {
                                        data += 8; /* port+len */
                                        len -= 8;
                                        protoprint(flags, type, xid,
                                            x->xid_prog,
                                            x->xid_vers,
                                            x->xid_proc,
                                            data, len);
                                }
                                break;
                        default:
                                break;
                        }
                }
        }

        if (flags & F_DTAIL) {
                show_header("PMAP:  ", "Portmapper", len);
                show_space();
                if (setjmp(xdr_err)) {
                        return;
                }
                (void) sprintf(get_line(0, 0),
                        "Proc = %d (%s)",
                        proc, procnames_long_2[proc]);
                if (type == CALL) {
                        switch (proc) {
                        case PMAPPROC_NULL:
                        case PMAPPROC_SET:
                        case PMAPPROC_UNSET:
                                break;
                        case PMAPPROC_GETPORT:
                                iprog = getxdr_u_long();
                                (void) sprintf(get_line(0, 0),
                                    "Program = %d (%s)",
                                    iprog, nameof_prog(iprog));
                                (void) showxdr_u_long("Version = %d");
                                proto = getxdr_u_long();
                                (void) sprintf(get_line(0, 0),
                                    "Protocol = %d (%s)",
                                    proto, getproto(proto));
                                break;
                        case PMAPPROC_DUMP:
                                break;
                        case PMAPPROC_CALLIT:
                                (void) sprintf(get_line(0, 0),
                                    "Program = %d (%s)",
                                    iprog, nameof_prog(iprog));
                                (void) sprintf(get_line(0, 0),
                                    "Version = %d", ivers);
                                (void) sprintf(get_line(0, 0),
                                    "Proc    = %d", iproc);
                                (void) showxdr_u_long("Callit data = %d bytes");
                                show_trailer();
                                trailer_done = 1;
                                data += 16; /* prog+ver+proc+len */
                                len -= 16;
                                protoprint(flags, type, xid,
                                    iprog, ivers, iproc,
                                    data, len);
                                break;
                        }
                } else {
                        switch (proc) {
                        case PMAPPROC_NULL:
                        case PMAPPROC_SET:
                        case PMAPPROC_UNSET:
                                break;
                        case PMAPPROC_GETPORT:
                                (void) showxdr_u_long("Port = %d");
                                break;
                        case PMAPPROC_DUMP:
                                show_pmaplist();
                                break;
                        case PMAPPROC_CALLIT:
                                (void) showxdr_u_long("Port = %d");
                                (void) showxdr_u_long("Length = %d bytes");
                                show_trailer();
                                trailer_done = 1;
                                if (x != NULL) {
                                        protoprint(flags, type, xid,
                                            x->xid_prog,
                                            x->xid_vers,
                                            x->xid_proc,
                                            data, len);
                                }
                                break;
                        }
                }
                if (!trailer_done)
                        show_trailer();
        }
}

char *
sum_pmaplist()
{
        int maps = 0;
        static char buff[16];

        if (setjmp(xdr_err)) {
                (void) sprintf(buff, "%d+ map(s) found", maps);
                return (buff);
        }

        while (getxdr_u_long()) {
                (void) getxdr_u_long(); /* program */
                (void) getxdr_u_long(); /* version */
                (void) getxdr_u_long(); /* protocol */
                (void) getxdr_u_long(); /* port */
                maps++;
        }

        (void) sprintf(buff, "%d map(s) found", maps);
        return (buff);
}

void
show_pmaplist()
{
        unsigned prog, vers, proto, port;
        int maps = 0;

        if (setjmp(xdr_err)) {
                (void) sprintf(get_line(0, 0),
                    " %d+ maps. (Frame is incomplete)",
                    maps);
                return;
        }

        (void) sprintf(get_line(0, 0), " Program Version Protocol   Port");

        while (getxdr_u_long()) {
                prog  = getxdr_u_long();
                vers  = getxdr_u_long();
                proto = getxdr_u_long();
                port  = getxdr_u_long();
                (void) sprintf(get_line(0, 0),
                    "%8d%8d%9d%7d  %s",
                    prog, vers, proto, port, nameof_prog(prog));
                maps++;
        }

        (void) sprintf(get_line(0, 0), " %d maps", maps);
}

/*
 * ******************************************
 */
char *sum_rpcblist();
void show_rpcblist();
char *sum_rpcb_entry_list();
void show_rpcb_entry_list();

static char *procnames_short_4[] = {
        /*
         * version 3 and 4 procs
         */
        "Null",         /* 0 */
        "SET",          /* 1 */
        "UNSET",        /* 2 */
        "GETADDR",      /* 3 */
        "DUMP",         /* 4 */
        "BCAST",        /* 5 */
        "GETTIME",      /* 6 */
        "UADDR2TADDR",  /* 7 */
        "TADDR2UADDR",  /* 8 */
        /*
         * version 4 procs only
         */
        "GETVERSADDR",  /* 9 */
        "INDIRECT",     /* 10 */
        "GETADDRLIST",  /* 11 */
        "GETSTAT",      /* 12 */
};

static char *procnames_long_4[] = {
        /*
         * version 3 and 4 procs
         */
        "Null procedure",                       /* 0 */
        "Set address",                          /* 1 */
        "Unset address",                        /* 2 */
        "Get address",                          /* 3 */
        "Dump the mappings",                    /* 4 */
        "Broadcast call (no error)",            /* 5 */
        "Get the time",                         /* 6 */
        "Universal to transport address",       /* 7 */
        "Transport to universal address",       /* 8 */
        /*
         * version 4 procs only
         */
        "Get address of specific version",      /* 9 */
        "Indirect call (return error)",         /* 10 */
        "Return addresses of prog/vers",        /* 11 */
        "Get statistics",                       /* 12 */
};

#define MAXPROC_3               8
#define MAXPROC_4               12
#define RPCBPROC_NULL           0

void
interpret_pmap_4(flags, type, xid, vers, proc, data, len)
        int flags, type, xid, vers, proc;
        char *data;
        int len;
{
        char *line;
        unsigned prog, ver;
        char buff1[MAXSTRINGLEN + 1];
        int iprog, ivers, iproc, ilen;
        extern int pi_frame;
        struct cache_struct *x, *find_callit();
        int trailer_done = 0;

        if (proc < 0 || proc > MAXPROC_4 || (vers == 3 && proc > MAXPROC_3))
                return;

        if (proc == RPCBPROC_BCAST || proc == RPCBPROC_INDIRECT) {
                if (type == CALL) {
                        iprog = getxdr_u_long();
                        ivers = getxdr_u_long();
                        iproc = getxdr_u_long();
                        stash_callit(xid, pi_frame,
                                iprog, ivers, iproc);
                } else {
                        x = find_callit(xid);
                }
        }

        if (flags & F_SUM) {
                if (setjmp(xdr_err)) {
                        return;
                }

                line = get_sum_line();

                if (type == CALL) {
                        (void) sprintf(line,
                                "RPCBIND C %s",
                                procnames_short_4[proc]);
                        line += strlen(line);
                        switch (proc) {
                        case RPCBPROC_SET:
                        case RPCBPROC_UNSET:
                        case RPCBPROC_GETADDR:
                        case RPCBPROC_GETVERSADDR:
                        case RPCBPROC_GETADDRLIST:
                                prog = getxdr_u_long();
                                ver  = getxdr_u_long();
                                (void) sprintf(line,
                                        " prog=%d (%s) vers=%d",
                                        prog, nameof_prog(prog),
                                        ver);
                                break;
                        case RPCBPROC_BCAST:
                        case RPCBPROC_INDIRECT:
                                (void) sprintf(line,
                                        " prog=%s vers=%d proc=%d",
                                        nameof_prog(iprog),
                                        ivers, iproc);
                                if (flags & F_ALLSUM) {
                                        (void) getxdr_u_long(); /* length */
                                        data += 16; /* prog+ver+proc+len */
                                        len -= 16;
                                        protoprint(flags, type, xid,
                                                iprog, ivers, iproc,
                                                data, len);
                                }
                                break;
                        default:
                                break;
                        }

                        check_retransmit(line, xid);
                } else {
                        int pos;

                        (void) sprintf(line, "RPCBIND R %s ",
                                procnames_short_4[proc]);
                        line += strlen(line);
                        switch (proc) {
                        case RPCBPROC_GETADDR:
                        case RPCBPROC_TADDR2UADDR:
                        case RPCBPROC_GETVERSADDR:
                                (void) getxdr_string(buff1, MAXSTRINGLEN);
                                (void) sprintf(line,
                                        " Uaddr=%s",
                                        buff1);
                                break;
                        case RPCBPROC_BCAST:
                        case RPCBPROC_INDIRECT:
                                pos = getxdr_pos();
                                (void) getxdr_string(buff1, MAXSTRINGLEN);
                                ilen = getxdr_u_long();
                                (void) sprintf(line, "Uaddr=%s len=%d",
                                        buff1, ilen);
                                if (flags & F_ALLSUM && x != NULL) {
                                        pos = getxdr_pos() - pos;
                                        data += pos; /* uaddr+len */
                                        len -= pos;
                                        protoprint(flags, type, xid,
                                                x->xid_prog,
                                                x->xid_vers,
                                                x->xid_proc,
                                                data, len);
                                }
                                break;
                        case RPCBPROC_DUMP:
                                (void) sprintf(line, "%s",
                                        sum_rpcblist());
                                break;
                        case RPCBPROC_GETTIME:
                                {
                                        time_t sec = getxdr_long();
                                        struct tm *tmp = gmtime(&sec);
                                        (void) strftime(line, MAXLINE,
                                            "%d-%h-%y %T GMT", tmp);
                                }
                                break;
                        case RPCBPROC_GETADDRLIST:
                                (void) sprintf(line, "%s",
                                        sum_rpcb_entry_list());
                                break;
                        default:
                                break;
                        }
                }
        }

        if (flags & F_DTAIL) {
                show_header("RPCB:  ", "RPC Bind", len);
                show_space();
                if (setjmp(xdr_err)) {
                        return;
                }
                (void) sprintf(get_line(0, 0),
                        "Proc = %d (%s)",
                        proc, procnames_long_4[proc]);
                if (type == CALL) {
                        switch (proc) {
                        case RPCBPROC_NULL:
                                break;
                        case RPCBPROC_SET:
                        case RPCBPROC_UNSET:
                        case RPCBPROC_GETADDR:
                        case RPCBPROC_GETVERSADDR:
                        case RPCBPROC_GETADDRLIST:
                                (void) showxdr_u_long("Program = %d");
                                (void) showxdr_u_long("Version = %d");
                                (void) showxdr_string(64, "Netid   = %s");
                                break;
                        case RPCBPROC_DUMP:
                                break;
                        case RPCBPROC_BCAST:
                        case RPCBPROC_INDIRECT:
                                (void) sprintf(get_line(0, 0),
                                        "Program = %d (%s)",
                                        iprog, nameof_prog(iprog));
                                (void) sprintf(get_line(0, 0),
                                        "Version = %d", ivers);
                                (void) sprintf(get_line(0, 0),
                                        "Proc    = %d", iproc);
                                (void) showxdr_u_long(
                                        "Callit data = %d bytes");
                                show_trailer();
                                trailer_done = 1;
                                data += 16; /* prog+ver+proc+len */
                                len -= 16;
                                protoprint(flags, type, xid,
                                        iprog, ivers, iproc,
                                        data, len);
                                break;
                        case RPCBPROC_GETTIME:
                                break;
                        case RPCBPROC_UADDR2TADDR:
                        case RPCBPROC_TADDR2UADDR:
                                break;
                        }
                } else {
                        switch (proc) {
                        case RPCBPROC_NULL:
                        case RPCBPROC_SET:
                        case RPCBPROC_UNSET:
                                break;
                        case RPCBPROC_GETADDR:
                        case RPCBPROC_TADDR2UADDR:
                        case RPCBPROC_GETVERSADDR:
                                (void) showxdr_string(64, "Uaddr = %s");
                                break;
                        case RPCBPROC_DUMP:
                                show_rpcblist();
                                break;
                        case RPCBPROC_BCAST:
                        case RPCBPROC_INDIRECT:
                                (void) showxdr_string(64, "Uaddr = %s");
                                (void) showxdr_u_long("Length = %d bytes");
                                show_trailer();
                                trailer_done = 1;
                                if (x != NULL) {
                                        protoprint(flags, type, xid,
                                                x->xid_prog,
                                                x->xid_vers,
                                                x->xid_proc,
                                                data, len);
                                }
                                break;
                        case RPCBPROC_GETTIME:
                                {
                                        int pos = getxdr_pos();
                                        time_t sec = getxdr_long();
                                        struct tm *tmp = gmtime(&sec);
                                        (void) strftime(get_line(pos,
                                            getxdr_pos()), MAXLINE,
                                            "Time = %d-%h-%y %T GMT", tmp);
                                }
                                break;
                        case RPCBPROC_UADDR2TADDR:
                                break;
                        case RPCBPROC_GETADDRLIST:
                                show_rpcb_entry_list();
                                break;
                        }
                }
                if (!trailer_done)
                        show_trailer();
        }
}

char *
sum_rpcblist()
{
        int maps = 0;
        static char buff[MAXSTRINGLEN + 1];

        if (setjmp(xdr_err)) {
                (void) sprintf(buff, "%d+ map(s) found", maps);
                return (buff);
        }

        while (getxdr_u_long()) {
                (void) getxdr_u_long();         /* program */
                (void) getxdr_u_long();         /* version */
                (void) getxdr_string(buff, MAXSTRINGLEN); /* netid */
                (void) getxdr_string(buff, MAXSTRINGLEN); /* uaddr */
                (void) getxdr_string(buff, MAXSTRINGLEN); /* owner */
                maps++;
        }

        (void) sprintf(buff, "%d map(s) found", maps);
        return (buff);
}

void
show_rpcblist()
{
        unsigned prog, vers;
        char netid[MAXSTRINGLEN + 1], uaddr[MAXSTRINGLEN + 1];
        char owner[MAXSTRINGLEN + 1];
        int maps = 0;

        if (setjmp(xdr_err)) {
                (void) sprintf(get_line(0, 0),
                    " %d+ maps. (Frame is incomplete)",
                    maps);
                return;
        }

        show_space();
        (void) sprintf(get_line(0, 0),
            " Program Vers Netid        Uaddr              Owner");

        while (getxdr_u_long()) {
                prog  = getxdr_u_long();
                vers  = getxdr_u_long();
                (void) getxdr_string(netid, MAXSTRINGLEN);
                (void) getxdr_string(uaddr, MAXSTRINGLEN);
                (void) getxdr_string(owner, MAXSTRINGLEN);
                (void) sprintf(get_line(0, 0),
                    "%8d%5d %-12s %-18s %-10s (%s)",
                    prog, vers,
                    netid, uaddr, owner,
                    nameof_prog(prog));
                maps++;
        }

        (void) sprintf(get_line(0, 0), " (%d maps)", maps);
}

char *
sum_rpcb_entry_list()
{
        int maps = 0;
        static char buff[MAXSTRINGLEN + 1];

        if (setjmp(xdr_err)) {
                (void) sprintf(buff, "%d+ map(s) found", maps);
                return (buff);
        }

        while (getxdr_u_long()) {
                (void) getxdr_string(buff, MAXSTRINGLEN); /* maddr      */
                (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_netid   */
                (void) getxdr_u_long();                   /* nc_semantics */
                (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_protofmly */
                (void) getxdr_string(buff, MAXSTRINGLEN); /* nc_proto   */
                maps++;
        }

        (void) sprintf(buff, "%d map(s) found", maps);
        return (buff);
}

char *semantics_strs[] = {"", "CLTS", "COTS", "COTS-ORD", "RAW"};

void
show_rpcb_entry_list()
{
        char maddr[MAXSTRINGLEN + 1], netid[MAXSTRINGLEN + 1];
        char protofmly[MAXSTRINGLEN + 1], proto[MAXSTRINGLEN + 1];
        unsigned sem;
        int maps = 0;

        if (setjmp(xdr_err)) {
                (void) sprintf(get_line(0, 0),
                    " %d+ maps. (Frame is incomplete)",
                    maps);
                return;
        }

        show_space();
        (void) sprintf(get_line(0, 0),
            " Maddr      Netid        Semantics Protofmly Proto");

        while (getxdr_u_long()) {
                (void) getxdr_string(maddr, MAXSTRINGLEN);
                (void) getxdr_string(netid, MAXSTRINGLEN);
                sem  = getxdr_u_long();
                (void) getxdr_string(protofmly, MAXSTRINGLEN);
                (void) getxdr_string(proto, MAXSTRINGLEN);
                (void) sprintf(get_line(0, 0),
                    "%-12s %-12s %-8s %-8s %-8s",
                    maddr, netid,
                    semantics_strs[sem],
                    protofmly, proto);
                maps++;
        }

        (void) sprintf(get_line(0, 0), " (%d maps)", maps);
}

#define CXID_CACHE_SIZE 16
struct cache_struct cxid_cache[CXID_CACHE_SIZE];
struct cache_struct *cxcpfirst  = &cxid_cache[0];
struct cache_struct *cxcp       = &cxid_cache[0];
struct cache_struct *cxcplast   = &cxid_cache[CXID_CACHE_SIZE - 1];

struct cache_struct *
find_callit(xid)
        ulong_t xid;
{
        struct cache_struct *x;

        for (x = cxcp; x >= cxcpfirst; x--)
                if (x->xid_num == xid)
                        return (x);
        for (x = cxcplast; x > cxcp; x--)
                if (x->xid_num == xid)
                        return (x);
        return (NULL);
}

static void
stash_callit(xid, frame, prog, vers, proc)
        ulong_t xid;
        int frame, prog, vers, proc;
{
        struct cache_struct *x;

        x = find_callit(xid);
        if (x == NULL) {
                x = cxcp++;
                if (cxcp > cxcplast)
                        cxcp = cxcpfirst;
                x->xid_num = xid;
                x->xid_frame = frame;
        }
        x->xid_prog = prog;
        x->xid_vers = vers;
        x->xid_proc = proc;
}