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