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