]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/tcpdump/print-nfs.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / contrib / tcpdump / print-nfs.c
1 /*
2  * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
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  * $FreeBSD$
22  */
23
24 #ifndef lint
25 static const char rcsid[] _U_ =
26     "@(#) $Header: /tcpdump/master/tcpdump/print-nfs.c,v 1.110.2.1 2007-12-22 03:08:45 guy Exp $ (LBL)";
27 #endif
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <tcpdump-stdinc.h>
34
35 #include <pcap.h>
36 #include <stdio.h>
37 #include <string.h>
38
39 #include "interface.h"
40 #include "addrtoname.h"
41 #include "extract.h"
42
43 #include "nfs.h"
44 #include "nfsfh.h"
45
46 #include "ip.h"
47 #ifdef INET6
48 #include "ip6.h"
49 #endif
50 #include "rpc_auth.h"
51 #include "rpc_msg.h"
52
53 static void nfs_printfh(const u_int32_t *, const u_int);
54 static int xid_map_enter(const struct sunrpc_msg *, const u_char *);
55 static int32_t xid_map_find(const struct sunrpc_msg *, const u_char *,
56                             u_int32_t *, u_int32_t *);
57 static void interp_reply(const struct sunrpc_msg *, u_int32_t, u_int32_t, int);
58 static const u_int32_t *parse_post_op_attr(const u_int32_t *, int);
59 static void print_sattr3(const struct nfsv3_sattr *sa3, int verbose);
60 static void print_nfsaddr(const u_char *, const char *, const char *);
61
62 /*
63  * Mapping of old NFS Version 2 RPC numbers to generic numbers.
64  */
65 u_int32_t nfsv3_procid[NFS_NPROCS] = {
66         NFSPROC_NULL,
67         NFSPROC_GETATTR,
68         NFSPROC_SETATTR,
69         NFSPROC_NOOP,
70         NFSPROC_LOOKUP,
71         NFSPROC_READLINK,
72         NFSPROC_READ,
73         NFSPROC_NOOP,
74         NFSPROC_WRITE,
75         NFSPROC_CREATE,
76         NFSPROC_REMOVE,
77         NFSPROC_RENAME,
78         NFSPROC_LINK,
79         NFSPROC_SYMLINK,
80         NFSPROC_MKDIR,
81         NFSPROC_RMDIR,
82         NFSPROC_READDIR,
83         NFSPROC_FSSTAT,
84         NFSPROC_NOOP,
85         NFSPROC_NOOP,
86         NFSPROC_NOOP,
87         NFSPROC_NOOP,
88         NFSPROC_NOOP,
89         NFSPROC_NOOP,
90         NFSPROC_NOOP,
91         NFSPROC_NOOP
92 };
93
94 /*
95  * NFS V2 and V3 status values.
96  *
97  * Some of these come from the RFCs for NFS V2 and V3, with the message
98  * strings taken from the FreeBSD C library "errlst.c".
99  *
100  * Others are errors that are not in the RFC but that I suspect some
101  * NFS servers could return; the values are FreeBSD errno values, as
102  * the first NFS server was the SunOS 2.0 one, and until 5.0 SunOS
103  * was primarily BSD-derived.
104  */
105 static struct tok status2str[] = {
106         { 1,     "Operation not permitted" },   /* EPERM */
107         { 2,     "No such file or directory" }, /* ENOENT */
108         { 5,     "Input/output error" },        /* EIO */
109         { 6,     "Device not configured" },     /* ENXIO */
110         { 11,    "Resource deadlock avoided" }, /* EDEADLK */
111         { 12,    "Cannot allocate memory" },    /* ENOMEM */
112         { 13,    "Permission denied" },         /* EACCES */
113         { 17,    "File exists" },               /* EEXIST */
114         { 18,    "Cross-device link" },         /* EXDEV */
115         { 19,    "Operation not supported by device" }, /* ENODEV */
116         { 20,    "Not a directory" },           /* ENOTDIR */
117         { 21,    "Is a directory" },            /* EISDIR */
118         { 22,    "Invalid argument" },          /* EINVAL */
119         { 26,    "Text file busy" },            /* ETXTBSY */
120         { 27,    "File too large" },            /* EFBIG */
121         { 28,    "No space left on device" },   /* ENOSPC */
122         { 30,    "Read-only file system" },     /* EROFS */
123         { 31,    "Too many links" },            /* EMLINK */
124         { 45,    "Operation not supported" },   /* EOPNOTSUPP */
125         { 62,    "Too many levels of symbolic links" }, /* ELOOP */
126         { 63,    "File name too long" },        /* ENAMETOOLONG */
127         { 66,    "Directory not empty" },       /* ENOTEMPTY */
128         { 69,    "Disc quota exceeded" },       /* EDQUOT */
129         { 70,    "Stale NFS file handle" },     /* ESTALE */
130         { 71,    "Too many levels of remote in path" }, /* EREMOTE */
131         { 99,    "Write cache flushed to disk" }, /* NFSERR_WFLUSH (not used) */
132         { 10001, "Illegal NFS file handle" },   /* NFS3ERR_BADHANDLE */
133         { 10002, "Update synchronization mismatch" }, /* NFS3ERR_NOT_SYNC */
134         { 10003, "READDIR/READDIRPLUS cookie is stale" }, /* NFS3ERR_BAD_COOKIE */
135         { 10004, "Operation not supported" },   /* NFS3ERR_NOTSUPP */
136         { 10005, "Buffer or request is too small" }, /* NFS3ERR_TOOSMALL */
137         { 10006, "Unspecified error on server" }, /* NFS3ERR_SERVERFAULT */
138         { 10007, "Object of that type not supported" }, /* NFS3ERR_BADTYPE */
139         { 10008, "Request couldn't be completed in time" }, /* NFS3ERR_JUKEBOX */
140         { 0,     NULL }
141 };
142
143 static struct tok nfsv3_writemodes[] = {
144         { 0,            "unstable" },
145         { 1,            "datasync" },
146         { 2,            "filesync" },
147         { 0,            NULL }
148 };
149
150 static struct tok type2str[] = {
151         { NFNON,        "NON" },
152         { NFREG,        "REG" },
153         { NFDIR,        "DIR" },
154         { NFBLK,        "BLK" },
155         { NFCHR,        "CHR" },
156         { NFLNK,        "LNK" },
157         { NFFIFO,       "FIFO" },
158         { 0,            NULL }
159 };
160
161 static void
162 print_nfsaddr(const u_char *bp, const char *s, const char *d)
163 {
164         struct ip *ip;
165 #ifdef INET6
166         struct ip6_hdr *ip6;
167         char srcaddr[INET6_ADDRSTRLEN], dstaddr[INET6_ADDRSTRLEN];
168 #else
169 #ifndef INET_ADDRSTRLEN
170 #define INET_ADDRSTRLEN 16
171 #endif
172         char srcaddr[INET_ADDRSTRLEN], dstaddr[INET_ADDRSTRLEN];
173 #endif
174
175         srcaddr[0] = dstaddr[0] = '\0';
176         switch (IP_V((struct ip *)bp)) {
177         case 4:
178                 ip = (struct ip *)bp;
179                 strlcpy(srcaddr, ipaddr_string(&ip->ip_src), sizeof(srcaddr));
180                 strlcpy(dstaddr, ipaddr_string(&ip->ip_dst), sizeof(dstaddr));
181                 break;
182 #ifdef INET6
183         case 6:
184                 ip6 = (struct ip6_hdr *)bp;
185                 strlcpy(srcaddr, ip6addr_string(&ip6->ip6_src),
186                     sizeof(srcaddr));
187                 strlcpy(dstaddr, ip6addr_string(&ip6->ip6_dst),
188                     sizeof(dstaddr));
189                 break;
190 #endif
191         default:
192                 strlcpy(srcaddr, "?", sizeof(srcaddr));
193                 strlcpy(dstaddr, "?", sizeof(dstaddr));
194                 break;
195         }
196
197         (void)printf("%s.%s > %s.%s: ", srcaddr, s, dstaddr, d);
198 }
199
200 static const u_int32_t *
201 parse_sattr3(const u_int32_t *dp, struct nfsv3_sattr *sa3)
202 {
203         TCHECK(dp[0]);
204         sa3->sa_modeset = EXTRACT_32BITS(dp);
205         dp++;
206         if (sa3->sa_modeset) {
207                 TCHECK(dp[0]);
208                 sa3->sa_mode = EXTRACT_32BITS(dp);
209                 dp++;
210         }
211
212         TCHECK(dp[0]);
213         sa3->sa_uidset = EXTRACT_32BITS(dp);
214         dp++;
215         if (sa3->sa_uidset) {
216                 TCHECK(dp[0]);
217                 sa3->sa_uid = EXTRACT_32BITS(dp);
218                 dp++;
219         }
220
221         TCHECK(dp[0]);
222         sa3->sa_gidset = EXTRACT_32BITS(dp);
223         dp++;
224         if (sa3->sa_gidset) {
225                 TCHECK(dp[0]);
226                 sa3->sa_gid = EXTRACT_32BITS(dp);
227                 dp++;
228         }
229
230         TCHECK(dp[0]);
231         sa3->sa_sizeset = EXTRACT_32BITS(dp);
232         dp++;
233         if (sa3->sa_sizeset) {
234                 TCHECK(dp[0]);
235                 sa3->sa_size = EXTRACT_32BITS(dp);
236                 dp++;
237         }
238
239         TCHECK(dp[0]);
240         sa3->sa_atimetype = EXTRACT_32BITS(dp);
241         dp++;
242         if (sa3->sa_atimetype == NFSV3SATTRTIME_TOCLIENT) {
243                 TCHECK(dp[1]);
244                 sa3->sa_atime.nfsv3_sec = EXTRACT_32BITS(dp);
245                 dp++;
246                 sa3->sa_atime.nfsv3_nsec = EXTRACT_32BITS(dp);
247                 dp++;
248         }
249
250         TCHECK(dp[0]);
251         sa3->sa_mtimetype = EXTRACT_32BITS(dp);
252         dp++;
253         if (sa3->sa_mtimetype == NFSV3SATTRTIME_TOCLIENT) {
254                 TCHECK(dp[1]);
255                 sa3->sa_mtime.nfsv3_sec = EXTRACT_32BITS(dp);
256                 dp++;
257                 sa3->sa_mtime.nfsv3_nsec = EXTRACT_32BITS(dp);
258                 dp++;
259         }
260
261         return dp;
262 trunc:
263         return NULL;
264 }
265
266 static int nfserr;              /* true if we error rather than trunc */
267
268 static void
269 print_sattr3(const struct nfsv3_sattr *sa3, int verbose)
270 {
271         if (sa3->sa_modeset)
272                 printf(" mode %o", sa3->sa_mode);
273         if (sa3->sa_uidset)
274                 printf(" uid %u", sa3->sa_uid);
275         if (sa3->sa_gidset)
276                 printf(" gid %u", sa3->sa_gid);
277         if (verbose > 1) {
278                 if (sa3->sa_atimetype == NFSV3SATTRTIME_TOCLIENT)
279                         printf(" atime %u.%06u", sa3->sa_atime.nfsv3_sec,
280                                sa3->sa_atime.nfsv3_nsec);
281                 if (sa3->sa_mtimetype == NFSV3SATTRTIME_TOCLIENT)
282                         printf(" mtime %u.%06u", sa3->sa_mtime.nfsv3_sec,
283                                sa3->sa_mtime.nfsv3_nsec);
284         }
285 }
286
287 void
288 nfsreply_print(register const u_char *bp, u_int length,
289                register const u_char *bp2)
290 {
291         register const struct sunrpc_msg *rp;
292         u_int32_t proc, vers, reply_stat;
293         char srcid[20], dstid[20];      /*fits 32bit*/
294         enum sunrpc_reject_stat rstat;
295         u_int32_t rlow;
296         u_int32_t rhigh;
297         enum sunrpc_auth_stat rwhy;
298
299         nfserr = 0;             /* assume no error */
300         rp = (const struct sunrpc_msg *)bp;
301
302         TCHECK(rp->rm_xid);
303         if (!nflag) {
304                 strlcpy(srcid, "nfs", sizeof(srcid));
305                 snprintf(dstid, sizeof(dstid), "%u",
306                     EXTRACT_32BITS(&rp->rm_xid));
307         } else {
308                 snprintf(srcid, sizeof(srcid), "%u", NFS_PORT);
309                 snprintf(dstid, sizeof(dstid), "%u",
310                     EXTRACT_32BITS(&rp->rm_xid));
311         }
312         print_nfsaddr(bp2, srcid, dstid);
313         TCHECK(rp->rm_reply.rp_stat);
314         reply_stat = EXTRACT_32BITS(&rp->rm_reply.rp_stat);
315         switch (reply_stat) {
316
317         case SUNRPC_MSG_ACCEPTED:
318                 (void)printf("reply ok %u", length);
319                 if (xid_map_find(rp, bp2, &proc, &vers) >= 0)
320                         interp_reply(rp, proc, vers, length);
321                 break;
322
323         case SUNRPC_MSG_DENIED:
324                 (void)printf("reply ERR %u: ", length);
325                 TCHECK(rp->rm_reply.rp_reject.rj_stat);
326                 rstat = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_stat);
327                 switch (rstat) {
328
329                 case SUNRPC_RPC_MISMATCH:
330                         TCHECK(rp->rm_reply.rp_reject.rj_vers.high);
331                         rlow = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_vers.low);
332                         rhigh = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_vers.high);
333                         (void)printf("RPC Version mismatch (%u-%u)",
334                             rlow, rhigh);
335                         break;
336
337                 case SUNRPC_AUTH_ERROR:
338                         TCHECK(rp->rm_reply.rp_reject.rj_why);
339                         rwhy = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_why);
340                         (void)printf("Auth ");
341                         switch (rwhy) {
342
343                         case SUNRPC_AUTH_OK:
344                                 (void)printf("OK");
345                                 break;
346
347                         case SUNRPC_AUTH_BADCRED:
348                                 (void)printf("Bogus Credentials (seal broken)");
349                                 break;
350
351                         case SUNRPC_AUTH_REJECTEDCRED:
352                                 (void)printf("Rejected Credentials (client should begin new session)");
353                                 break;
354
355                         case SUNRPC_AUTH_BADVERF:
356                                 (void)printf("Bogus Verifier (seal broken)");
357                                 break;
358
359                         case SUNRPC_AUTH_REJECTEDVERF:
360                                 (void)printf("Verifier expired or was replayed");
361                                 break;
362
363                         case SUNRPC_AUTH_TOOWEAK:
364                                 (void)printf("Credentials are too weak");
365                                 break;
366
367                         case SUNRPC_AUTH_INVALIDRESP:
368                                 (void)printf("Bogus response verifier");
369                                 break;
370
371                         case SUNRPC_AUTH_FAILED:
372                                 (void)printf("Unknown failure");
373                                 break;
374
375                         default:
376                                 (void)printf("Invalid failure code %u",
377                                     (unsigned int)rwhy);
378                                 break;
379                         }
380                         break;
381
382                 default:
383                         (void)printf("Unknown reason for rejecting rpc message %u",
384                             (unsigned int)rstat);
385                         break;
386                 }
387                 break;
388
389         default:
390                 (void)printf("reply Unknown rpc response code=%u %u",
391                     reply_stat, length);
392                 break;
393         }
394         return;
395
396 trunc:
397         if (!nfserr)
398                 fputs(" [|nfs]", stdout);
399 }
400
401 /*
402  * Return a pointer to the first file handle in the packet.
403  * If the packet was truncated, return 0.
404  */
405 static const u_int32_t *
406 parsereq(register const struct sunrpc_msg *rp, register u_int length)
407 {
408         register const u_int32_t *dp;
409         register u_int len;
410
411         /*
412          * find the start of the req data (if we captured it)
413          */
414         dp = (u_int32_t *)&rp->rm_call.cb_cred;
415         TCHECK(dp[1]);
416         len = EXTRACT_32BITS(&dp[1]);
417         if (len < length) {
418                 dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
419                 TCHECK(dp[1]);
420                 len = EXTRACT_32BITS(&dp[1]);
421                 if (len < length) {
422                         dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
423                         TCHECK2(dp[0], 0);
424                         return (dp);
425                 }
426         }
427 trunc:
428         return (NULL);
429 }
430
431 /*
432  * Print out an NFS file handle and return a pointer to following word.
433  * If packet was truncated, return 0.
434  */
435 static const u_int32_t *
436 parsefh(register const u_int32_t *dp, int v3)
437 {
438         u_int len;
439
440         if (v3) {
441                 TCHECK(dp[0]);
442                 len = EXTRACT_32BITS(dp) / 4;
443                 dp++;
444         } else
445                 len = NFSX_V2FH / 4;
446
447         if (TTEST2(*dp, len * sizeof(*dp))) {
448                 nfs_printfh(dp, len);
449                 return (dp + len);
450         }
451 trunc:
452         return (NULL);
453 }
454
455 /*
456  * Print out a file name and return pointer to 32-bit word past it.
457  * If packet was truncated, return 0.
458  */
459 static const u_int32_t *
460 parsefn(register const u_int32_t *dp)
461 {
462         register u_int32_t len;
463         register const u_char *cp;
464
465         /* Bail if we don't have the string length */
466         TCHECK(*dp);
467
468         /* Fetch string length; convert to host order */
469         len = *dp++;
470         NTOHL(len);
471
472         TCHECK2(*dp, ((len + 3) & ~3));
473
474         cp = (u_char *)dp;
475         /* Update 32-bit pointer (NFS filenames padded to 32-bit boundaries) */
476         dp += ((len + 3) & ~3) / sizeof(*dp);
477         putchar('"');
478         if (fn_printn(cp, len, snapend)) {
479                 putchar('"');
480                 goto trunc;
481         }
482         putchar('"');
483
484         return (dp);
485 trunc:
486         return NULL;
487 }
488
489 /*
490  * Print out file handle and file name.
491  * Return pointer to 32-bit word past file name.
492  * If packet was truncated (or there was some other error), return 0.
493  */
494 static const u_int32_t *
495 parsefhn(register const u_int32_t *dp, int v3)
496 {
497         dp = parsefh(dp, v3);
498         if (dp == NULL)
499                 return (NULL);
500         putchar(' ');
501         return (parsefn(dp));
502 }
503
504 void
505 nfsreq_print(register const u_char *bp, u_int length,
506     register const u_char *bp2)
507 {
508         register const struct sunrpc_msg *rp;
509         register const u_int32_t *dp;
510         nfs_type type;
511         int v3;
512         u_int32_t proc;
513         struct nfsv3_sattr sa3;
514         char srcid[20], dstid[20];      /*fits 32bit*/
515
516         nfserr = 0;             /* assume no error */
517         rp = (const struct sunrpc_msg *)bp;
518
519         TCHECK(rp->rm_xid);
520         if (!nflag) {
521                 snprintf(srcid, sizeof(srcid), "%u",
522                     EXTRACT_32BITS(&rp->rm_xid));
523                 strlcpy(dstid, "nfs", sizeof(dstid));
524         } else {
525                 snprintf(srcid, sizeof(srcid), "%u",
526                     EXTRACT_32BITS(&rp->rm_xid));
527                 snprintf(dstid, sizeof(dstid), "%u", NFS_PORT);
528         }
529         print_nfsaddr(bp2, srcid, dstid);
530         (void)printf("%d", length);
531
532         if (!xid_map_enter(rp, bp2))    /* record proc number for later on */
533                 goto trunc;
534
535         v3 = (EXTRACT_32BITS(&rp->rm_call.cb_vers) == NFS_VER3);
536         proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
537
538         if (!v3 && proc < NFS_NPROCS)
539                 proc =  nfsv3_procid[proc];
540
541         switch (proc) {
542         case NFSPROC_NOOP:
543                 printf(" nop");
544                 return;
545         case NFSPROC_NULL:
546                 printf(" null");
547                 return;
548
549         case NFSPROC_GETATTR:
550                 printf(" getattr");
551                 if ((dp = parsereq(rp, length)) != NULL &&
552                     parsefh(dp, v3) != NULL)
553                         return;
554                 break;
555
556         case NFSPROC_SETATTR:
557                 printf(" setattr");
558                 if ((dp = parsereq(rp, length)) != NULL &&
559                     parsefh(dp, v3) != NULL)
560                         return;
561                 break;
562
563         case NFSPROC_LOOKUP:
564                 printf(" lookup");
565                 if ((dp = parsereq(rp, length)) != NULL &&
566                     parsefhn(dp, v3) != NULL)
567                         return;
568                 break;
569
570         case NFSPROC_ACCESS:
571                 printf(" access");
572                 if ((dp = parsereq(rp, length)) != NULL &&
573                     (dp = parsefh(dp, v3)) != NULL) {
574                         TCHECK(dp[0]);
575                         printf(" %04x", EXTRACT_32BITS(&dp[0]));
576                         return;
577                 }
578                 break;
579
580         case NFSPROC_READLINK:
581                 printf(" readlink");
582                 if ((dp = parsereq(rp, length)) != NULL &&
583                     parsefh(dp, v3) != NULL)
584                         return;
585                 break;
586
587         case NFSPROC_READ:
588                 printf(" read");
589                 if ((dp = parsereq(rp, length)) != NULL &&
590                     (dp = parsefh(dp, v3)) != NULL) {
591                         if (v3) {
592                                 TCHECK(dp[2]);
593                                 printf(" %u bytes @ %" PRIu64,
594                                        EXTRACT_32BITS(&dp[2]),
595                                        EXTRACT_64BITS(&dp[0]));
596                         } else {
597                                 TCHECK(dp[1]);
598                                 printf(" %u bytes @ %u",
599                                     EXTRACT_32BITS(&dp[1]),
600                                     EXTRACT_32BITS(&dp[0]));
601                         }
602                         return;
603                 }
604                 break;
605
606         case NFSPROC_WRITE:
607                 printf(" write");
608                 if ((dp = parsereq(rp, length)) != NULL &&
609                     (dp = parsefh(dp, v3)) != NULL) {
610                         if (v3) {
611                                 TCHECK(dp[2]);
612                                 printf(" %u (%u) bytes @ %" PRIu64,
613                                                 EXTRACT_32BITS(&dp[4]),
614                                                 EXTRACT_32BITS(&dp[2]),
615                                                 EXTRACT_64BITS(&dp[0]));
616                                 if (vflag) {
617                                         dp += 3;
618                                         TCHECK(dp[0]);
619                                         printf(" <%s>",
620                                                 tok2str(nfsv3_writemodes,
621                                                         NULL, EXTRACT_32BITS(dp)));
622                                 }
623                         } else {
624                                 TCHECK(dp[3]);
625                                 printf(" %u (%u) bytes @ %u (%u)",
626                                                 EXTRACT_32BITS(&dp[3]),
627                                                 EXTRACT_32BITS(&dp[2]),
628                                                 EXTRACT_32BITS(&dp[1]),
629                                                 EXTRACT_32BITS(&dp[0]));
630                         }
631                         return;
632                 }
633                 break;
634
635         case NFSPROC_CREATE:
636                 printf(" create");
637                 if ((dp = parsereq(rp, length)) != NULL &&
638                     parsefhn(dp, v3) != NULL)
639                         return;
640                 break;
641
642         case NFSPROC_MKDIR:
643                 printf(" mkdir");
644                 if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp, v3) != 0)
645                         return;
646                 break;
647
648         case NFSPROC_SYMLINK:
649                 printf(" symlink");
650                 if ((dp = parsereq(rp, length)) != 0 &&
651                     (dp = parsefhn(dp, v3)) != 0) {
652                         fputs(" ->", stdout);
653                         if (v3 && (dp = parse_sattr3(dp, &sa3)) == 0)
654                                 break;
655                         if (parsefn(dp) == 0)
656                                 break;
657                         if (v3 && vflag)
658                                 print_sattr3(&sa3, vflag);
659                         return;
660                 }
661                 break;
662
663         case NFSPROC_MKNOD:
664                 printf(" mknod");
665                 if ((dp = parsereq(rp, length)) != 0 &&
666                     (dp = parsefhn(dp, v3)) != 0) {
667                         TCHECK(*dp);
668                         type = (nfs_type)EXTRACT_32BITS(dp);
669                         dp++;
670                         if ((dp = parse_sattr3(dp, &sa3)) == 0)
671                                 break;
672                         printf(" %s", tok2str(type2str, "unk-ft %d", type));
673                         if (vflag && (type == NFCHR || type == NFBLK)) {
674                                 TCHECK(dp[1]);
675                                 printf(" %u/%u",
676                                        EXTRACT_32BITS(&dp[0]),
677                                        EXTRACT_32BITS(&dp[1]));
678                                 dp += 2;
679                         }
680                         if (vflag)
681                                 print_sattr3(&sa3, vflag);
682                         return;
683                 }
684                 break;
685
686         case NFSPROC_REMOVE:
687                 printf(" remove");
688                 if ((dp = parsereq(rp, length)) != NULL &&
689                     parsefhn(dp, v3) != NULL)
690                         return;
691                 break;
692
693         case NFSPROC_RMDIR:
694                 printf(" rmdir");
695                 if ((dp = parsereq(rp, length)) != NULL &&
696                     parsefhn(dp, v3) != NULL)
697                         return;
698                 break;
699
700         case NFSPROC_RENAME:
701                 printf(" rename");
702                 if ((dp = parsereq(rp, length)) != NULL &&
703                     (dp = parsefhn(dp, v3)) != NULL) {
704                         fputs(" ->", stdout);
705                         if (parsefhn(dp, v3) != NULL)
706                                 return;
707                 }
708                 break;
709
710         case NFSPROC_LINK:
711                 printf(" link");
712                 if ((dp = parsereq(rp, length)) != NULL &&
713                     (dp = parsefh(dp, v3)) != NULL) {
714                         fputs(" ->", stdout);
715                         if (parsefhn(dp, v3) != NULL)
716                                 return;
717                 }
718                 break;
719
720         case NFSPROC_READDIR:
721                 printf(" readdir");
722                 if ((dp = parsereq(rp, length)) != NULL &&
723                     (dp = parsefh(dp, v3)) != NULL) {
724                         if (v3) {
725                                 TCHECK(dp[4]);
726                                 /*
727                                  * We shouldn't really try to interpret the
728                                  * offset cookie here.
729                                  */
730                                 printf(" %u bytes @ %" PRId64,
731                                     EXTRACT_32BITS(&dp[4]),
732                                     EXTRACT_64BITS(&dp[0]));
733                                 if (vflag)
734                                         printf(" verf %08x%08x", dp[2],
735                                                dp[3]);
736                         } else {
737                                 TCHECK(dp[1]);
738                                 /*
739                                  * Print the offset as signed, since -1 is
740                                  * common, but offsets > 2^31 aren't.
741                                  */
742                                 printf(" %u bytes @ %d",
743                                     EXTRACT_32BITS(&dp[1]),
744                                     EXTRACT_32BITS(&dp[0]));
745                         }
746                         return;
747                 }
748                 break;
749
750         case NFSPROC_READDIRPLUS:
751                 printf(" readdirplus");
752                 if ((dp = parsereq(rp, length)) != NULL &&
753                     (dp = parsefh(dp, v3)) != NULL) {
754                         TCHECK(dp[4]);
755                         /*
756                          * We don't try to interpret the offset
757                          * cookie here.
758                          */
759                         printf(" %u bytes @ %" PRId64,
760                                 EXTRACT_32BITS(&dp[4]),
761                                 EXTRACT_64BITS(&dp[0]));
762                         if (vflag) {
763                                 TCHECK(dp[5]);
764                                 printf(" max %u verf %08x%08x",
765                                        EXTRACT_32BITS(&dp[5]), dp[2], dp[3]);
766                         }
767                         return;
768                 }
769                 break;
770
771         case NFSPROC_FSSTAT:
772                 printf(" fsstat");
773                 if ((dp = parsereq(rp, length)) != NULL &&
774                     parsefh(dp, v3) != NULL)
775                         return;
776                 break;
777
778         case NFSPROC_FSINFO:
779                 printf(" fsinfo");
780                 if ((dp = parsereq(rp, length)) != NULL &&
781                     parsefh(dp, v3) != NULL)
782                         return;
783                 break;
784
785         case NFSPROC_PATHCONF:
786                 printf(" pathconf");
787                 if ((dp = parsereq(rp, length)) != NULL &&
788                     parsefh(dp, v3) != NULL)
789                         return;
790                 break;
791
792         case NFSPROC_COMMIT:
793                 printf(" commit");
794                 if ((dp = parsereq(rp, length)) != NULL &&
795                     (dp = parsefh(dp, v3)) != NULL) {
796                         TCHECK(dp[2]);
797                         printf(" %u bytes @ %" PRIu64,
798                                 EXTRACT_32BITS(&dp[2]),
799                                 EXTRACT_64BITS(&dp[0]));
800                         return;
801                 }
802                 break;
803
804         default:
805                 printf(" proc-%u", EXTRACT_32BITS(&rp->rm_call.cb_proc));
806                 return;
807         }
808
809 trunc:
810         if (!nfserr)
811                 fputs(" [|nfs]", stdout);
812 }
813
814 /*
815  * Print out an NFS file handle.
816  * We assume packet was not truncated before the end of the
817  * file handle pointed to by dp.
818  *
819  * Note: new version (using portable file-handle parser) doesn't produce
820  * generation number.  It probably could be made to do that, with some
821  * additional hacking on the parser code.
822  */
823 static void
824 nfs_printfh(register const u_int32_t *dp, const u_int len)
825 {
826         my_fsid fsid;
827         ino_t ino;
828         const char *sfsname = NULL;
829         char *spacep;
830
831         if (uflag) {
832                 u_int i;
833                 char const *sep = "";
834
835                 printf(" fh[");
836                 for (i=0; i<len; i++) {
837                         (void)printf("%s%x", sep, dp[i]);
838                         sep = ":";
839                 }
840                 printf("]");
841                 return;
842         }
843
844         Parse_fh((const u_char *)dp, len, &fsid, &ino, NULL, &sfsname, 0);
845
846         if (sfsname) {
847                 /* file system ID is ASCII, not numeric, for this server OS */
848                 static char temp[NFSX_V3FHMAX+1];
849
850                 /* Make sure string is null-terminated */
851                 strncpy(temp, sfsname, NFSX_V3FHMAX);
852                 temp[sizeof(temp) - 1] = '\0';
853                 /* Remove trailing spaces */
854                 spacep = strchr(temp, ' ');
855                 if (spacep)
856                         *spacep = '\0';
857
858                 (void)printf(" fh %s/", temp);
859         } else {
860                 (void)printf(" fh %d,%d/",
861                              fsid.Fsid_dev.Major, fsid.Fsid_dev.Minor);
862         }
863
864         if(fsid.Fsid_dev.Minor == 257)
865                 /* Print the undecoded handle */
866                 (void)printf("%s", fsid.Opaque_Handle);
867         else
868                 (void)printf("%ld", (long) ino);
869 }
870
871 /*
872  * Maintain a small cache of recent client.XID.server/proc pairs, to allow
873  * us to match up replies with requests and thus to know how to parse
874  * the reply.
875  */
876
877 struct xid_map_entry {
878         u_int32_t       xid;            /* transaction ID (net order) */
879         int ipver;                      /* IP version (4 or 6) */
880 #ifdef INET6
881         struct in6_addr client;         /* client IP address (net order) */
882         struct in6_addr server;         /* server IP address (net order) */
883 #else
884         struct in_addr  client;         /* client IP address (net order) */
885         struct in_addr  server;         /* server IP address (net order) */
886 #endif
887         u_int32_t       proc;           /* call proc number (host order) */
888         u_int32_t       vers;           /* program version (host order) */
889 };
890
891 /*
892  * Map entries are kept in an array that we manage as a ring;
893  * new entries are always added at the tail of the ring.  Initially,
894  * all the entries are zero and hence don't match anything.
895  */
896
897 #define XIDMAPSIZE      64
898
899 struct xid_map_entry xid_map[XIDMAPSIZE];
900
901 int     xid_map_next = 0;
902 int     xid_map_hint = 0;
903
904 static int
905 xid_map_enter(const struct sunrpc_msg *rp, const u_char *bp)
906 {
907         struct ip *ip = NULL;
908 #ifdef INET6
909         struct ip6_hdr *ip6 = NULL;
910 #endif
911         struct xid_map_entry *xmep;
912
913         if (!TTEST(rp->rm_call.cb_vers))
914                 return (0);
915         switch (IP_V((struct ip *)bp)) {
916         case 4:
917                 ip = (struct ip *)bp;
918                 break;
919 #ifdef INET6
920         case 6:
921                 ip6 = (struct ip6_hdr *)bp;
922                 break;
923 #endif
924         default:
925                 return (1);
926         }
927
928         xmep = &xid_map[xid_map_next];
929
930         if (++xid_map_next >= XIDMAPSIZE)
931                 xid_map_next = 0;
932
933         xmep->xid = rp->rm_xid;
934         if (ip) {
935                 xmep->ipver = 4;
936                 memcpy(&xmep->client, &ip->ip_src, sizeof(ip->ip_src));
937                 memcpy(&xmep->server, &ip->ip_dst, sizeof(ip->ip_dst));
938         }
939 #ifdef INET6
940         else if (ip6) {
941                 xmep->ipver = 6;
942                 memcpy(&xmep->client, &ip6->ip6_src, sizeof(ip6->ip6_src));
943                 memcpy(&xmep->server, &ip6->ip6_dst, sizeof(ip6->ip6_dst));
944         }
945 #endif
946         xmep->proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
947         xmep->vers = EXTRACT_32BITS(&rp->rm_call.cb_vers);
948         return (1);
949 }
950
951 /*
952  * Returns 0 and puts NFSPROC_xxx in proc return and
953  * version in vers return, or returns -1 on failure
954  */
955 static int
956 xid_map_find(const struct sunrpc_msg *rp, const u_char *bp, u_int32_t *proc,
957              u_int32_t *vers)
958 {
959         int i;
960         struct xid_map_entry *xmep;
961         u_int32_t xid = rp->rm_xid;
962         struct ip *ip = (struct ip *)bp;
963 #ifdef INET6
964         struct ip6_hdr *ip6 = (struct ip6_hdr *)bp;
965 #endif
966         int cmp;
967
968         /* Start searching from where we last left off */
969         i = xid_map_hint;
970         do {
971                 xmep = &xid_map[i];
972                 cmp = 1;
973                 if (xmep->ipver != IP_V(ip) || xmep->xid != xid)
974                         goto nextitem;
975                 switch (xmep->ipver) {
976                 case 4:
977                         if (memcmp(&ip->ip_src, &xmep->server,
978                                    sizeof(ip->ip_src)) != 0 ||
979                             memcmp(&ip->ip_dst, &xmep->client,
980                                    sizeof(ip->ip_dst)) != 0) {
981                                 cmp = 0;
982                         }
983                         break;
984 #ifdef INET6
985                 case 6:
986                         if (memcmp(&ip6->ip6_src, &xmep->server,
987                                    sizeof(ip6->ip6_src)) != 0 ||
988                             memcmp(&ip6->ip6_dst, &xmep->client,
989                                    sizeof(ip6->ip6_dst)) != 0) {
990                                 cmp = 0;
991                         }
992                         break;
993 #endif
994                 default:
995                         cmp = 0;
996                         break;
997                 }
998                 if (cmp) {
999                         /* match */
1000                         xid_map_hint = i;
1001                         *proc = xmep->proc;
1002                         *vers = xmep->vers;
1003                         return 0;
1004                 }
1005         nextitem:
1006                 if (++i >= XIDMAPSIZE)
1007                         i = 0;
1008         } while (i != xid_map_hint);
1009
1010         /* search failed */
1011         return (-1);
1012 }
1013
1014 /*
1015  * Routines for parsing reply packets
1016  */
1017
1018 /*
1019  * Return a pointer to the beginning of the actual results.
1020  * If the packet was truncated, return 0.
1021  */
1022 static const u_int32_t *
1023 parserep(register const struct sunrpc_msg *rp, register u_int length)
1024 {
1025         register const u_int32_t *dp;
1026         u_int len;
1027         enum sunrpc_accept_stat astat;
1028
1029         /*
1030          * Portability note:
1031          * Here we find the address of the ar_verf credentials.
1032          * Originally, this calculation was
1033          *      dp = (u_int32_t *)&rp->rm_reply.rp_acpt.ar_verf
1034          * On the wire, the rp_acpt field starts immediately after
1035          * the (32 bit) rp_stat field.  However, rp_acpt (which is a
1036          * "struct accepted_reply") contains a "struct opaque_auth",
1037          * whose internal representation contains a pointer, so on a
1038          * 64-bit machine the compiler inserts 32 bits of padding
1039          * before rp->rm_reply.rp_acpt.ar_verf.  So, we cannot use
1040          * the internal representation to parse the on-the-wire
1041          * representation.  Instead, we skip past the rp_stat field,
1042          * which is an "enum" and so occupies one 32-bit word.
1043          */
1044         dp = ((const u_int32_t *)&rp->rm_reply) + 1;
1045         TCHECK(dp[1]);
1046         len = EXTRACT_32BITS(&dp[1]);
1047         if (len >= length)
1048                 return (NULL);
1049         /*
1050          * skip past the ar_verf credentials.
1051          */
1052         dp += (len + (2*sizeof(u_int32_t) + 3)) / sizeof(u_int32_t);
1053         TCHECK2(dp[0], 0);
1054
1055         /*
1056          * now we can check the ar_stat field
1057          */
1058         astat = (enum sunrpc_accept_stat) EXTRACT_32BITS(dp);
1059         switch (astat) {
1060
1061         case SUNRPC_SUCCESS:
1062                 break;
1063
1064         case SUNRPC_PROG_UNAVAIL:
1065                 printf(" PROG_UNAVAIL");
1066                 nfserr = 1;             /* suppress trunc string */
1067                 return (NULL);
1068
1069         case SUNRPC_PROG_MISMATCH:
1070                 printf(" PROG_MISMATCH");
1071                 nfserr = 1;             /* suppress trunc string */
1072                 return (NULL);
1073
1074         case SUNRPC_PROC_UNAVAIL:
1075                 printf(" PROC_UNAVAIL");
1076                 nfserr = 1;             /* suppress trunc string */
1077                 return (NULL);
1078
1079         case SUNRPC_GARBAGE_ARGS:
1080                 printf(" GARBAGE_ARGS");
1081                 nfserr = 1;             /* suppress trunc string */
1082                 return (NULL);
1083
1084         case SUNRPC_SYSTEM_ERR:
1085                 printf(" SYSTEM_ERR");
1086                 nfserr = 1;             /* suppress trunc string */
1087                 return (NULL);
1088
1089         default:
1090                 printf(" ar_stat %d", astat);
1091                 nfserr = 1;             /* suppress trunc string */
1092                 return (NULL);
1093         }
1094         /* successful return */
1095         TCHECK2(*dp, sizeof(astat));
1096         return ((u_int32_t *) (sizeof(astat) + ((char *)dp)));
1097 trunc:
1098         return (0);
1099 }
1100
1101 static const u_int32_t *
1102 parsestatus(const u_int32_t *dp, int *er)
1103 {
1104         int errnum;
1105
1106         TCHECK(dp[0]);
1107
1108         errnum = EXTRACT_32BITS(&dp[0]);
1109         if (er)
1110                 *er = errnum;
1111         if (errnum != 0) {
1112                 if (!qflag)
1113                         printf(" ERROR: %s",
1114                             tok2str(status2str, "unk %d", errnum));
1115                 nfserr = 1;
1116         }
1117         return (dp + 1);
1118 trunc:
1119         return NULL;
1120 }
1121
1122 static const u_int32_t *
1123 parsefattr(const u_int32_t *dp, int verbose, int v3)
1124 {
1125         const struct nfs_fattr *fap;
1126
1127         fap = (const struct nfs_fattr *)dp;
1128         TCHECK(fap->fa_gid);
1129         if (verbose) {
1130                 printf(" %s %o ids %d/%d",
1131                     tok2str(type2str, "unk-ft %d ",
1132                     EXTRACT_32BITS(&fap->fa_type)),
1133                     EXTRACT_32BITS(&fap->fa_mode),
1134                     EXTRACT_32BITS(&fap->fa_uid),
1135                     EXTRACT_32BITS(&fap->fa_gid));
1136                 if (v3) {
1137                         TCHECK(fap->fa3_size);
1138                         printf(" sz %" PRIu64,
1139                                 EXTRACT_64BITS((u_int32_t *)&fap->fa3_size));
1140                 } else {
1141                         TCHECK(fap->fa2_size);
1142                         printf(" sz %d", EXTRACT_32BITS(&fap->fa2_size));
1143                 }
1144         }
1145         /* print lots more stuff */
1146         if (verbose > 1) {
1147                 if (v3) {
1148                         TCHECK(fap->fa3_ctime);
1149                         printf(" nlink %d rdev %d/%d",
1150                                EXTRACT_32BITS(&fap->fa_nlink),
1151                                EXTRACT_32BITS(&fap->fa3_rdev.specdata1),
1152                                EXTRACT_32BITS(&fap->fa3_rdev.specdata2));
1153                         printf(" fsid %" PRIx64,
1154                                 EXTRACT_64BITS((u_int32_t *)&fap->fa3_fsid));
1155                         printf(" fileid %" PRIx64,
1156                                 EXTRACT_64BITS((u_int32_t *)&fap->fa3_fileid));
1157                         printf(" a/m/ctime %u.%06u",
1158                                EXTRACT_32BITS(&fap->fa3_atime.nfsv3_sec),
1159                                EXTRACT_32BITS(&fap->fa3_atime.nfsv3_nsec));
1160                         printf(" %u.%06u",
1161                                EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_sec),
1162                                EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_nsec));
1163                         printf(" %u.%06u",
1164                                EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_sec),
1165                                EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_nsec));
1166                 } else {
1167                         TCHECK(fap->fa2_ctime);
1168                         printf(" nlink %d rdev %x fsid %x nodeid %x a/m/ctime",
1169                                EXTRACT_32BITS(&fap->fa_nlink),
1170                                EXTRACT_32BITS(&fap->fa2_rdev),
1171                                EXTRACT_32BITS(&fap->fa2_fsid),
1172                                EXTRACT_32BITS(&fap->fa2_fileid));
1173                         printf(" %u.%06u",
1174                                EXTRACT_32BITS(&fap->fa2_atime.nfsv2_sec),
1175                                EXTRACT_32BITS(&fap->fa2_atime.nfsv2_usec));
1176                         printf(" %u.%06u",
1177                                EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_sec),
1178                                EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_usec));
1179                         printf(" %u.%06u",
1180                                EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_sec),
1181                                EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_usec));
1182                 }
1183         }
1184         return ((const u_int32_t *)((unsigned char *)dp +
1185                 (v3 ? NFSX_V3FATTR : NFSX_V2FATTR)));
1186 trunc:
1187         return (NULL);
1188 }
1189
1190 static int
1191 parseattrstat(const u_int32_t *dp, int verbose, int v3)
1192 {
1193         int er;
1194
1195         dp = parsestatus(dp, &er);
1196         if (dp == NULL)
1197                 return (0);
1198         if (er)
1199                 return (1);
1200
1201         return (parsefattr(dp, verbose, v3) != NULL);
1202 }
1203
1204 static int
1205 parsediropres(const u_int32_t *dp)
1206 {
1207         int er;
1208
1209         if (!(dp = parsestatus(dp, &er)))
1210                 return (0);
1211         if (er)
1212                 return (1);
1213
1214         dp = parsefh(dp, 0);
1215         if (dp == NULL)
1216                 return (0);
1217
1218         return (parsefattr(dp, vflag, 0) != NULL);
1219 }
1220
1221 static int
1222 parselinkres(const u_int32_t *dp, int v3)
1223 {
1224         int er;
1225
1226         dp = parsestatus(dp, &er);
1227         if (dp == NULL)
1228                 return(0);
1229         if (er)
1230                 return(1);
1231         if (v3 && !(dp = parse_post_op_attr(dp, vflag)))
1232                 return (0);
1233         putchar(' ');
1234         return (parsefn(dp) != NULL);
1235 }
1236
1237 static int
1238 parsestatfs(const u_int32_t *dp, int v3)
1239 {
1240         const struct nfs_statfs *sfsp;
1241         int er;
1242
1243         dp = parsestatus(dp, &er);
1244         if (dp == NULL)
1245                 return (0);
1246         if (!v3 && er)
1247                 return (1);
1248
1249         if (qflag)
1250                 return(1);
1251
1252         if (v3) {
1253                 if (vflag)
1254                         printf(" POST:");
1255                 if (!(dp = parse_post_op_attr(dp, vflag)))
1256                         return (0);
1257         }
1258
1259         TCHECK2(*dp, (v3 ? NFSX_V3STATFS : NFSX_V2STATFS));
1260
1261         sfsp = (const struct nfs_statfs *)dp;
1262
1263         if (v3) {
1264                 printf(" tbytes %" PRIu64 " fbytes %" PRIu64 " abytes %" PRIu64,
1265                         EXTRACT_64BITS((u_int32_t *)&sfsp->sf_tbytes),
1266                         EXTRACT_64BITS((u_int32_t *)&sfsp->sf_fbytes),
1267                         EXTRACT_64BITS((u_int32_t *)&sfsp->sf_abytes));
1268                 if (vflag) {
1269                         printf(" tfiles %" PRIu64 " ffiles %" PRIu64 " afiles %" PRIu64 " invar %u",
1270                                EXTRACT_64BITS((u_int32_t *)&sfsp->sf_tfiles),
1271                                EXTRACT_64BITS((u_int32_t *)&sfsp->sf_ffiles),
1272                                EXTRACT_64BITS((u_int32_t *)&sfsp->sf_afiles),
1273                                EXTRACT_32BITS(&sfsp->sf_invarsec));
1274                 }
1275         } else {
1276                 printf(" tsize %d bsize %d blocks %d bfree %d bavail %d",
1277                         EXTRACT_32BITS(&sfsp->sf_tsize),
1278                         EXTRACT_32BITS(&sfsp->sf_bsize),
1279                         EXTRACT_32BITS(&sfsp->sf_blocks),
1280                         EXTRACT_32BITS(&sfsp->sf_bfree),
1281                         EXTRACT_32BITS(&sfsp->sf_bavail));
1282         }
1283
1284         return (1);
1285 trunc:
1286         return (0);
1287 }
1288
1289 static int
1290 parserddires(const u_int32_t *dp)
1291 {
1292         int er;
1293
1294         dp = parsestatus(dp, &er);
1295         if (dp == NULL)
1296                 return (0);
1297         if (er)
1298                 return (1);
1299         if (qflag)
1300                 return (1);
1301
1302         TCHECK(dp[2]);
1303         printf(" offset %x size %d ",
1304                EXTRACT_32BITS(&dp[0]), EXTRACT_32BITS(&dp[1]));
1305         if (dp[2] != 0)
1306                 printf(" eof");
1307
1308         return (1);
1309 trunc:
1310         return (0);
1311 }
1312
1313 static const u_int32_t *
1314 parse_wcc_attr(const u_int32_t *dp)
1315 {
1316         printf(" sz %" PRIu64, EXTRACT_64BITS(&dp[0]));
1317         printf(" mtime %u.%06u ctime %u.%06u",
1318                EXTRACT_32BITS(&dp[2]), EXTRACT_32BITS(&dp[3]),
1319                EXTRACT_32BITS(&dp[4]), EXTRACT_32BITS(&dp[5]));
1320         return (dp + 6);
1321 }
1322
1323 /*
1324  * Pre operation attributes. Print only if vflag > 1.
1325  */
1326 static const u_int32_t *
1327 parse_pre_op_attr(const u_int32_t *dp, int verbose)
1328 {
1329         TCHECK(dp[0]);
1330         if (!EXTRACT_32BITS(&dp[0]))
1331                 return (dp + 1);
1332         dp++;
1333         TCHECK2(*dp, 24);
1334         if (verbose > 1) {
1335                 return parse_wcc_attr(dp);
1336         } else {
1337                 /* If not verbose enough, just skip over wcc_attr */
1338                 return (dp + 6);
1339         }
1340 trunc:
1341         return (NULL);
1342 }
1343
1344 /*
1345  * Post operation attributes are printed if vflag >= 1
1346  */
1347 static const u_int32_t *
1348 parse_post_op_attr(const u_int32_t *dp, int verbose)
1349 {
1350         TCHECK(dp[0]);
1351         if (!EXTRACT_32BITS(&dp[0]))
1352                 return (dp + 1);
1353         dp++;
1354         if (verbose) {
1355                 return parsefattr(dp, verbose, 1);
1356         } else
1357                 return (dp + (NFSX_V3FATTR / sizeof (u_int32_t)));
1358 trunc:
1359         return (NULL);
1360 }
1361
1362 static const u_int32_t *
1363 parse_wcc_data(const u_int32_t *dp, int verbose)
1364 {
1365         if (verbose > 1)
1366                 printf(" PRE:");
1367         if (!(dp = parse_pre_op_attr(dp, verbose)))
1368                 return (0);
1369
1370         if (verbose)
1371                 printf(" POST:");
1372         return parse_post_op_attr(dp, verbose);
1373 }
1374
1375 static const u_int32_t *
1376 parsecreateopres(const u_int32_t *dp, int verbose)
1377 {
1378         int er;
1379
1380         if (!(dp = parsestatus(dp, &er)))
1381                 return (0);
1382         if (er)
1383                 dp = parse_wcc_data(dp, verbose);
1384         else {
1385                 TCHECK(dp[0]);
1386                 if (!EXTRACT_32BITS(&dp[0]))
1387                         return (dp + 1);
1388                 dp++;
1389                 if (!(dp = parsefh(dp, 1)))
1390                         return (0);
1391                 if (verbose) {
1392                         if (!(dp = parse_post_op_attr(dp, verbose)))
1393                                 return (0);
1394                         if (vflag > 1) {
1395                                 printf(" dir attr:");
1396                                 dp = parse_wcc_data(dp, verbose);
1397                         }
1398                 }
1399         }
1400         return (dp);
1401 trunc:
1402         return (NULL);
1403 }
1404
1405 static int
1406 parsewccres(const u_int32_t *dp, int verbose)
1407 {
1408         int er;
1409
1410         if (!(dp = parsestatus(dp, &er)))
1411                 return (0);
1412         return parse_wcc_data(dp, verbose) != 0;
1413 }
1414
1415 static const u_int32_t *
1416 parsev3rddirres(const u_int32_t *dp, int verbose)
1417 {
1418         int er;
1419
1420         if (!(dp = parsestatus(dp, &er)))
1421                 return (0);
1422         if (vflag)
1423                 printf(" POST:");
1424         if (!(dp = parse_post_op_attr(dp, verbose)))
1425                 return (0);
1426         if (er)
1427                 return dp;
1428         if (vflag) {
1429                 TCHECK(dp[1]);
1430                 printf(" verf %08x%08x", dp[0], dp[1]);
1431                 dp += 2;
1432         }
1433         return dp;
1434 trunc:
1435         return (NULL);
1436 }
1437
1438 static int
1439 parsefsinfo(const u_int32_t *dp)
1440 {
1441         struct nfsv3_fsinfo *sfp;
1442         int er;
1443
1444         if (!(dp = parsestatus(dp, &er)))
1445                 return (0);
1446         if (vflag)
1447                 printf(" POST:");
1448         if (!(dp = parse_post_op_attr(dp, vflag)))
1449                 return (0);
1450         if (er)
1451                 return (1);
1452
1453         sfp = (struct nfsv3_fsinfo *)dp;
1454         TCHECK(*sfp);
1455         printf(" rtmax %u rtpref %u wtmax %u wtpref %u dtpref %u",
1456                EXTRACT_32BITS(&sfp->fs_rtmax),
1457                EXTRACT_32BITS(&sfp->fs_rtpref),
1458                EXTRACT_32BITS(&sfp->fs_wtmax),
1459                EXTRACT_32BITS(&sfp->fs_wtpref),
1460                EXTRACT_32BITS(&sfp->fs_dtpref));
1461         if (vflag) {
1462                 printf(" rtmult %u wtmult %u maxfsz %" PRIu64,
1463                        EXTRACT_32BITS(&sfp->fs_rtmult),
1464                        EXTRACT_32BITS(&sfp->fs_wtmult),
1465                        EXTRACT_64BITS((u_int32_t *)&sfp->fs_maxfilesize));
1466                 printf(" delta %u.%06u ",
1467                        EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_sec),
1468                        EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_nsec));
1469         }
1470         return (1);
1471 trunc:
1472         return (0);
1473 }
1474
1475 static int
1476 parsepathconf(const u_int32_t *dp)
1477 {
1478         int er;
1479         struct nfsv3_pathconf *spp;
1480
1481         if (!(dp = parsestatus(dp, &er)))
1482                 return (0);
1483         if (vflag)
1484                 printf(" POST:");
1485         if (!(dp = parse_post_op_attr(dp, vflag)))
1486                 return (0);
1487         if (er)
1488                 return (1);
1489
1490         spp = (struct nfsv3_pathconf *)dp;
1491         TCHECK(*spp);
1492
1493         printf(" linkmax %u namemax %u %s %s %s %s",
1494                EXTRACT_32BITS(&spp->pc_linkmax),
1495                EXTRACT_32BITS(&spp->pc_namemax),
1496                EXTRACT_32BITS(&spp->pc_notrunc) ? "notrunc" : "",
1497                EXTRACT_32BITS(&spp->pc_chownrestricted) ? "chownres" : "",
1498                EXTRACT_32BITS(&spp->pc_caseinsensitive) ? "igncase" : "",
1499                EXTRACT_32BITS(&spp->pc_casepreserving) ? "keepcase" : "");
1500         return (1);
1501 trunc:
1502         return (0);
1503 }
1504
1505 static void
1506 interp_reply(const struct sunrpc_msg *rp, u_int32_t proc, u_int32_t vers, int length)
1507 {
1508         register const u_int32_t *dp;
1509         register int v3;
1510         int er;
1511
1512         v3 = (vers == NFS_VER3);
1513
1514         if (!v3 && proc < NFS_NPROCS)
1515                 proc = nfsv3_procid[proc];
1516
1517         switch (proc) {
1518
1519         case NFSPROC_NOOP:
1520                 printf(" nop");
1521                 return;
1522
1523         case NFSPROC_NULL:
1524                 printf(" null");
1525                 return;
1526
1527         case NFSPROC_GETATTR:
1528                 printf(" getattr");
1529                 dp = parserep(rp, length);
1530                 if (dp != NULL && parseattrstat(dp, !qflag, v3) != 0)
1531                         return;
1532                 break;
1533
1534         case NFSPROC_SETATTR:
1535                 printf(" setattr");
1536                 if (!(dp = parserep(rp, length)))
1537                         return;
1538                 if (v3) {
1539                         if (parsewccres(dp, vflag))
1540                                 return;
1541                 } else {
1542                         if (parseattrstat(dp, !qflag, 0) != 0)
1543                                 return;
1544                 }
1545                 break;
1546
1547         case NFSPROC_LOOKUP:
1548                 printf(" lookup");
1549                 if (!(dp = parserep(rp, length)))
1550                         break;
1551                 if (v3) {
1552                         if (!(dp = parsestatus(dp, &er)))
1553                                 break;
1554                         if (er) {
1555                                 if (vflag > 1) {
1556                                         printf(" post dattr:");
1557                                         dp = parse_post_op_attr(dp, vflag);
1558                                 }
1559                         } else {
1560                                 if (!(dp = parsefh(dp, v3)))
1561                                         break;
1562                                 if ((dp = parse_post_op_attr(dp, vflag)) &&
1563                                     vflag > 1) {
1564                                         printf(" post dattr:");
1565                                         dp = parse_post_op_attr(dp, vflag);
1566                                 }
1567                         }
1568                         if (dp)
1569                                 return;
1570                 } else {
1571                         if (parsediropres(dp) != 0)
1572                                 return;
1573                 }
1574                 break;
1575
1576         case NFSPROC_ACCESS:
1577                 printf(" access");
1578                 if (!(dp = parserep(rp, length)))
1579                         break;
1580                 if (!(dp = parsestatus(dp, &er)))
1581                         break;
1582                 if (vflag)
1583                         printf(" attr:");
1584                 if (!(dp = parse_post_op_attr(dp, vflag)))
1585                         break;
1586                 if (!er)
1587                         printf(" c %04x", EXTRACT_32BITS(&dp[0]));
1588                 return;
1589
1590         case NFSPROC_READLINK:
1591                 printf(" readlink");
1592                 dp = parserep(rp, length);
1593                 if (dp != NULL && parselinkres(dp, v3) != 0)
1594                         return;
1595                 break;
1596
1597         case NFSPROC_READ:
1598                 printf(" read");
1599                 if (!(dp = parserep(rp, length)))
1600                         break;
1601                 if (v3) {
1602                         if (!(dp = parsestatus(dp, &er)))
1603                                 break;
1604                         if (!(dp = parse_post_op_attr(dp, vflag)))
1605                                 break;
1606                         if (er)
1607                                 return;
1608                         if (vflag) {
1609                                 TCHECK(dp[1]);
1610                                 printf(" %u bytes", EXTRACT_32BITS(&dp[0]));
1611                                 if (EXTRACT_32BITS(&dp[1]))
1612                                         printf(" EOF");
1613                         }
1614                         return;
1615                 } else {
1616                         if (parseattrstat(dp, vflag, 0) != 0)
1617                                 return;
1618                 }
1619                 break;
1620
1621         case NFSPROC_WRITE:
1622                 printf(" write");
1623                 if (!(dp = parserep(rp, length)))
1624                         break;
1625                 if (v3) {
1626                         if (!(dp = parsestatus(dp, &er)))
1627                                 break;
1628                         if (!(dp = parse_wcc_data(dp, vflag)))
1629                                 break;
1630                         if (er)
1631                                 return;
1632                         if (vflag) {
1633                                 TCHECK(dp[0]);
1634                                 printf(" %u bytes", EXTRACT_32BITS(&dp[0]));
1635                                 if (vflag > 1) {
1636                                         TCHECK(dp[1]);
1637                                         printf(" <%s>",
1638                                                 tok2str(nfsv3_writemodes,
1639                                                         NULL, EXTRACT_32BITS(&dp[1])));
1640                                 }
1641                                 return;
1642                         }
1643                 } else {
1644                         if (parseattrstat(dp, vflag, v3) != 0)
1645                                 return;
1646                 }
1647                 break;
1648
1649         case NFSPROC_CREATE:
1650                 printf(" create");
1651                 if (!(dp = parserep(rp, length)))
1652                         break;
1653                 if (v3) {
1654                         if (parsecreateopres(dp, vflag) != 0)
1655                                 return;
1656                 } else {
1657                         if (parsediropres(dp) != 0)
1658                                 return;
1659                 }
1660                 break;
1661
1662         case NFSPROC_MKDIR:
1663                 printf(" mkdir");
1664                 if (!(dp = parserep(rp, length)))
1665                         break;
1666                 if (v3) {
1667                         if (parsecreateopres(dp, vflag) != 0)
1668                                 return;
1669                 } else {
1670                         if (parsediropres(dp) != 0)
1671                                 return;
1672                 }
1673                 break;
1674
1675         case NFSPROC_SYMLINK:
1676                 printf(" symlink");
1677                 if (!(dp = parserep(rp, length)))
1678                         break;
1679                 if (v3) {
1680                         if (parsecreateopres(dp, vflag) != 0)
1681                                 return;
1682                 } else {
1683                         if (parsestatus(dp, &er) != 0)
1684                                 return;
1685                 }
1686                 break;
1687
1688         case NFSPROC_MKNOD:
1689                 printf(" mknod");
1690                 if (!(dp = parserep(rp, length)))
1691                         break;
1692                 if (parsecreateopres(dp, vflag) != 0)
1693                         return;
1694                 break;
1695
1696         case NFSPROC_REMOVE:
1697                 printf(" remove");
1698                 if (!(dp = parserep(rp, length)))
1699                         break;
1700                 if (v3) {
1701                         if (parsewccres(dp, vflag))
1702                                 return;
1703                 } else {
1704                         if (parsestatus(dp, &er) != 0)
1705                                 return;
1706                 }
1707                 break;
1708
1709         case NFSPROC_RMDIR:
1710                 printf(" rmdir");
1711                 if (!(dp = parserep(rp, length)))
1712                         break;
1713                 if (v3) {
1714                         if (parsewccres(dp, vflag))
1715                                 return;
1716                 } else {
1717                         if (parsestatus(dp, &er) != 0)
1718                                 return;
1719                 }
1720                 break;
1721
1722         case NFSPROC_RENAME:
1723                 printf(" rename");
1724                 if (!(dp = parserep(rp, length)))
1725                         break;
1726                 if (v3) {
1727                         if (!(dp = parsestatus(dp, &er)))
1728                                 break;
1729                         if (vflag) {
1730                                 printf(" from:");
1731                                 if (!(dp = parse_wcc_data(dp, vflag)))
1732                                         break;
1733                                 printf(" to:");
1734                                 if (!(dp = parse_wcc_data(dp, vflag)))
1735                                         break;
1736                         }
1737                         return;
1738                 } else {
1739                         if (parsestatus(dp, &er) != 0)
1740                                 return;
1741                 }
1742                 break;
1743
1744         case NFSPROC_LINK:
1745                 printf(" link");
1746                 if (!(dp = parserep(rp, length)))
1747                         break;
1748                 if (v3) {
1749                         if (!(dp = parsestatus(dp, &er)))
1750                                 break;
1751                         if (vflag) {
1752                                 printf(" file POST:");
1753                                 if (!(dp = parse_post_op_attr(dp, vflag)))
1754                                         break;
1755                                 printf(" dir:");
1756                                 if (!(dp = parse_wcc_data(dp, vflag)))
1757                                         break;
1758                                 return;
1759                         }
1760                 } else {
1761                         if (parsestatus(dp, &er) != 0)
1762                                 return;
1763                 }
1764                 break;
1765
1766         case NFSPROC_READDIR:
1767                 printf(" readdir");
1768                 if (!(dp = parserep(rp, length)))
1769                         break;
1770                 if (v3) {
1771                         if (parsev3rddirres(dp, vflag))
1772                                 return;
1773                 } else {
1774                         if (parserddires(dp) != 0)
1775                                 return;
1776                 }
1777                 break;
1778
1779         case NFSPROC_READDIRPLUS:
1780                 printf(" readdirplus");
1781                 if (!(dp = parserep(rp, length)))
1782                         break;
1783                 if (parsev3rddirres(dp, vflag))
1784                         return;
1785                 break;
1786
1787         case NFSPROC_FSSTAT:
1788                 printf(" fsstat");
1789                 dp = parserep(rp, length);
1790                 if (dp != NULL && parsestatfs(dp, v3) != 0)
1791                         return;
1792                 break;
1793
1794         case NFSPROC_FSINFO:
1795                 printf(" fsinfo");
1796                 dp = parserep(rp, length);
1797                 if (dp != NULL && parsefsinfo(dp) != 0)
1798                         return;
1799                 break;
1800
1801         case NFSPROC_PATHCONF:
1802                 printf(" pathconf");
1803                 dp = parserep(rp, length);
1804                 if (dp != NULL && parsepathconf(dp) != 0)
1805                         return;
1806                 break;
1807
1808         case NFSPROC_COMMIT:
1809                 printf(" commit");
1810                 dp = parserep(rp, length);
1811                 if (dp != NULL && parsewccres(dp, vflag) != 0)
1812                         return;
1813                 break;
1814
1815         default:
1816                 printf(" proc-%u", proc);
1817                 return;
1818         }
1819 trunc:
1820         if (!nfserr)
1821                 fputs(" [|nfs]", stdout);
1822 }