home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / tcpdumpb.zip / print-nfs.c < prev    next >
C/C++ Source or Header  |  1997-02-25  |  27KB  |  864 lines

  1. /*
  2.  * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996
  3.  *      The Regents of the University of California.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that: (1) source code distributions
  7.  * retain the above copyright notice and this paragraph in its entirety, (2)
  8.  * distributions including binary code include the above copyright notice and
  9.  * this paragraph in its entirety in the documentation or other materials
  10.  * provided with the distribution, and (3) all advertising materials mentioning
  11.  * features or use of this software display the following acknowledgement:
  12.  * ``This product includes software developed by the University of California,
  13.  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
  14.  * the University nor the names of its contributors may be used to endorse
  15.  * or promote products derived from this software without specific prior
  16.  * written permission.
  17.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  18.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  19.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  20.  */
  21. #ifndef lint
  22. static char rcsid[] =
  23.     "@(#) $Header: print-nfs.c,v 1.56 96/07/23 14:17:25 leres Exp $ (LBL)";
  24. #endif
  25.  
  26. #include <sys/param.h>
  27. #include <sys/time.h>
  28. #include <sys/socket.h>
  29.  
  30. #if __STDC__
  31. struct mbuf;
  32. struct rtentry;
  33. #endif
  34. #include <net/if.h>
  35.  
  36. #include <netinet/in.h>
  37. #include <netinet/if_ether.h>
  38. #include <netinet/in_systm.h>
  39. #include <netinet/ip.h>
  40. #ifndef __EMX__
  41. #include <netinet/ip_var.h>
  42. #endif
  43.  
  44. #include <rpc/rpc.h>
  45.  
  46. #include <ctype.h>
  47. #include <pcap.h>
  48. #include <stdio.h>
  49. #include <string.h>
  50.  
  51. #include "interface.h"
  52. #include "addrtoname.h"
  53.  
  54. #include "nfsv2.h"
  55. #include "nfsfh.h"
  56.  
  57. static void nfs_printfh(const u_int32_t *);
  58. static void xid_map_enter(const struct rpc_msg *, const struct ip *);
  59. static int32_t xid_map_find(const struct rpc_msg *, const struct ip *);
  60. static void interp_reply(const struct rpc_msg *, u_int32_t, u_int);
  61.  
  62. void
  63. nfsreply_print(register const u_char *bp, u_int length,
  64.                register const u_char *bp2)
  65. {
  66.         register const struct rpc_msg *rp;
  67.         register const struct ip *ip;
  68.         int32_t proc;
  69.  
  70.         rp = (const struct rpc_msg *)bp;
  71.         ip = (const struct ip *)bp2;
  72.  
  73.         if (!nflag)
  74.                 (void)printf("%s.nfs > %s.%x: reply %s %d",
  75.                              ipaddr_string(&ip->ip_src),
  76.                              ipaddr_string(&ip->ip_dst),
  77.                              (u_int32_t)ntohl(rp->rm_xid),
  78.                              ntohl(rp->rm_reply.rp_stat) == MSG_ACCEPTED?
  79.                                      "ok":"ERR",
  80.                              length);
  81.         else
  82.                 (void)printf("%s.%x > %s.%x: reply %s %d",
  83.                              ipaddr_string(&ip->ip_src),
  84.                              NFS_PORT,
  85.                              ipaddr_string(&ip->ip_dst),
  86.                              (u_int32_t)ntohl(rp->rm_xid),
  87.                              ntohl(rp->rm_reply.rp_stat) == MSG_ACCEPTED?
  88.                                 "ok":"ERR",
  89.                              length);
  90.  
  91.         proc = xid_map_find(rp, ip);
  92.         if (proc >= 0)
  93.                 interp_reply(rp, (u_int32_t)proc, length);
  94. }
  95.  
  96. /*
  97.  * Return a pointer to the first file handle in the packet.
  98.  * If the packet was truncated, return 0.
  99.  */
  100. static const u_int32_t *
  101. parsereq(register const struct rpc_msg *rp, register u_int length)
  102. {
  103.         register const u_int32_t *dp;
  104.         register u_int len;
  105.  
  106.         /*
  107.          * find the start of the req data (if we captured it)
  108.          */
  109.         dp = (u_int32_t *)&rp->rm_call.cb_cred;
  110.         TCHECK(dp[1]);
  111.         len = ntohl(dp[1]);
  112.         if (len < length) {
  113.                 dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
  114.                 TCHECK(dp[1]);
  115.                 len = ntohl(dp[1]);
  116.                 if (len < length) {
  117.                         dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
  118.                         TCHECK2(dp[0], 0);
  119.                         return (dp);
  120.                 }
  121.         }
  122. trunc:
  123.         return (0);
  124. }
  125.  
  126. /*
  127.  * Print out an NFS file handle and return a pointer to following word.
  128.  * If packet was truncated, return 0.
  129.  */
  130. static const u_int32_t *
  131. parsefh(register const u_int32_t *dp)
  132. {
  133.         if (dp + 8 <= (u_int32_t *)snapend) {
  134.                 nfs_printfh(dp);
  135.                 return (dp + 8);
  136.         }
  137.         return (0);
  138. }
  139.  
  140. /*
  141.  * Print out a file name and return pointer to 32-bit word past it.
  142.  * If packet was truncated, return 0.
  143.  */
  144. static const u_int32_t *
  145. parsefn(register const u_int32_t *dp)
  146. {
  147.         register u_int32_t len;
  148.         register const u_char *cp;
  149.  
  150.         /* Bail if we don't have the string length */
  151.         if ((u_char *)dp > snapend - sizeof(*dp))
  152.                 return(0);
  153.  
  154.         /* Fetch string length; convert to host order */
  155.         len = *dp++;
  156.         NTOHL(len);
  157.  
  158.         cp = (u_char *)dp;
  159.         /* Update 32-bit pointer (NFS filenames padded to 32-bit boundaries) */
  160.         dp += ((len + 3) & ~3) / sizeof(*dp);
  161.         if ((u_char *)dp > snapend)
  162.                 return (0);
  163.         /* XXX seems like we should be checking the length */
  164.         putchar('"');
  165.         (void) fn_printn(cp, len, NULL);
  166.         putchar('"');
  167.  
  168.         return (dp);
  169. }
  170.  
  171. /*
  172.  * Print out file handle and file name.
  173.  * Return pointer to 32-bit word past file name.
  174.  * If packet was truncated (or there was some other error), return 0.
  175.  */
  176. static const u_int32_t *
  177. parsefhn(register const u_int32_t *dp)
  178. {
  179.         dp = parsefh(dp);
  180.         if (dp == 0)
  181.                 return (0);
  182.         putchar(' ');
  183.         return (parsefn(dp));
  184. }
  185.  
  186. void
  187. nfsreq_print(register const u_char *bp, u_int length,
  188.     register const u_char *bp2)
  189. {
  190.         register const struct rpc_msg *rp;
  191.         register const struct ip *ip;
  192.         register const u_int32_t *dp;
  193.  
  194.         rp = (const struct rpc_msg *)bp;
  195.         ip = (const struct ip *)bp2;
  196.         if (!nflag)
  197.                 (void)printf("%s.%x > %s.nfs: %d",
  198.                              ipaddr_string(&ip->ip_src),
  199.                              (u_int32_t)ntohl(rp->rm_xid),
  200.                              ipaddr_string(&ip->ip_dst),
  201.                              length);
  202.         else
  203.                 (void)printf("%s.%x > %s.%x: %d",
  204.                              ipaddr_string(&ip->ip_src),
  205.                              (u_int32_t)ntohl(rp->rm_xid),
  206.                              ipaddr_string(&ip->ip_dst),
  207.                              NFS_PORT,
  208.                              length);
  209.  
  210.         xid_map_enter(rp, ip);  /* record proc number for later on */
  211.  
  212.         switch (ntohl(rp->rm_call.cb_proc)) {
  213. #ifdef NFSPROC_NOOP
  214.         case NFSPROC_NOOP:
  215.                 printf(" nop");
  216.                 return;
  217. #else
  218. #define NFSPROC_NOOP -1
  219. #endif
  220.         case NFSPROC_NULL:
  221.                 printf(" null");
  222.                 return;
  223.  
  224.         case NFSPROC_GETATTR:
  225.                 printf(" getattr");
  226.                 if ((dp = parsereq(rp, length)) != 0 && parsefh(dp) != 0)
  227.                         return;
  228.                 break;
  229.  
  230.         case NFSPROC_SETATTR:
  231.                 printf(" setattr");
  232.                 if ((dp = parsereq(rp, length)) != 0 && parsefh(dp) != 0)
  233.                         return;
  234.                 break;
  235.  
  236. #if NFSPROC_ROOT != NFSPROC_NOOP
  237.         case NFSPROC_ROOT:
  238.                 printf(" root");
  239.                 break;
  240. #endif
  241.         case NFSPROC_LOOKUP:
  242.                 printf(" lookup");
  243.                 if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
  244.                         return;
  245.                 break;
  246.  
  247.         case NFSPROC_READLINK:
  248.                 printf(" readlink");
  249.                 if ((dp = parsereq(rp, length)) != 0 && parsefh(dp) != 0)
  250.                         return;
  251.                 break;
  252.  
  253.         case NFSPROC_READ:
  254.                 printf(" read");
  255.                 if ((dp = parsereq(rp, length)) != 0 &&
  256.                     (dp = parsefh(dp)) != 0) {
  257.                         TCHECK2(dp[0], 3 * sizeof(*dp));
  258.                         printf(" %u bytes @ %u",
  259.                             (u_int32_t)ntohl(dp[1]),
  260.                             (u_int32_t)ntohl(dp[0]));
  261.                         return;
  262.                 }
  263.                 break;
  264.  
  265. #if NFSPROC_WRITECACHE != NFSPROC_NOOP
  266.         case NFSPROC_WRITECACHE:
  267.                 printf(" writecache");
  268.                 if ((dp = parsereq(rp, length)) != 0 &&
  269.                     (dp = parsefh(dp)) != 0) {
  270.                         TCHECK2(dp[0], 4 * sizeof(*dp));
  271.                         printf(" %u (%u) bytes @ %u (%u)",
  272.                             (u_int32_t)ntohl(dp[3]),
  273.                             (u_int32_t)ntohl(dp[2]),
  274.                             (u_int32_t)ntohl(dp[1]),
  275.                             (u_int32_t)ntohl(dp[0]));
  276.                         return;
  277.                 }
  278.                 break;
  279. #endif
  280.         case NFSPROC_WRITE:
  281.                 printf(" write");
  282.                 if ((dp = parsereq(rp, length)) != 0 &&
  283.                     (dp = parsefh(dp)) != 0) {
  284.                         TCHECK2(dp[0], 4 * sizeof(*dp));
  285.                         printf(" %u (%u) bytes @ %u (%u)",
  286.                             (u_int32_t)ntohl(dp[3]),
  287.                             (u_int32_t)ntohl(dp[2]),
  288.                             (u_int32_t)ntohl(dp[1]),
  289.                             (u_int32_t)ntohl(dp[0]));
  290.                         return;
  291.                 }
  292.                 break;
  293.  
  294.         case NFSPROC_CREATE:
  295.                 printf(" create");
  296.                 if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
  297.                         return;
  298.                 break;
  299.  
  300.         case NFSPROC_REMOVE:
  301.                 printf(" remove");
  302.                 if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
  303.                         return;
  304.                 break;
  305.  
  306.         case NFSPROC_RENAME:
  307.                 printf(" rename");
  308.                 if ((dp = parsereq(rp, length)) != 0 &&
  309.                     (dp = parsefhn(dp)) != 0) {
  310.                         fputs(" ->", stdout);
  311.                         if (parsefhn(dp) != 0)
  312.                                 return;
  313.                 }
  314.                 break;
  315.  
  316.         case NFSPROC_LINK:
  317.                 printf(" link");
  318.                 if ((dp = parsereq(rp, length)) != 0 &&
  319.                     (dp = parsefh(dp)) != 0) {
  320.                         fputs(" ->", stdout);
  321.                         if (parsefhn(dp) != 0)
  322.                                 return;
  323.                 }
  324.                 break;
  325.  
  326.         case NFSPROC_SYMLINK:
  327.                 printf(" symlink");
  328.                 if ((dp = parsereq(rp, length)) != 0 &&
  329.                     (dp = parsefhn(dp)) != 0) {
  330.                         fputs(" -> ", stdout);
  331.                         if (parsefn(dp) != 0)
  332.                                 return;
  333.                 }
  334.                 break;
  335.  
  336.         case NFSPROC_MKDIR:
  337.                 printf(" mkdir");
  338.                 if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
  339.                         return;
  340.                 break;
  341.  
  342.         case NFSPROC_RMDIR:
  343.                 printf(" rmdir");
  344.                 if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
  345.                         return;
  346.                 break;
  347.  
  348.         case NFSPROC_READDIR:
  349.                 printf(" readdir");
  350.                 if ((dp = parsereq(rp, length)) != 0 &&
  351.                     (dp = parsefh(dp)) != 0) {
  352.                         TCHECK2(dp[0], 2 * sizeof(*dp));
  353.                         /*
  354.                          * Print the offset as signed, since -1 is common,
  355.                          * but offsets > 2^31 aren't.
  356.                          */
  357.                         printf(" %u bytes @ %d",
  358.                             (u_int32_t)ntohl(dp[1]),
  359.                             (u_int32_t)ntohl(dp[0]));
  360.                         return;
  361.                 }
  362.                 break;
  363.  
  364.         case NFSPROC_STATFS:
  365.                 printf(" statfs");
  366.                 if ((dp = parsereq(rp, length)) != 0 && parsefh(dp) != 0)
  367.                         return;
  368.                 break;
  369.  
  370.         default:
  371.                 printf(" proc-%u", (u_int32_t)ntohl(rp->rm_call.cb_proc));
  372.                 return;
  373.         }
  374. trunc:
  375.         fputs(" [|nfs]", stdout);
  376. }
  377.  
  378. /*
  379.  * Print out an NFS file handle.
  380.  * We assume packet was not truncated before the end of the
  381.  * file handle pointed to by dp.
  382.  *
  383.  * Note: new version (using portable file-handle parser) doesn't produce
  384.  * generation number.  It probably could be made to do that, with some
  385.  * additional hacking on the parser code.
  386.  */
  387. static void
  388. nfs_printfh(register const u_int32_t *dp)
  389. {
  390.         my_fsid fsid;
  391.         ino_t ino;
  392.         char *sfsname = NULL;
  393.  
  394.         Parse_fh((caddr_t*)dp, &fsid, &ino, NULL, &sfsname, 0);
  395.  
  396.         if (sfsname) {
  397.             /* file system ID is ASCII, not numeric, for this server OS */
  398.             static char temp[NFS_FHSIZE+1];
  399.  
  400.             /* Make sure string is null-terminated */
  401.             strncpy(temp, sfsname, NFS_FHSIZE);
  402.             /* Remove trailing spaces */
  403.             sfsname = strchr(temp, ' ');
  404.             if (sfsname)
  405.                 *sfsname = 0;
  406.  
  407.             (void)printf(" fh %s/%u", temp, (u_int32_t)ino);
  408.         }
  409.         else {
  410.             (void)printf(" fh %u,%u/%u",
  411.                 fsid.fsid_dev.Major,
  412.                 fsid.fsid_dev.Minor,
  413.                 (u_int32_t)ino);
  414.         }
  415. }
  416.  
  417. /*
  418.  * Maintain a small cache of recent client.XID.server/proc pairs, to allow
  419.  * us to match up replies with requests and thus to know how to parse
  420.  * the reply.
  421.  */
  422.  
  423. struct xid_map_entry {
  424.         u_int32_t               xid;            /* transaction ID (net order) */
  425.         struct in_addr  client;         /* client IP address (net order) */
  426.         struct in_addr  server;         /* server IP address (net order) */
  427.         u_int32_t               proc;           /* call proc number (host order) */
  428. };
  429.  
  430. /*
  431.  * Map entries are kept in an array that we manage as a ring;
  432.  * new entries are always added at the tail of the ring.  Initially,
  433.  * all the entries are zero and hence don't match anything.
  434.  */
  435.  
  436. #define XIDMAPSIZE      64
  437.  
  438. struct xid_map_entry xid_map[XIDMAPSIZE];
  439.  
  440. int     xid_map_next = 0;
  441. int     xid_map_hint = 0;
  442.  
  443. static void
  444. xid_map_enter(const struct rpc_msg *rp, const struct ip *ip)
  445. {
  446.         struct xid_map_entry *xmep;
  447.  
  448.         xmep = &xid_map[xid_map_next];
  449.  
  450.         if (++xid_map_next >= XIDMAPSIZE)
  451.                 xid_map_next = 0;
  452.  
  453.         xmep->xid = rp->rm_xid;
  454.         xmep->client = ip->ip_src;
  455.         xmep->server = ip->ip_dst;
  456.         xmep->proc = ntohl(rp->rm_call.cb_proc);
  457. }
  458.  
  459. /* Returns NFSPROC_xxx or -1 on failure */
  460. static int32_t
  461. xid_map_find(const struct rpc_msg *rp, const struct ip *ip)
  462. {
  463.         int i;
  464.         struct xid_map_entry *xmep;
  465.         u_int32_t xid = rp->rm_xid;
  466.         u_int32_t clip = ip->ip_dst.s_addr;
  467.         u_int32_t sip = ip->ip_src.s_addr;
  468.  
  469.         /* Start searching from where we last left off */
  470.         i = xid_map_hint;
  471.         do {
  472.                 xmep = &xid_map[i];
  473.                 if (xmep->xid == xid && xmep->client.s_addr == clip &&
  474.                     xmep->server.s_addr == sip) {
  475.                         /* match */
  476.                         xid_map_hint = i;
  477.                         return ((int32_t)xmep->proc);
  478.                 }
  479.                 if (++i >= XIDMAPSIZE)
  480.                         i = 0;
  481.         } while (i != xid_map_hint);
  482.  
  483.         /* search failed */
  484.         return(-1);
  485. }
  486.  
  487. /*
  488.  * Routines for parsing reply packets
  489.  */
  490.  
  491. /*
  492.  * Return a pointer to the beginning of the actual results.
  493.  * If the packet was truncated, return 0.
  494.  */
  495. static const u_int32_t *
  496. parserep(register const struct rpc_msg *rp, register u_int length)
  497. {
  498.         register const u_int32_t *dp;
  499.         u_int len;
  500.         enum accept_stat astat;
  501.  
  502.         /*
  503.          * Portability note:
  504.          * Here we find the address of the ar_verf credentials.
  505.          * Originally, this calculation was
  506.          *      dp = (u_int32_t *)&rp->rm_reply.rp_acpt.ar_verf
  507.          * On the wire, the rp_acpt field starts immediately after
  508.          * the (32 bit) rp_stat field.  However, rp_acpt (which is a
  509.          * "struct accepted_reply") contains a "struct opaque_auth",
  510.          * whose internal representation contains a pointer, so on a
  511.          * 64-bit machine the compiler inserts 32 bits of padding
  512.          * before rp->rm_reply.rp_acpt.ar_verf.  So, we cannot use
  513.          * the internal representation to parse the on-the-wire
  514.          * representation.  Instead, we skip past the rp_stat field,
  515.          * which is an "enum" and so occupies one 32-bit word.
  516.          */
  517.         dp = ((const u_int32_t *)&rp->rm_reply) + 1;
  518.         TCHECK2(dp[0], 1);
  519.                 return(0);
  520.         len = ntohl(dp[1]);
  521.         if (len >= length)
  522.                 return(0);
  523.         /*
  524.          * skip past the ar_verf credentials.
  525.          */
  526.         dp += (len + (2*sizeof(u_int32_t) + 3)) / sizeof(u_int32_t);
  527.         TCHECK2(dp[0], 0);
  528.  
  529.         /*
  530.          * now we can check the ar_stat field
  531.          */
  532.         astat = ntohl(*(enum accept_stat *)dp);
  533.         switch (astat) {
  534.  
  535.         case SUCCESS:
  536.                 break;
  537.  
  538.         case PROG_UNAVAIL:
  539.                 printf(" PROG_UNAVAIL");
  540.                 return(0);
  541.  
  542.         case PROG_MISMATCH:
  543.                 printf(" PROG_MISMATCH");
  544.                 return(0);
  545.  
  546.         case PROC_UNAVAIL:
  547.                 printf(" PROC_UNAVAIL");
  548.                 return(0);
  549.  
  550.         case GARBAGE_ARGS:
  551.                 printf(" GARBAGE_ARGS");
  552.                 return(0);
  553.  
  554.         case SYSTEM_ERR:
  555.                 printf(" SYSTEM_ERR");
  556.                 return(0);
  557.  
  558.         default:
  559.                 printf(" ar_stat %d", astat);
  560.                 return(0);
  561.         }
  562.         /* successful return */
  563.         if ((sizeof(astat) + ((u_char *)dp)) < snapend)
  564.                 return((u_int32_t *) (sizeof(astat) + ((char *)dp)));
  565.  
  566. trunc:
  567.         return (0);
  568. }
  569.  
  570. static const u_int32_t *
  571. parsestatus(const u_int32_t *dp)
  572. {
  573.         int errnum;
  574.  
  575.         TCHECK(dp[0]);
  576.         errnum = ntohl(dp[0]);
  577.         if (errnum != 0) {
  578.                 char *errmsg;
  579.  
  580.                 if (qflag)
  581.                         return(0);
  582.  
  583.                 errmsg = pcap_strerror(errnum);
  584.                 printf(" ERROR: %s", errmsg);
  585.                 return(0);
  586.         }
  587.         return (dp + 1);
  588. trunc:
  589.         return (0);
  590. }
  591.  
  592. static struct tok type2str[] = {
  593.         { NFNON,        "NON" },
  594.         { NFREG,        "REG" },
  595.         { NFDIR,        "DIR" },
  596.         { NFBLK,        "BLK" },
  597.         { NFCHR,        "CHR" },
  598.         { NFLNK,        "LNK" },
  599.         { 0,            NULL }
  600. };
  601.  
  602. static const u_int32_t *
  603. parsefattr(const u_int32_t *dp, int verbose)
  604. {
  605.         const struct nfsv2_fattr *fap;
  606.  
  607.         fap = (const struct nfsv2_fattr *)dp;
  608.         if (verbose) {
  609.                 TCHECK(fap->fa_nfssize);
  610.                 printf(" %s %o ids %u/%u sz %u ",
  611.                     tok2str(type2str, "unk-ft %d ",
  612.                     (u_int32_t)ntohl(fap->fa_type)),
  613.                     (u_int32_t)ntohl(fap->fa_mode),
  614.                     (u_int32_t)ntohl(fap->fa_uid),
  615.                     (u_int32_t)ntohl(fap->fa_gid),
  616.                     (u_int32_t)ntohl(fap->fa_nfssize));
  617.         }
  618.         /* print lots more stuff */
  619.         if (verbose > 1) {
  620.                 TCHECK(fap->fa_nfsfileid);
  621.                 printf("nlink %u rdev %x fsid %x nodeid %x a/m/ctime ",
  622.                     (u_int32_t)ntohl(fap->fa_nlink),
  623.                     (u_int32_t)ntohl(fap->fa_nfsrdev),
  624.                     (u_int32_t)ntohl(fap->fa_nfsfsid),
  625.                     (u_int32_t)ntohl(fap->fa_nfsfileid));
  626.                 TCHECK(fap->fa_nfsatime);
  627.                 printf("%u.%06u ",
  628.                     (u_int32_t)ntohl(fap->fa_nfsatime.nfs_sec),
  629.                     (u_int32_t)ntohl(fap->fa_nfsatime.nfs_usec));
  630.                 TCHECK(fap->fa_nfsmtime);
  631.                 printf("%u.%06u ",
  632.                     (u_int32_t)ntohl(fap->fa_nfsmtime.nfs_sec),
  633.                     (u_int32_t)ntohl(fap->fa_nfsmtime.nfs_usec));
  634.                 TCHECK(fap->fa_nfsctime);
  635.                 printf("%u.%06u ",
  636.                     (u_int32_t)ntohl(fap->fa_nfsctime.nfs_sec),
  637.                     (u_int32_t)ntohl(fap->fa_nfsctime.nfs_usec));
  638.         }
  639.         return ((const u_int32_t *)&fap[1]);
  640. trunc:
  641.         return (NULL);
  642. }
  643.  
  644. static int
  645. parseattrstat(const u_int32_t *dp, int verbose)
  646. {
  647.         dp = parsestatus(dp);
  648.         if (dp == NULL)
  649.                 return (0);
  650.  
  651.         return (parsefattr(dp, verbose) != NULL);
  652. }
  653.  
  654. static int
  655. parsediropres(const u_int32_t *dp)
  656. {
  657.         dp = parsestatus(dp);
  658.         if (dp == NULL)
  659.                 return (0);
  660.  
  661.         dp = parsefh(dp);
  662.         if (dp == NULL)
  663.                 return (0);
  664.  
  665.         return (parsefattr(dp, vflag) != NULL);
  666. }
  667.  
  668. static int
  669. parselinkres(const u_int32_t *dp)
  670. {
  671.         dp = parsestatus(dp);
  672.         if (dp == NULL)
  673.                 return(0);
  674.  
  675.         putchar(' ');
  676.         return (parsefn(dp) != NULL);
  677. }
  678.  
  679. static int
  680. parsestatfs(const u_int32_t *dp)
  681. {
  682.         const struct nfsv2_statfs *sfsp;
  683.  
  684.         dp = parsestatus(dp);
  685.         if (dp == NULL)
  686.                 return(0);
  687.  
  688.         if (!qflag) {
  689.                 sfsp = (const struct nfsv2_statfs *)dp;
  690.                 TCHECK(sfsp->sf_bavail);
  691.                 printf(" tsize %u bsize %u blocks %u bfree %u bavail %u",
  692.                     (u_int32_t)ntohl(sfsp->sf_tsize),
  693.                     (u_int32_t)ntohl(sfsp->sf_bsize),
  694.                     (u_int32_t)ntohl(sfsp->sf_blocks),
  695.                     (u_int32_t)ntohl(sfsp->sf_bfree),
  696.                     (u_int32_t)ntohl(sfsp->sf_bavail));
  697.         }
  698.  
  699.         return (1);
  700. trunc:
  701.         return (0);
  702. }
  703.  
  704. static int
  705. parserddires(const u_int32_t *dp)
  706. {
  707.         dp = parsestatus(dp);
  708.         if (dp == 0)
  709.                 return (0);
  710.         if (!qflag) {
  711.                 TCHECK(dp[0]);
  712.                 printf(" offset %x", (u_int32_t)ntohl(dp[0]));
  713.                 TCHECK(dp[1]);
  714.                 printf(" size %u", (u_int32_t)ntohl(dp[1]));
  715.                 TCHECK(dp[2]);
  716.                 if (dp[2] != 0)
  717.                         printf(" eof");
  718.         }
  719.  
  720.         return (1);
  721. trunc:
  722.         return (0);
  723. }
  724.  
  725. static void
  726. interp_reply(const struct rpc_msg *rp, u_int32_t proc, u_int length)
  727. {
  728.         register const u_int32_t *dp;
  729.  
  730.         switch (proc) {
  731.  
  732. #ifdef NFSPROC_NOOP
  733.         case NFSPROC_NOOP:
  734.                 printf(" nop");
  735.                 return;
  736. #else
  737. #define NFSPROC_NOOP -1
  738. #endif
  739.         case NFSPROC_NULL:
  740.                 printf(" null");
  741.                 return;
  742.  
  743.         case NFSPROC_GETATTR:
  744.                 printf(" getattr");
  745.                 dp = parserep(rp, length);
  746.                 if (dp != 0 && parseattrstat(dp, !qflag) != 0)
  747.                         return;
  748.                 break;
  749.  
  750.         case NFSPROC_SETATTR:
  751.                 printf(" setattr");
  752.                 dp = parserep(rp, length);
  753.                 if (dp != 0 && parseattrstat(dp, !qflag) != 0)
  754.                         return;
  755.                 break;
  756.  
  757. #if NFSPROC_ROOT != NFSPROC_NOOP
  758.         case NFSPROC_ROOT:
  759.                 printf(" root");
  760.                 break;
  761. #endif
  762.         case NFSPROC_LOOKUP:
  763.                 printf(" lookup");
  764.                 dp = parserep(rp, length);
  765.                 if (dp != 0 && parsediropres(dp) != 0)
  766.                         return;
  767.                 break;
  768.  
  769.         case NFSPROC_READLINK:
  770.                 printf(" readlink");
  771.                 dp = parserep(rp, length);
  772.                 if (dp != 0 && parselinkres(dp) != 0)
  773.                         return;
  774.                 break;
  775.  
  776.         case NFSPROC_READ:
  777.                 printf(" read");
  778.                 dp = parserep(rp, length);
  779.                 if (dp != 0 && parseattrstat(dp, vflag) != 0)
  780.                         return;
  781.                 break;
  782.  
  783. #if NFSPROC_WRITECACHE != NFSPROC_NOOP
  784.         case NFSPROC_WRITECACHE:
  785.                 printf(" writecache");
  786.                 break;
  787. #endif
  788.         case NFSPROC_WRITE:
  789.                 printf(" write");
  790.                 dp = parserep(rp, length);
  791.                 if (dp != 0 && parseattrstat(dp, vflag) != 0)
  792.                         return;
  793.                 break;
  794.  
  795.         case NFSPROC_CREATE:
  796.                 printf(" create");
  797.                 dp = parserep(rp, length);
  798.                 if (dp != 0 && parsediropres(dp) != 0)
  799.                         return;
  800.                 break;
  801.  
  802.         case NFSPROC_REMOVE:
  803.                 printf(" remove");
  804.                 dp = parserep(rp, length);
  805.                 if (dp != 0 && parsestatus(dp) != 0)
  806.                         return;
  807.                 break;
  808.  
  809.         case NFSPROC_RENAME:
  810.                 printf(" rename");
  811.                 dp = parserep(rp, length);
  812.                 if (dp != 0 && parsestatus(dp) != 0)
  813.                         return;
  814.                 break;
  815.  
  816.         case NFSPROC_LINK:
  817.                 printf(" link");
  818.                 dp = parserep(rp, length);
  819.                 if (dp != 0 && parsestatus(dp) != 0)
  820.                         return;
  821.                 break;
  822.  
  823.         case NFSPROC_SYMLINK:
  824.                 printf(" symlink");
  825.                 dp = parserep(rp, length);
  826.                 if (dp != 0 && parsestatus(dp) != 0)
  827.                         return;
  828.                 break;
  829.  
  830.         case NFSPROC_MKDIR:
  831.                 printf(" mkdir");
  832.                 dp = parserep(rp, length);
  833.                 if (dp != 0 && parsediropres(dp) != 0)
  834.                         return;
  835.                 break;
  836.  
  837.         case NFSPROC_RMDIR:
  838.                 printf(" rmdir");
  839.                 dp = parserep(rp, length);
  840.                 if (dp != 0 && parsestatus(dp) != 0)
  841.                         return;
  842.                 break;
  843.  
  844.         case NFSPROC_READDIR:
  845.                 printf(" readdir");
  846.                 dp = parserep(rp, length);
  847.                 if (dp != 0 && parserddires(dp) != 0)
  848.                         return;
  849.                 break;
  850.  
  851.         case NFSPROC_STATFS:
  852.                 printf(" statfs");
  853.                 dp = parserep(rp, length);
  854.                 if (dp != 0 && parsestatfs(dp) != 0)
  855.                         return;
  856.                 break;
  857.  
  858.         default:
  859.                 printf(" proc-%u", proc);
  860.                 return;
  861.         }
  862.         fputs(" [|nfs]", stdout);
  863. }
  864.