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