root/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rpc.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

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

#include <netinet/in.h>
#include <netdb.h>

#include <sys/tiuser.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
#include <rpc/auth_unix.h>
#include <rpc/auth_des.h>
#include <rpc/clnt.h>
#include <rpc/rpc_msg.h>
#include <rpc/pmap_clnt.h>
#include <rpc/svc.h>
#include <rpcsvc/yp_prot.h>
#include <rpc/pmap_prot.h>
#include "snoop.h"

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

int pos;
struct cache_struct *find_xid();
extern jmp_buf xdr_err;
void protoprint();
void print_rpcsec_gss_cred(int xid, int authlen);
char *nameof_prog();
char *nameof_astat();
char *nameof_why();
static void rpc_detail_call(int, int, int, int, int, int, char *, int);
static void rpc_detail_reply(int, int, struct cache_struct *, char *, int len);
static void print_creds(int);
static void print_verif(int);
static void stash_xid(ulong_t, int, int, int, int);
struct cache_struct xid_cache[XID_CACHE_SIZE];

#define LAST_FRAG ((ulong_t)1 << 31)

int
interpret_rpc(int flags, char *rpc, int fraglen, int type)
{
        ulong_t xid;
        int direction;
        struct cache_struct *x;
        int rpcvers, prog, vers, proc;
        int status, astat, rstat, why;
        char *lp;
        unsigned recmark;
        int markpos;
        extern int pi_frame;
        int lo, hi;

        xdr_init(rpc, fraglen);

        if (setjmp(xdr_err)) {
                if (flags & F_DTAIL)
                        (void) sprintf(get_line(0, 0),
                            "----  short frame ---");
                return (fraglen);
        }

        if (type == IPPROTO_TCP) {      /* record mark */
                markpos = getxdr_pos();
                recmark = getxdr_long();
        }

        xid       = getxdr_u_long();
        direction = getxdr_long();

        if (direction == CALL) {
                rpcvers = getxdr_long();
                pos = getxdr_pos();
                prog = getxdr_long();
                vers = getxdr_long();
                proc = getxdr_long();
                stash_xid(xid, pi_frame, prog, vers, proc);
                if (!(flags & (F_SUM | F_DTAIL))) {
                        protoprint(flags, CALL, xid, prog, vers, proc,
                            rpc, fraglen);
                }
        } else {
                x = find_xid(xid);
        }

        if (flags & F_SUM) {
                switch (direction) {
                case CALL:
                        (void) sprintf(get_sum_line(),
                            "RPC C XID=%lu PROG=%d (%s) VERS=%d PROC=%d",
                            xid,
                            prog, nameof_prog(prog),
                            vers, proc);
                        if (getxdr_long() == RPCSEC_GSS) { /* Cred auth type */
                                extract_rpcsec_gss_cred_info(xid);
                                /* RPCSEC_GSS cred auth data */
                        } else {
                                xdr_skip(getxdr_long());
                                /* non RPCSEC_GSS cred auth data */
                        }
                        xdr_skip(4);                    /* Verf auth type */
                        xdr_skip(RNDUP(getxdr_long())); /* Verf auth data */

                        protoprint(flags, CALL, xid, prog, vers, proc,
                            rpc, fraglen);
                        break;

                case REPLY:
                        lp = get_sum_line();
                        if (x == NULL)
                                (void) sprintf(lp, "RPC R XID=%lu", xid);
                        else
                                (void) sprintf(lp, "RPC R (#%d) XID=%lu",
                                    x->xid_frame, xid);

                        lp += strlen(lp);
                        status = getxdr_long();
                        switch (status) {
                        case MSG_ACCEPTED:
                                /* eat flavor and verifier */
                                (void) getxdr_long();
                                xdr_skip(RNDUP(getxdr_long()));
                                astat = getxdr_long();
                                (void) sprintf(lp, " %s",
                                    nameof_astat(astat));
                                lp += strlen(lp);

                                switch (astat) {
                                case SUCCESS:
                                        if (x) {
                                                protoprint(flags, REPLY,
                                                    xid,
                                                    x->xid_prog,
                                                    x->xid_vers,
                                                    x->xid_proc,
                                                    rpc, fraglen);
                                        }
                                        break;

                                case PROG_UNAVAIL :
                                case PROG_MISMATCH:
                                case PROC_UNAVAIL :
                                        lo = getxdr_long();
                                        hi = getxdr_long();
                                        (void) sprintf(lp,
                                            " (low=%d, high=%d)",
                                            lo, hi);
                                        break;

                                case GARBAGE_ARGS:
                                case SYSTEM_ERR:
                                default:
                                        ;
                                }
                                break;

                        case MSG_DENIED:
                                rstat = getxdr_long();

                                switch (rstat) {
                                case RPC_MISMATCH:
                                        lo = getxdr_long();
                                        hi = getxdr_long();
                                        (void) sprintf(lp,
                                        " Vers mismatch (low=%d, high=%d)",
                                            lo, hi);
                                        break;

                                case AUTH_ERROR:
                                        why = getxdr_u_long();
                                        (void) sprintf(lp,
                                            " Can't authenticate (%s)",
                                            nameof_why(why));
                                        break;
                                }
                        }
                        break;
                }
        }

        if (flags & F_DTAIL) {
                show_header("RPC:  ", "SUN RPC Header", fraglen);
                show_space();
                if (type == IPPROTO_TCP) {      /* record mark */
                        (void) sprintf(get_line(markpos, markpos+4),
                            "Record Mark: %s fragment, length = %d",
                            recmark & LAST_FRAG ? "last" : "",
                            recmark & ~LAST_FRAG);
                }

                (void) sprintf(get_line(0, 0),
                    "Transaction id = %lu",
                    xid);
                (void) sprintf(get_line(0, 0),
                    "Type = %d (%s)",
                    direction,
                    direction == CALL ? "Call":"Reply");

                switch (direction) {
                case CALL:
                        rpc_detail_call(flags, xid, rpcvers,
                            prog, vers, proc, rpc, fraglen);
                        break;
                case REPLY:
                        rpc_detail_reply(flags, xid, x, rpc, fraglen);
                        break;
                }
        }

        return (fraglen);
}

static void
rpc_detail_call(int flags, int xid, int rpcvers, int prog, int vers, int proc,
    char *data, int len)
{
        char *nameof_flavor();
        char *nameof_prog();

        (void) sprintf(get_line(pos, getxdr_pos()),
            "RPC version = %d",
            rpcvers);
        (void) sprintf(get_line(pos, getxdr_pos()),
            "Program = %d (%s), version = %d, procedure = %d",
            prog, nameof_prog(prog), vers, proc);
        print_creds(xid);
        print_verif(CALL);
        show_trailer();
        protoprint(flags, CALL, xid, prog, vers, proc, data, len);
}

char *
nameof_flavor(int flavor)
{
        switch (flavor) {
        case AUTH_NONE : return ("None");
        case AUTH_UNIX : return ("Unix");
        case AUTH_SHORT: return ("Unix short");
        case AUTH_DES  : return ("DES");
        case RPCSEC_GSS: return ("RPCSEC_GSS");
        default: return ("unknown");
        }
}

char *
tohex(char *p, int len)
{
        int i, j;
        static char hbuff[1024];
        static char *hexstr = "0123456789ABCDEF";
        char toobig = 0;

        if (len * 2 > sizeof (hbuff)) {
                toobig++;
                len = sizeof (hbuff) / 2;
        }

        j = 0;
        for (i = 0; i < len; i++) {
                hbuff[j++] = hexstr[p[i] >> 4   & 0x0f];
                hbuff[j++] = hexstr[p[i]        & 0x0f];
        }

        if (toobig) {
                hbuff[len * 2 - strlen("<Too Long>")] = '\0';
                strcat(hbuff, "<Too Long>");
        } else
                hbuff[j] = '\0';

        return (hbuff);
}

static void
print_creds(int xid)
{
        int pos, flavor, authlen;
        int uid, gid, len;
        int tlen, idlen;
        int i, namekind;
        char *p, *line;

        pos = getxdr_pos();
        flavor  = getxdr_long();
        authlen = getxdr_long();
        (void) sprintf(get_line(pos, getxdr_pos()),
            "Credentials: Flavor = %d (%s), len = %d bytes",
            flavor, nameof_flavor(flavor), authlen);
        if (authlen <= 0)
                return;

        switch (flavor) {
        case AUTH_UNIX:
                (void) showxdr_time("   Time = %s");
                (void) showxdr_string(MAX_MACHINE_NAME, "   Hostname = %s");
                pos = getxdr_pos();
                uid = getxdr_u_long();
                gid = getxdr_u_long();
                (void) sprintf(get_line(pos, getxdr_pos()),
                    "   Uid = %d, Gid = %d",
                    uid, gid);
                len = getxdr_u_long();
                line = get_line(pos, len * 4);
                if (len == 0)
                        (void) sprintf(line, "   Groups = (none)");
                else {
                        (void) sprintf(line, "   Groups = ");
                        line += strlen(line);
                        while (len--) {
                                gid = getxdr_u_long();
                                (void) sprintf(line, "%d ", gid);
                                line += strlen(line);
                        }
                }
                break;

        case AUTH_DES:
                namekind = getxdr_u_long();
                (void) sprintf(get_line(pos, getxdr_pos()),
                    "   Name kind = %d (%s)",
                    namekind,
                    namekind == ADN_FULLNAME ?
                    "fullname" : "nickname");
                switch (namekind) {
                case ADN_FULLNAME:
                        (void) showxdr_string(64,
                            "   Network name = %s");
                        (void) showxdr_hex(8,
                            "   Conversation key = 0x%s (DES encrypted)");
                        (void) showxdr_hex(4,
                            "   Window = 0x%s (DES encrypted)");
                        break;

                case ADN_NICKNAME:
                        (void) showxdr_hex(4, "   Nickname = 0x%s");
                        break;
                };
                break;

        case RPCSEC_GSS:
                print_rpcsec_gss_cred(xid, authlen);
                break;

        default:
                (void) showxdr_hex(authlen, "[%s]");
                break;
        }
}

static void
print_verif(int direction)
{
        int pos, flavor, verlen;

        pos = getxdr_pos();
        flavor = getxdr_long();
        verlen = getxdr_long();
        (void) sprintf(get_line(pos, getxdr_pos()),
            "Verifier   : Flavor = %d (%s), len = %d bytes",
            flavor, nameof_flavor(flavor), verlen);
        if (verlen == 0)
                return;

        switch (flavor) {
        case AUTH_DES:
                (void) showxdr_hex(8, "   Timestamp = 0x%s (DES encrypted)");
                if (direction == CALL)
                        (void) showxdr_hex(4,
                            "   Window    = 0x%s (DES encrypted)");
                else
                        (void) showxdr_hex(4, "   Nickname  = 0x%s");
                break;

        /* For other flavors like AUTH_NONE, AUTH_UNIX, RPCSEC_GSS etc. */
        default:
                (void) showxdr_hex(verlen, "[%s]");
                break;
        }
}

struct rpcnames {
        int   rp_prog;
        char *rp_name;
} rpcnames[] = {
100000, "PMAP",                 /* Portmapper */
100001, "RSTAT",                /* Remote stats */
100002, "RUSERS",               /* Remote users */
100003, "NFS",                  /* Nfs */
100004, "NIS",                  /* Network Information Service */
100005, "MOUNT",                /* Mount demon */
100006, "DBX",                  /* Remote dbx */
100007, "NISBIND",              /* NIS binder */
100008, "WALL",                 /* Shutdown msg */
100009, "NISPASSWD",            /* Yppasswd server */
100010, "ETHERSTAT",            /* Ether stats */
100011, "RQUOTA",               /* Disk quotas */
100012, "SPRAY",                /* Spray packets */
100013, "IBM3270",              /* 3270 mapper */
100014, "IBMRJE",               /* RJE mapper */
100015, "SELNSVC",              /* Selection service */
100016, "RDATABASE",            /* Remote database access */
100017, "REX",                  /* Remote execution */
100018, "ALICE",                /* Alice Office Automation */
100019, "SCHED",                /* Scheduling service */
100020, "LLM",                  /* Local lock manager */
100021, "NLM",                  /* Network lock manager */
100022, "X25INR",               /* X.25 inr protocol */
100023, "STATMON1",             /* Status monitor 1 */
100024, "STATMON2",             /* Status monitor 2 */
100025, "SELNLIB",              /* Selection library */
100026, "BOOTPARAM",            /* Boot parameters service */
100027, "MAZEPROG",             /* Mazewars game */
100028, "NISUPDATE",            /* NIS update */
100029, "KEYSERVE",             /* Key server */
100030, "SECURECMD",            /* Secure login */
100031, "NETFWDI",              /* NFS net forwarder init */
100032, "NETFWDT",              /* NFS net forwarder trans */
100033, "SUNLINKMAP",           /* Sunlink MAP */
100034, "NETMON",               /* Network monitor */
100035, "DBASE",                /* Lightweight database */
100036, "PWDAUTH",              /* Password authorization */
100037, "TFS",                  /* Translucent file svc */
100038, "NSE",                  /* NSE server */
100039, "NSE_ACTIVATE",         /* NSE activate daemon */
100040, "SUNVIEW_HELP",         /* Sunview help */
100041, "PNP",                  /* PNP install */
100042, "IPADDR_ALLOC",         /* IP addr allocator */
100043, "FILEHANDLE",           /* Show filehandle */
100044, "MVSNFS",               /* MVS NFS mount  */
100045, "REM_FILEOP_USER",      /* Remote user file operations */
100046, "BATCH_NISUPDATE",      /* Batched ypupdate */
100047, "NEM",                  /* Network execution mgr */
100048, "RAYTRACE_RD",          /* Raytrace/mandelbrot remote daemon */
100049, "RAYTRACE_LD",          /* Raytrace/mandelbrot local daemon */
100050, "REM_FILEOP_GROUP",     /* Remote group file operations */
100051, "REM_FILEOP_SYSTEM",    /* Remote system file operations */
100052, "REM_SYSTEM_ROLE",      /* Remote system role operations */
100055, "IOADMD",               /* Ioadmd */
100056, "FILEMERGE",            /* Filemerge */
100057, "NAMEBIND",             /* Name Binding Program */
100058, "NJE",                  /* Sunlink NJE */
100059, "MVSATTR",              /* MVSNFS get attribute service */
100060, "RMGR",                 /* SunAccess/SunLink resource manager */
100061, "UIDALLOC",             /* UID allocation service */
100062, "LBSERVER",             /* License broker */
100063, "LBBINDER",             /* NETlicense client binder */
100064, "GIDALLOC",             /* GID allocation service */
100065, "SUNISAM",              /* SunIsam */
100066, "RDBSRV",               /* Remote Debug Server */
100067, "NETDIR",               /* Network directory daemon */
100068, "CMSD",                 /* Network calendar program */
100069, "NISXFR",               /* NIS transfer */
100070, "TIMED",                /* RPC.timed */
100071, "BUGTRAQ",              /* Bugtraqd */
100072, "NeFS",                 /* Internal use only */
100073, "BILLBOARD",            /* Connectathon Billboard - NFS */
100074, "BILLBOARD",            /* Connectathon Billboard - X */
100075, "SCHEDROOM",            /* Sun meeting room scheduler */
100076, "AUTHNEGOTIATE",        /* Authentication negotiation */
100077, "ATTRPROG",             /* Database manipulation */
100080, "AUTODUMP",             /* Sun consulting special */
100081, "EVENT_SVC",            /* Event protocol */
100085, "ARM_PSD",              /* ARM policy */
100086, "ARMTOD",               /* ARM TOD */
100087, "NA.ADMIN",             /* Sun (SNAG) administration agent */
100099, "PLD",                  /* Genesil 8.1 hot plot */
100101, "NA.EVENT",             /* SNM (SunNet Manager) event dispatcher */
100102, "NA.LOGGER",            /* SNM report logger */
100103, "NA.DISCOVER",          /* SNM network discovery agent */
100104, "NA.SYNC",              /* SNM sync interface agent */
100105, "NA.DISKINFO",          /* SNM disk info agent */
100106, "NA.IOSTAT",            /* SNM iostat agent */
100107, "NA.HOSTPERF",          /* SNM rstat proxy agent */
100108, "NA.CONFIG",            /* SNM host configuration agent */
100109, "NA.ACTIVITY",          /* SNM activity daemon */
100111, "NA.LPSTAT",            /* SNM printer agent */
100112, "NA.HOSTMEM",           /* SNM host network memory agent */
100113, "NA.SAMPLE",            /* SNM sample agent */
100114, "NA.X25",               /* SNM X.25 agent */
100115, "NA.PING",              /* SNM ping proxy agent */
100116, "NA.RPCNFS",            /* SNM rpc and nfs agent */
100117, "NA.HOSTIF",            /* SNM host interface agent */
100118, "NA.ETHERIF",           /* SNM ethernet interface agent */
100119, "NA.IPPATH",            /* SNM traceroute proxy agent */
100120, "NA.IPROUTES",          /* SNM routing table agent */
100121, "NA.LAYERS",            /* SNM protocol layers gent */
100122, "NA.SNMP",              /* SNM SNMP proxy agent */
100123, "NA.TRAFFIC",           /* SNM network traffic agent */
100124, "NA.DNI",               /* DNI (DECnet) proxy agent */
100125, "NA.CHAT",              /* IBM Channel attach proxy agent */
100126, "NA.FDDI",              /* FDDI agent */
100127, "NA.FDDISMT",           /* FDDI SMT proxy agent */
100128, "NA.MHS",               /* MHS agent */
100130, "SNM_GRAPHER",          /* SNM 3D grapher */
100132, "NA.TR",                /* Token Ring agent */
100134, "NA.TOKENRING",         /* Token Ring agent */
100136, "NA.FRAMERELAY",        /* Frame Relay agent */
100175, "NA.SNMPTRAP",          /* SNM SNMP trap daemon */
100180, "NA.MIPROUTES",         /* SNM multicast routing table agent */
100201, "MVSNFSSTAT",           /* MVS/NFS Memory usage statistic server */
100227, "NFS_ACL",              /* NFS ACL support */
101002, "NSELINKTOOL",          /* NSE link daemon */
101003, "NSELINKAPP",           /* NSE link application */
110001, "GOLABEL",              /* SunOS MLS  */
110002, "PUC",                  /* SunOS MLS  */
150001, "PCNFSD",               /* PC passwd authorization */
150002, "TOPS",                 /* TOPS name mapping */
150003, "TOPS",                 /* TOPS external attribute storage */
150004, "TOPS",                 /* TOPS hierarchical file system */
150005, "TOPS",                 /* TOPS NFS transparency extensions */
150006, "SOLARNET_FW",          /* SolarNet Framework protocol */
160001, "CM",                   /* Nihon Sun - Japanese Input system */
300004, "FRAME 1",              /* Frame program 1 */
300009, "FRAME 2",              /* Frame program 2 */
390101, "RAP",                  /* Legato RAP protocol */
390102, "RAPRD",                /* Legato RAP resource dir protocol */
500021, "ZNS",                  /* Zeus Network Service */
};

int
compare(struct rpcnames *a, struct rpcnames *b)
{
        return (a->rp_prog - b->rp_prog);
}

char *
nameof_prog(int prog)
{
        struct rpcnames *r;
        struct rpcnames *bsearch();
        int elems = sizeof (rpcnames) / sizeof (*r);

        r = bsearch(&prog, rpcnames, elems, sizeof (*r), compare);
        if (r)
                return (r->rp_name);

        if (prog >= 0x40000000 && prog <= 0x5fffffff)
                return ("transient");

        return ("?");
}

char *
nameof_astat(int status)
{
        switch (status) {
        case SUCCESS      : return ("Success");
        case PROG_UNAVAIL : return ("Program unavailable");
        case PROG_MISMATCH: return ("Program number mismatch");
        case PROC_UNAVAIL : return ("Procedure unavailable");
        case GARBAGE_ARGS : return ("Garbage arguments");
        case SYSTEM_ERR   : return ("System error");
        default: return ("unknown");
        }
}

char *
nameof_why(int why)
{
        switch (why) {
        case AUTH_BADCRED:      return ("bogus credentials (seal broken)");
        case AUTH_REJECTEDCRED: return ("client should begin new session");
        case AUTH_BADVERF:      return ("bogus verifier (seal broken)");
        case AUTH_REJECTEDVERF: return ("verifier expired or was replayed");
        case AUTH_TOOWEAK:      return ("too weak");
        case AUTH_INVALIDRESP:  return ("bogus response verifier");
        case AUTH_TIMEEXPIRE:   return ("time of credential expired");
        case AUTH_TKT_FILE:     return ("something wrong with ticket file");
        case AUTH_DECODE:       return ("can't decode authenticator");
        case AUTH_NET_ADDR:     return ("net address in ticket wrong");
        case RPCSEC_GSS_NOCRED: return ("no credentials for user");
        case RPCSEC_GSS_FAILED: return ("GSS failure, credentials deleted");
        case AUTH_FAILED:
        default:
                return ("unknown reason");
        }
}

static void
rpc_detail_reply(int flags, int xid, struct cache_struct *x, char *data,
    int len)
{
        int status;
        int astat, rstat, why;
        int pos;

        if (x) {
                (void) sprintf(get_line(0, 0),
                    "This is a reply to frame %d",
                    x->xid_frame);
        }
        pos = getxdr_pos();
        status = getxdr_long();
        (void) sprintf(get_line(pos, getxdr_pos()),
            "Status = %d (%s)",
            status, status ? "Denied" : "Accepted");

        switch (status) {
        case MSG_ACCEPTED:
                print_verif(REPLY);
                pos = getxdr_pos();
                astat = getxdr_long();
                (void) sprintf(get_line(pos, getxdr_pos()),
                    "Accept status = %d (%s)",
                    astat, nameof_astat(astat));

                switch (astat) {
                case SUCCESS:
                        if (x) {
                                show_trailer();
                                protoprint(flags, REPLY, xid,
                                    x->xid_prog, x->xid_vers, x->xid_proc,
                                    data, len);
                        }
                        break;
                case PROG_UNAVAIL :
                        break;
                case PROG_MISMATCH:
                case PROC_UNAVAIL :
                        showxdr_long("   Low  = %d");
                        showxdr_long("   High = %d");
                        break;
                case GARBAGE_ARGS:
                case SYSTEM_ERR:
                default:
                        ;
                }

                break;

        case MSG_DENIED:
                pos = getxdr_pos();
                rstat = getxdr_long();
                (void) sprintf(get_line(pos, getxdr_pos()),
                    "Reject status = %d (%s)",
                    rstat,
                    rstat ? "can't authenticate"
                    : "version mismatch");

                switch (rstat) {
                case RPC_MISMATCH:
                        showxdr_long("   Low  = %d");
                        showxdr_long("   High = %d");
                        break;
                case AUTH_ERROR:
                        why = getxdr_u_long();
                        (void) sprintf(get_line(pos, getxdr_pos()),
                            "   Why = %d (%s)",
                            why, nameof_why(why));
                        break;
                }
                break;
        }
}

/*
 * Return true if this is a valid RPC packet
 */
int
valid_rpc(char *rpc, int rpclen)
{
        XDR     xdrm;
        struct rpc_msg msg;

        if (rpclen < 12)
                return (0);

        xdrmem_create(&xdrm, rpc, rpclen, XDR_DECODE);
        if (xdr_u_int(&xdrm, &msg.rm_xid) &&
            xdr_u_int(&xdrm, (uint_t *)&msg.rm_direction)) {
                switch (msg.rm_direction) {
                case CALL:
                        if (xdr_rpcvers(&xdrm, &msg.rm_call.cb_rpcvers) &&
                            msg.rm_call.cb_rpcvers == 2)
                                return (1);
                        break;
                case REPLY:
                        if (xdr_u_int(&xdrm,
                            (uint_t *)&msg.rm_reply.rp_stat) &&
                            (msg.rm_reply.rp_stat == MSG_ACCEPTED ||
                            msg.rm_reply.rp_stat == MSG_DENIED))
                                return (1);
                        break;
                }
        }

        return (0);
}

struct cache_struct *xcpfirst   = &xid_cache[0];
struct cache_struct *xcp        = &xid_cache[0];
struct cache_struct *xcplast    = &xid_cache[XID_CACHE_SIZE - 1];

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

        for (x = xcp; x >= xcpfirst; x--)
                if (x->xid_num == xid)
                        return (x);
        for (x = xcplast; x > xcp; x--)
                if (x->xid_num == xid)
                        return (x);
        return (NULL);
}

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

        x = find_xid(xid);
        if (x == NULL) {
                x = xcp++;
                if (xcp > xcplast)
                        xcp = xcpfirst;
                x->xid_num = xid;
                x->xid_frame = frame;
        }
        x->xid_prog = prog;
        x->xid_vers = vers;
        x->xid_proc = proc;
        x->xid_gss_proc = RPCSEC_GSS_DATA;
        x->xid_gss_service = rpc_gss_svc_default;
}

void
check_retransmit(char *line, ulong_t xid)
{
        struct cache_struct *x;
        extern int pi_frame;

        x = find_xid(xid);
        if (x && x->xid_frame != pi_frame)
                (void) strcat(line, " (retransmit)");
}