Home | History | Annotate | Download | only in snoop
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright (c) 1991, 1999, 2001 by Sun Microsystems, Inc.
     24  * All rights reserved.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <ctype.h>
     30 #include <sys/types.h>
     31 #include <sys/errno.h>
     32 #include <setjmp.h>
     33 #include <string.h>
     34 
     35 #include <netinet/in.h>
     36 #include <rpc/types.h>
     37 #include <rpc/rpc.h>
     38 #include <rpc/xdr.h>
     39 #include <rpc/auth.h>
     40 #include <rpc/clnt.h>
     41 #include <rpc/rpc_msg.h>
     42 #include <rpcsvc/nis.h>
     43 #include <rpcsvc/nis_callback.h>
     44 
     45 #include "snoop.h"
     46 #include "nis_clnt.h"
     47 
     48 extern char *dlc_header;
     49 extern jmp_buf xdr_err;
     50 
     51 /*
     52  * Number of spaces for each level of indentation.  Since this value is
     53  *   assumed in most of the strings below, defining it is pretty quixotic.
     54  */
     55 #define	INDENT_SPACES	4
     56 
     57 /*
     58  * ==== Old (pre Sep '91) format for public keys in NIS+ directories, now
     59  *	removed from the header files.  Should be removed from snoop once
     60  *	we're sure we won't see old-style packets
     61  */
     62 #ifndef SZ_PKEY
     63 #define	SZ_PKEY 64
     64 #endif /* SZ_PKEY */
     65 
     66 /*
     67  * ==== New (Aug '91) NIS+ remote procedure, which hasn't made into our
     68  *	header files yet.  When it does, nuke this stuff.
     69  */
     70 #ifndef	NIS_UPDKEYS
     71 #define	NIS_UPDKEYS	24
     72 #endif	/* NIS_UPDKEYS */
     73 
     74 /*
     75  * ==== Constants for public keys.  We should use NIS_MAXKEYLEN and the
     76  *	key-type constants from the NIS+ header files, but at present (Aug '91)
     77  *	we're ahead of the header files.
     78  */
     79 #define	KEYLEN_FIRST	0
     80 #define	KEYLEN_LIMIT	1024
     81 #define	KEYTYPE_FIRST	0
     82 #define	KEYTYPE_LIMIT	3
     83 
     84 
     85 static char *procnames_short[] = {
     86 	"Null",		/*  0 */
     87 	"Lookup",	/*  1 */
     88 	"Add",		/*  2 */
     89 	"Modify",	/*  3 */
     90 	"Remove",	/*  4 */
     91 	"IBlist",	/*  5 */
     92 	"IBadd",	/*  6 */
     93 	"IBmodify",	/*  7 */
     94 	"IBremove",	/*  8 */
     95 	"IBfirst",	/*  9 */
     96 	"IBnext",	/*  10 */
     97 	"** Unused 11",	/*  11 */
     98 	"FindDir",	/*  12 */
     99 	"** Unused 13",	/*  13 */
    100 	"Status",	/*  14 */
    101 	"DumpLog",	/*  15 */
    102 	"Dump",		/*  16 */
    103 	"Callback",	/*  17 */
    104 	"CheckpointTime",	/*  18 */
    105 	"Checkpoint",	/*  19 */
    106 	"Ping",		/*  20 */
    107 	"ServerState",	/*  21 */
    108 	"MakeDir",	/*  22 */
    109 	"RemoveDir",	/*  23 */
    110 	"UpdateKeys",	/*  24 */
    111 };
    112 
    113 static char *procnames_long[] = {
    114 	"Null procedure",		/*  0 */
    115 	"Lookup",			/*  1 */
    116 	"Add",				/*  2 */
    117 	"Modify",			/*  3 */
    118 	"Remove",			/*  4 */
    119 	"List IBase",			/*  5 */
    120 	"Add to IBase",			/*  6 */
    121 	"Modify IBase",			/*  7 */
    122 	"Remove from IBase",		/*  8 */
    123 	"IBase First Entry",		/*  9 */
    124 	"IBase Next Entry",		/*  10 */
    125 	"** 11 (Unused) **",		/*  11 */
    126 	"Find Directory",		/*  12 */
    127 	"** 13 (Unused) **",		/*  13 */
    128 	"Get/Reset Statistics",		/*  14 */
    129 	"Dump Directory Log",		/*  15 */
    130 	"Dump Directory Contents",	/*  16 */
    131 	"Check Callback Thread",	/*  17 */
    132 	"Get Checkpoint Time",		/*  18 */
    133 	"Establish Checkpoint",		/*  19 */
    134 	"Ping Replicas",		/*  20 */
    135 	"Change Server State",		/*  21 */
    136 	"Make Directory",		/*  22 */
    137 	"Remove Directory",		/*  23 */
    138 	"Update Public Keys",		/*  24 */
    139 };
    140 
    141 #define	MAXPROC	24
    142 
    143 static void detail_bool(void);
    144 static void detail_callback(void);
    145 static void detail_cback_data(void);
    146 static void detail_cookie(void);
    147 static void detail_cp_result(void);
    148 static void detail_cptime(void);
    149 static void detail_dump_args(void);
    150 static void detail_ib_request(void);
    151 static void detail_fd_args(void);
    152 static void detail_fd_result(void);
    153 static void detail_log_entry(void);
    154 static void detail_log_result(void);
    155 static void detail_nis_attrs(int);
    156 static void detail_nis_error(void);
    157 static void detail_nis_name(void);
    158 static void detail_nis_result(void);
    159 static void detail_nis_taglist(void);
    160 static void detail_ns_request(void);
    161 static void detail_ping_args(void);
    162 static int is_printable(char *, unsigned int);
    163 static void sum_bool(char *);
    164 static void sum_callback(char *);
    165 static void sum_cback_data(char *);
    166 static void sum_cp_result(char *);
    167 static void sum_cptime(char *);
    168 static void sum_dump_args(char *);
    169 static void sum_ib_request(char *);
    170 static void sum_fd_args(char *);
    171 static void sum_fd_result(char *);
    172 static void sum_log_result(char *);
    173 static void sum_nis_error(char *);
    174 static void sum_nis_name(char *);
    175 static void sum_nis_result(char *);
    176 static void sum_nis_taglist(char *);
    177 static void sum_ns_request(char *);
    178 static void sum_ping_args(char *);
    179 
    180 void
    181 interpret_nisplus(flags, type, xid, vers, proc, data, len)
    182 	int flags, type, xid, vers, proc;
    183 	char *data;
    184 	int len;
    185 {
    186 	char *line;
    187 
    188 	if (proc < 0 || proc > MAXPROC)
    189 		return;
    190 
    191 	if (flags & F_SUM) {
    192 		if (setjmp(xdr_err)) {
    193 			return;
    194 		}
    195 
    196 		line = get_sum_line();
    197 
    198 		if (type == CALL) {
    199 			(void) sprintf(line,
    200 				"NIS+ C %s",
    201 				procnames_short[proc]);
    202 			line += strlen(line);
    203 
    204 			switch (proc) {
    205 			case NIS_LOOKUP:
    206 			case NIS_ADD:
    207 			case NIS_MODIFY:
    208 			case NIS_REMOVE:
    209 				sum_ns_request(line);
    210 				break;
    211 			case NIS_IBLIST:
    212 			case NIS_IBADD:
    213 			case NIS_IBMODIFY:
    214 			case NIS_IBREMOVE:
    215 			case NIS_IBFIRST:
    216 			case NIS_IBNEXT:
    217 				sum_ib_request(line);
    218 				break;
    219 			case NIS_FINDDIRECTORY:
    220 				sum_fd_args(line);
    221 				break;
    222 			case NIS_STATUS:
    223 			case NIS_SERVSTATE:
    224 				sum_nis_taglist(line);
    225 				break;
    226 			case NIS_DUMPLOG:
    227 			case NIS_DUMP:
    228 				sum_dump_args(line);
    229 				break;
    230 			case NIS_CALLBACK:
    231 				sum_callback(line);
    232 				break;
    233 			case NIS_PING:
    234 				sum_ping_args(line);
    235 				break;
    236 			case NIS_CPTIME:
    237 			case NIS_CHECKPOINT:
    238 			case NIS_MKDIR:
    239 			case NIS_RMDIR:
    240 			case NIS_UPDKEYS:
    241 				sum_nis_name(line);
    242 				break;
    243 			default:
    244 				/* === mutter about bogus procnums? */
    245 				break;
    246 			}
    247 
    248 			check_retransmit(line, xid);
    249 		} else {
    250 			(void) sprintf(line, "NIS+ R %s ",
    251 				procnames_short[proc]);
    252 			line += strlen(line);
    253 			switch (proc) {
    254 			case NIS_LOOKUP:
    255 			case NIS_ADD:
    256 			case NIS_MODIFY:
    257 			case NIS_REMOVE:
    258 			case NIS_IBLIST:
    259 			case NIS_IBADD:
    260 			case NIS_IBMODIFY:
    261 			case NIS_IBREMOVE:
    262 			case NIS_IBFIRST:
    263 			case NIS_IBNEXT:
    264 				sum_nis_result(line);
    265 				break;
    266 			case NIS_FINDDIRECTORY:
    267 				sum_fd_result(line);
    268 				break;
    269 			case NIS_STATUS:
    270 			case NIS_SERVSTATE:
    271 				sum_nis_taglist(line);
    272 				break;
    273 			case NIS_DUMPLOG:
    274 			case NIS_DUMP:
    275 				sum_log_result(line);
    276 				break;
    277 			case NIS_CALLBACK:
    278 				sum_bool(line);
    279 				break;
    280 			case NIS_CPTIME:
    281 				sum_cptime(line);
    282 				break;
    283 			case NIS_CHECKPOINT:
    284 				sum_cp_result(line);
    285 				break;
    286 			case NIS_PING:
    287 				break;
    288 			case NIS_MKDIR:
    289 			case NIS_RMDIR:
    290 			case NIS_UPDKEYS:
    291 				sum_nis_error(line);
    292 				break;
    293 			default:
    294 				/* === mutter about bogus procnums? */
    295 				break;
    296 			}
    297 		}
    298 	}
    299 
    300 	if (flags & F_DTAIL) {
    301 		show_header("NIS+:  ", "NIS+ Name Service", len);
    302 		show_space();
    303 		if (setjmp(xdr_err)) {
    304 			return;
    305 		}
    306 		(void) sprintf(get_line(0, 0),
    307 			"Proc = %d (%s)",
    308 			proc, procnames_long[proc]);
    309 
    310 		if (type == CALL) {
    311 			switch (proc) {
    312 			case NIS_LOOKUP:
    313 			case NIS_ADD:
    314 			case NIS_MODIFY:
    315 			case NIS_REMOVE:
    316 				detail_ns_request();
    317 				break;
    318 			case NIS_IBLIST:
    319 			case NIS_IBADD:
    320 			case NIS_IBMODIFY:
    321 			case NIS_IBREMOVE:
    322 			case NIS_IBFIRST:
    323 			case NIS_IBNEXT:
    324 				detail_ib_request();
    325 				break;
    326 			case NIS_FINDDIRECTORY:
    327 				detail_fd_args();
    328 				break;
    329 			case NIS_STATUS:
    330 			case NIS_SERVSTATE:
    331 				detail_nis_taglist();
    332 				break;
    333 			case NIS_DUMPLOG:
    334 			case NIS_DUMP:
    335 				detail_dump_args();
    336 				break;
    337 			case NIS_CALLBACK:
    338 				detail_callback();
    339 				break;
    340 			case NIS_PING:
    341 				detail_ping_args();
    342 				break;
    343 			case NIS_CPTIME:
    344 			case NIS_CHECKPOINT:
    345 			case NIS_MKDIR:
    346 			case NIS_RMDIR:
    347 			case NIS_UPDKEYS:
    348 				detail_nis_name();
    349 				break;
    350 			default:
    351 				/* === mutter about bogus procnums? */
    352 				break;
    353 			}
    354 		} else {
    355 			switch (proc) {
    356 			case NIS_LOOKUP:
    357 			case NIS_ADD:
    358 			case NIS_MODIFY:
    359 			case NIS_REMOVE:
    360 			case NIS_IBLIST:
    361 			case NIS_IBADD:
    362 			case NIS_IBMODIFY:
    363 			case NIS_IBREMOVE:
    364 			case NIS_IBFIRST:
    365 			case NIS_IBNEXT:
    366 				detail_nis_result();
    367 				break;
    368 			case NIS_FINDDIRECTORY:
    369 				detail_fd_result();
    370 				break;
    371 			case NIS_STATUS:
    372 			case NIS_SERVSTATE:
    373 				detail_nis_taglist();
    374 				break;
    375 			case NIS_DUMPLOG:
    376 			case NIS_DUMP:
    377 				detail_log_result();
    378 				break;
    379 			case NIS_CALLBACK:
    380 				detail_bool();
    381 				break;
    382 			case NIS_CPTIME:
    383 				detail_cptime();
    384 				break;
    385 			case NIS_CHECKPOINT:
    386 				detail_cp_result();
    387 				break;
    388 			case NIS_PING:
    389 				break;
    390 			case NIS_MKDIR:
    391 			case NIS_RMDIR:
    392 			case NIS_UPDKEYS:
    393 				detail_nis_error();
    394 				break;
    395 			default:
    396 				/* === mutter about bogus procnums? */
    397 				break;
    398 			}
    399 		}
    400 
    401 		show_trailer();
    402 	}
    403 }
    404 
    405 static char *cbnames_short[] = {
    406 	"Null",		/*  0 */
    407 	"Receive",	/*  1 */
    408 	"Finish",	/*  2 */
    409 	"Error",	/*  3 */
    410 };
    411 
    412 static char *cbnames_long[] = {
    413 	"Null procedure",		/*  0 */
    414 	"Receive Callback Data",	/*  1 */
    415 	"Finish Callback",		/*  2 */
    416 	"Callback Error",		/*  3 */
    417 };
    418 
    419 void
    420 interpret_nisp_cb(flags, type, xid, vers, proc, data, len)
    421 	int flags, type, xid, vers, proc;
    422 	char *data;
    423 	int len;
    424 {
    425 	char *line;
    426 
    427 	if (flags & F_SUM) {
    428 		if (setjmp(xdr_err)) {
    429 			return;
    430 		}
    431 
    432 		line = get_sum_line();
    433 
    434 		if (type == CALL) {
    435 			(void) sprintf(line,
    436 				"NIS+ Callback C %s",
    437 				cbnames_short[proc]);
    438 			line += strlen(line);
    439 
    440 			switch (proc) {
    441 			    case CBPROC_RECEIVE:
    442 				sum_cback_data(line);
    443 				break;
    444 			    case CBPROC_FINISH:
    445 				/* void: nothing to do */
    446 				break;
    447 			    case CBPROC_ERROR:
    448 				sum_nis_error(line);
    449 				break;
    450 			    default:
    451 				/* === mutter about bogus procnums? */
    452 				break;
    453 			}
    454 
    455 			check_retransmit(line, xid);
    456 		} else {
    457 			(void) sprintf(line, "NIS+ Callback R %s ",
    458 				cbnames_short[proc]);
    459 			line += strlen(line);
    460 			switch (proc) {
    461 			    case CBPROC_RECEIVE:
    462 				sum_bool(line);
    463 				break;
    464 			    case CBPROC_FINISH:
    465 			    case CBPROC_ERROR:
    466 				/* void: nothing to do */
    467 				break;
    468 			    default:
    469 				/* === mutter about bogus procnums? */
    470 				break;
    471 			}
    472 		}
    473 	}
    474 
    475 	if (flags & F_DTAIL) {
    476 		show_header("NIS+ CB:  ", "NIS+ Name Service Callback", len);
    477 		show_space();
    478 		if (setjmp(xdr_err)) {
    479 			return;
    480 		}
    481 		(void) sprintf(get_line(0, 0),
    482 			"Proc = %d (%s)",
    483 			proc, cbnames_long[proc]);
    484 
    485 		if (type == CALL) {
    486 			switch (proc) {
    487 			    case CBPROC_RECEIVE:
    488 				detail_cback_data();
    489 				break;
    490 			    case CBPROC_FINISH:
    491 				/* void: nothing to do */
    492 				break;
    493 			    case CBPROC_ERROR:
    494 				detail_nis_error();
    495 				break;
    496 			    default:
    497 				/* === mutter about bogus procnums? */
    498 				break;
    499 			}
    500 		} else {
    501 			switch (proc) {
    502 			    case CBPROC_RECEIVE:
    503 				detail_bool();
    504 				break;
    505 			    case CBPROC_FINISH:
    506 			    case CBPROC_ERROR:
    507 				/* void: nothing to do */
    508 				break;
    509 			    default:
    510 				/* === mutter about bogus procnums? */
    511 				break;
    512 			}
    513 		}
    514 
    515 		show_trailer();
    516 	}
    517 }
    518 
    519 /*
    520  * stringof_XXX() routines -- return printable representations of various
    521  *	numeric values.  Would be nice if we could get this from the NIS+
    522  *	library instead of reinventing them.
    523  * N.B.  Mucho use of pointer-to-static result types (ugh); don't expect the
    524  *	return value to stay good for long.
    525  */
    526 
    527 #ifndef RIGHTS_FORMAT
    528 #define	RIGHTS_FORMAT	1
    529 #endif	/* RIGHTS_FORMAT */
    530 
    531 static struct {
    532 	int shift;
    533 	char *name;
    534 } rightsclasses[] = {
    535 #if	RIGHTS_FORMAT == 1
    536 	24,	"",
    537 	16,	"",
    538 	8,	"",
    539 	0,	"",
    540 #elif	RIGHTS_FORMAT == 2
    541 	16,	"o:",
    542 	8,	"g:",
    543 	0,	"w:",
    544 	24,	"u:",
    545 #else	/* RIGHTS_FORMAT == 3 */
    546 	16,	"owner:",
    547 	8,	"group:",
    548 	0,	"world:",
    549 	24,	"unauth:",
    550 #endif	/* RIGHTS_FORMAT */
    551 };
    552 
    553 char *
    554 stringof_rights(rights)
    555 	unsigned rights;
    556 {
    557 	static char rightsbuf[100];
    558 	char *p;
    559 	int i;
    560 
    561 	sprintf(rightsbuf, "%08x (", rights);
    562 	p = rightsbuf + strlen(rightsbuf);
    563 
    564 	for (i = 0; i < 4; i++) {
    565 		int shift = rightsclasses[i].shift;
    566 		strcpy(p, rightsclasses[i].name);
    567 		p += strlen(p);
    568 		*p++ = (rights >> shift) & NIS_READ_ACC    ? 'r' : '-';
    569 		*p++ = (rights >> shift) & NIS_MODIFY_ACC  ? 'm' : '-';
    570 		*p++ = (rights >> shift) & NIS_CREATE_ACC  ? 'c' : '-';
    571 		*p++ = (rights >> shift) & NIS_DESTROY_ACC ? 'd' : '-';
    572 		*p++ = ' ';
    573 		rights &= ~((NIS_READ_ACC   | NIS_MODIFY_ACC  |
    574 				NIS_CREATE_ACC | NIS_DESTROY_ACC) << shift);
    575 	}
    576 	if (rights == 0) {
    577 		p[-1] = ')';		/* Nuke that last space */
    578 		p[ 0] = 0;
    579 	} else {
    580 		sprintf(p, "+ Unknown bits: %08x **)", rights);
    581 	}
    582 	return (rightsbuf);
    583 }
    584 
    585 char *
    586 stringof_ib_flags(flags)
    587 	unsigned flags;
    588 {
    589 	static char flagsbuf[120];
    590 
    591 	if (flags == 0) {
    592 		sprintf(flagsbuf, "%08x", flags);
    593 		return (flagsbuf);
    594 	}
    595 	sprintf(flagsbuf,
    596 		"%08x (%s%s%s%s",
    597 		flags,
    598 		flags & MOD_SAMEOBJ		? "MOD_SAMEOBJ, "	: "",
    599 		flags & REM_MULTIPLE		? "REM_MULTIPLE, "	: "",
    600 		flags & ADD_OVERWRITE		? "ADD_OVERWRITE, "	: "",
    601 		flags & RETURN_RESULT		? "RETURN_RESULT, "	: "");
    602 	flags &= ~(MOD_SAMEOBJ | REM_MULTIPLE | ADD_OVERWRITE | RETURN_RESULT);
    603 	if (flags != 0) {
    604 		sprintf(flagsbuf + strlen(flagsbuf),
    605 			"** Unknown bits %08x **, ", flags);
    606 	}
    607 	/* Replace the trailing ", " with ")" */
    608 	sprintf(flagsbuf + strlen(flagsbuf) - 2, ")");
    609 	return (flagsbuf);
    610 }
    611 
    612 char *
    613 stringof_colflags(flags)
    614 	unsigned flags;
    615 {
    616 	static char flagsbuf[120];
    617 
    618 	if (flags == 0) {
    619 		sprintf(flagsbuf, "%08x", flags);
    620 		return (flagsbuf);
    621 	}
    622 	sprintf(flagsbuf,
    623 		"%08x (%s%s%s%s%s",
    624 		flags,
    625 		flags & TA_CASE		? "Case-insensitive, "	: "",
    626 		flags & TA_SEARCHABLE	? "Searchable, "	: "",
    627 		flags & TA_XDR		? "XDR Encoded, "	: "",
    628 		flags & TA_CRYPT	? "Encrypted, "		: "",
    629 		flags & TA_BINARY	? "Binary, " 		: "");
    630 	flags &= ~(TA_CASE | TA_SEARCHABLE | TA_XDR | TA_CRYPT | TA_BINARY);
    631 	if (flags != 0) {
    632 		sprintf(flagsbuf + strlen(flagsbuf),
    633 			"** Unknown bits %08x **, ", flags);
    634 	}
    635 	/* Replace the trailing ", " with ")" */
    636 	sprintf(flagsbuf + strlen(flagsbuf) - 2, ")");
    637 	return (flagsbuf);
    638 }
    639 
    640 char *
    641 stringof_entryflags(flags)
    642 	unsigned flags;
    643 {
    644 	static char flagsbuf[100];
    645 
    646 	if (flags == 0) {
    647 		sprintf(flagsbuf, "%08x", flags);
    648 		return (flagsbuf);
    649 	}
    650 	sprintf(flagsbuf,
    651 		"%08x (%s%s%s%s",
    652 		flags,
    653 		flags & EN_MODIFIED	? "Modified, "		: "",
    654 		flags & EN_XDR		? "XDR Encoded, "	: "",
    655 		flags & EN_CRYPT	? "Encrypted, "		: "",
    656 		flags & EN_BINARY	? "Binary, " 		: "");
    657 	flags &= ~(EN_MODIFIED | EN_XDR | EN_CRYPT | EN_BINARY);
    658 	if (flags != 0) {
    659 		sprintf(flagsbuf + strlen(flagsbuf),
    660 			"** Unknown bits %08x **, ", flags);
    661 	}
    662 	/* Replace the trailing ", " with ")" */
    663 	sprintf(flagsbuf + strlen(flagsbuf) - 2, ")");
    664 	return (flagsbuf);
    665 }
    666 
    667 char *
    668 stringof_groupflags(flags)
    669 	unsigned flags;
    670 {
    671 	static char flagsbuf[80];
    672 
    673 	if (flags == 0) {
    674 		sprintf(flagsbuf, "%08x", flags);
    675 		return (flagsbuf);
    676 	}
    677 	sprintf(flagsbuf,
    678 		"%08x (%s%s%s",
    679 		flags,
    680 		flags & NEGMEM_GROUPS	? "Negative, "		: "",
    681 		flags & RECURS_GROUPS	? "Recursive, "		: "",
    682 		flags & IMPMEM_GROUPS	? "Implicit, "		: "");
    683 	flags &= ~(NEGMEM_GROUPS | RECURS_GROUPS | IMPMEM_GROUPS);
    684 	if (flags != 0) {
    685 		sprintf(flagsbuf + strlen(flagsbuf),
    686 			"** Unknown bits %08x **, ", flags);
    687 	}
    688 	/* Replace the trailing ", " with ")" */
    689 	sprintf(flagsbuf + strlen(flagsbuf) - 2, ")");
    690 	return (flagsbuf);
    691 }
    692 
    693 char *
    694 stringof_tag(ttype)
    695 	int ttype;
    696 {
    697 	switch (ttype) {
    698 	    case TAG_DEBUG:	return ("DEBUG");
    699 	    case TAG_STATS:	return ("STATS");
    700 	    case TAG_GCACHE:	return ("GCACHE");
    701 	    case TAG_DCACHE:	return ("DCACHE");
    702 	    case TAG_OCACHE:	return ("OCACHE");
    703 	    case TAG_SECURE:	return ("SECURE");
    704 
    705 #ifdef undef
    706 /*
    707  * Old tags, removed September '91 (some of the tag-numbers have been
    708  *   reassigned, just to make things completely confusing).
    709  */
    710 	    case TAG_LOOKUPS:	return ("LOOKUPS";
    711 	    case TAG_S_LOOKUPS:	return ("S_LOOKUPS";
    712 	    case TAG_U_LOOKUPS:	return ("U_LOOKUPS";
    713 #endif /* undef */
    714 	    case TAG_OPSTATS:	return ("OPSTATS");
    715 	    case TAG_THREADS:	return ("THREADS");
    716 	    case TAG_UPDATES:	return ("UPDATES");
    717 	    case TAG_VISIBLE:	return ("VISIBLE");
    718 	    case TAG_S_DCACHE:	return ("S_DCACHE");
    719 	    case TAG_S_OCACHE:	return ("S_OCACHE");
    720 	    case TAG_S_GCACHE:	return ("S_GCACHE");
    721 	    case TAG_S_STORAGE:	return ("S_STORAGE");
    722 
    723 	    default:		return ("*Unknown*");
    724 	}
    725 }
    726 
    727 char *
    728 stringof_otype(otype)
    729 	int otype;
    730 {
    731 	static char buf[30];
    732 
    733 	switch (otype) {
    734 	    case NIS_BOGUS_OBJ:	return ("BOGUS (uninitialized?)");
    735 	    case NIS_NO_OBJ:	return ("NULL");
    736 	    case NIS_DIRECTORY_OBJ:	return ("DIRECTORY");
    737 	    case NIS_GROUP_OBJ:	return ("GROUP");
    738 	    case NIS_TABLE_OBJ:	return ("TABLE");
    739 	    case NIS_ENTRY_OBJ:	return ("ENTRY");
    740 	    case NIS_LINK_OBJ:	return ("LINK");
    741 	    case NIS_PRIVATE_OBJ:	return ("PRIVATE");
    742 	    default:		sprintf(buf, "** Unknown (%d) **", otype);
    743 				return (buf);
    744 	}
    745 }
    746 
    747 char *
    748 stringof_entry_t(et)
    749 	int et;
    750 {
    751 	static char buf[30];
    752 
    753 	switch (et) {
    754 	    case LOG_NOP:	return ("LOG_NOP");	/* === lowercase? */
    755 	    case ADD_NAME:	return ("ADD_NAME");
    756 	    case REM_NAME:	return ("REM_NAME");
    757 	    case MOD_NAME_OLD:	return ("MOD_NAME_OLD");
    758 	    case MOD_NAME_NEW:	return ("MOD_NAME_NEW");
    759 	    case ADD_IBASE:	return ("ADD_IBASE");
    760 	    case REM_IBASE:	return ("REM_IBASE");
    761 	    case MOD_IBASE:	return ("MOD_IBASE");
    762 	    case UPD_STAMP:	return ("UPD_STAMP");
    763 	    default:		sprintf(buf, "** Unknown (%d) **", et);
    764 				return (buf);
    765 	}
    766 }
    767 
    768 char *
    769 stringof_ktype(ktype)
    770 	unsigned ktype;
    771 {
    772 	static char buf[40];
    773 	char *p;
    774 
    775 	sprintf(buf, "%d ", ktype);
    776 	p = buf + strlen(buf);
    777 	switch (ktype) {
    778 	    case 0:	strcpy(p, "(None)");		break;
    779 	    case 1:	strcpy(p, "(Diffie-Hellman)");	break;
    780 	    case 2:	strcpy(p, "(RSA)");		break;
    781 	    case 4:	strcpy(p, "(Diffie-Hellman Ext)"); break;
    782 	    default:	strcpy(p, "(** Unknown **)");	break;
    783 	}
    784 	return (buf);
    785 }
    786 
    787 static void
    788 sumxdr_nis_name(line)
    789 	char *line;
    790 {
    791 	char buff[NIS_MAXNAMELEN + 1];
    792 
    793 	(void) sprintf(line, " \"%s\"", getxdr_string(buff, NIS_MAXNAMELEN));
    794 	/* ==== ? could/should truncate long names a la showxdr_string() ? */
    795 }
    796 
    797 static unsigned
    798 sumxdr_nis_error(line)
    799 	char *line;
    800 {
    801 	unsigned err = getxdr_u_long();
    802 	sprintf(line, "[%s]", nis_sperrno(err));
    803 	return (err);
    804 }
    805 
    806 static void
    807 sum_ns_request(line)
    808 	char *line;
    809 {
    810 	sumxdr_nis_name(line);
    811 	/* Leave the optional object for detailed listings */
    812 }
    813 
    814 int nchars_attrval = 32;
    815 
    816 static void
    817 sum_ib_request(line)
    818 	char *line;
    819 {
    820 	unsigned long nattrs;
    821 	unsigned long len;
    822 	int pos;
    823 	char *val;
    824 	char buff[NIS_MAXATTRNAME + 1];
    825 
    826 	sumxdr_nis_name(line);
    827 	line += strlen(line);
    828 	nattrs = getxdr_u_long();
    829 	switch (nattrs) {
    830 	    case 0:
    831 		/* === Could print "No attrs " */
    832 		break;
    833 	    case 1:
    834 		sprintf(line, " [%s = ", getxdr_string(buff, NIS_MAXATTRNAME));
    835 		line += strlen(line);
    836 		pos = getxdr_pos();
    837 		val = getxdr_bytes((uint_t *)&len);
    838 		if (is_printable(val, len)) {
    839 			if (len - 1 > nchars_attrval) {
    840 				sprintf(line, "(%d-character value)", len);
    841 			} else {
    842 				sprintf(line, "\"%s\"", val);
    843 			}
    844 		} else {
    845 			if ((len - 1) * 2 > nchars_attrval) {
    846 				sprintf(line, "(%d-byte value)", len);
    847 			} else {
    848 				/*
    849 				 * Too lazy to print hex ourselves; instead,
    850 				 *   back up and use snoop's hex-printer
    851 				 */
    852 				setxdr_pos(pos);
    853 				len = getxdr_u_long(); /* Yes, we did know it */
    854 				strcpy(line, getxdr_hex(len));
    855 			}
    856 		}
    857 		line += strlen(line);
    858 		*line++ = ']';
    859 		*line = 0;
    860 		break;
    861 	    default:
    862 		sprintf(line, " (Num attrs = %lu)", nattrs);
    863 		/* === Note that we haven't skipped the attrs */
    864 		break;
    865 	}
    866 	/* === Don't bother with flags, obj, cbhost, bufsize, cookie */
    867 }
    868 
    869 static void
    870 sum_fd_args(line)
    871 	char *line;
    872 {
    873 	sumxdr_nis_name(line);
    874 #ifdef LESS_TERSE
    875 	strcat(line, " for");
    876 	sumxdr_nis_name(line + strlen(line));
    877 #endif /* LESS_TERSE */
    878 }
    879 
    880 #define	MAXTAGLEN	1024		/* num chars of tag to display */
    881 
    882 static void
    883 sum_nis_taglist(line)
    884 	char *line;
    885 {
    886 	char buff[MAXTAGLEN + 1];
    887 	unsigned ntags;
    888 	unsigned ttype;
    889 
    890 	ntags = getxdr_u_long();
    891 	if (ntags == 0) {
    892 		strcpy(line, " [But no tags !?]");
    893 		return;
    894 	}
    895 	ttype = getxdr_u_long();
    896 	sprintf(line,
    897 		" %s=\"%s\"", stringof_tag(ttype),
    898 		getxdr_string(buff, MAXTAGLEN));
    899 	if (ntags > 1) {
    900 		sprintf(line + strlen(line), " [and %lu more tags]", ntags - 1);
    901 	}
    902 }
    903 
    904 static void
    905 sum_dump_args(line)
    906 	char *line;
    907 {
    908 	sumxdr_nis_name(line);
    909 	strcat(line, " from ");
    910 	strcat(line, getxdr_time());
    911 }
    912 
    913 /*ARGSUSED*/
    914 static void
    915 sum_callback(line)	/* i.e. the netobj argument to NIS_CALLBACK */
    916 	char *line;
    917 {
    918 	/* netobjs are pretty opaque, so don't try to summarize */
    919 }
    920 
    921 static void
    922 sum_ping_args(line)
    923 	char *line;
    924 {
    925 	sumxdr_nis_name(line);
    926 	strcat(line, " at ");
    927 	strcat(line, getxdr_time());
    928 }
    929 
    930 static void
    931 sum_nis_name(line)
    932 	char *line;
    933 {
    934 	sumxdr_nis_name(line);
    935 }
    936 
    937 static void
    938 sum_cback_data(line)
    939 	char *line;
    940 {
    941 	(void) sprintf(line, " (%lu entries)", getxdr_u_long());
    942 }
    943 
    944 static void
    945 sum_nis_result(line)
    946 	char *line;
    947 {
    948 	unsigned nobjs;
    949 
    950 	(void) sumxdr_nis_error(line);
    951 	nobjs = getxdr_u_long();
    952 	if (nobjs != 0) {
    953 		sprintf(line + strlen(line),
    954 			" and %lu object%s", nobjs, (nobjs == 1) ? "" : "s");
    955 	}
    956 }
    957 
    958 static void
    959 sum_fd_result(line)
    960 	char *line;
    961 {
    962 	(void) sumxdr_nis_error(line);
    963 #ifdef LESS_TERSE
    964 	strcat(line, " from");
    965 	sumxdr_nis_name(line + strlen(line));
    966 #endif /* LESS_TERSE */
    967 }
    968 
    969 static void
    970 sum_log_result(line)
    971 	char *line;
    972 {
    973 	uint_t dummy;
    974 	unsigned nents;
    975 
    976 	(void) sumxdr_nis_error(line);
    977 	(void) getxdr_bytes(&dummy);	/* Skip netobj lr_cookie */
    978 	nents = getxdr_u_long();
    979 	sprintf(line + strlen(line),
    980 		" and %lu log entr%s", nents, (nents == 1) ? "y" : "ies");
    981 }
    982 
    983 static void
    984 sum_bool(line)
    985 	char *line;
    986 {
    987 	(void) sprintf(line, getxdr_u_long() ? "true" : "false");
    988 }
    989 
    990 static void
    991 sum_cptime(line)
    992 	char *line;
    993 {
    994 	(void) sprintf(line, " %s", getxdr_time());
    995 }
    996 
    997 static void
    998 sum_cp_result(line)
    999 	char *line;
   1000 {
   1001 	(void) sumxdr_nis_error(line);
   1002 }
   1003 
   1004 static void
   1005 sum_nis_error(line)
   1006 	char *line;
   1007 {
   1008 	(void) sumxdr_nis_error(line);
   1009 }
   1010 
   1011 #define	SHOW_NIS_NAME(format)	(void) showxdr_string(NIS_MAXNAMELEN, format)
   1012 #define	SHOW_NAME()		SHOW_NIS_NAME("Name = %s")
   1013 
   1014 /*
   1015  * showxdr_longhex() -- ersatz wrapper around showxdr_hex() to ensure we
   1016  *	don't display too much on one line.  Multi-line results will
   1017  *  ===	probably look pretty awful, but we may at least prevent core-dumps
   1018  */
   1019 int nwords_longhex = 8;
   1020 
   1021 static void
   1022 showxdr_longhex(len, fmt)
   1023 	int len;
   1024 	char *fmt;
   1025 {
   1026 	int nbytes_longhex = 4 * nwords_longhex;
   1027 
   1028 	while (len > nbytes_longhex) {
   1029 		showxdr_hex(nbytes_longhex, fmt);
   1030 		len -= nbytes_longhex;
   1031 	}
   1032 	showxdr_hex(len, fmt);
   1033 }
   1034 
   1035 static void
   1036 detail_nis_error()
   1037 {
   1038 	enum nis_error status;
   1039 	int pos;
   1040 
   1041 	pos = getxdr_pos();
   1042 	status = (enum nis_error) getxdr_u_long();
   1043 	sprintf(get_line(pos, getxdr_pos()),
   1044 		"Status = %lu (%s)", status, nis_sperrno(status));
   1045 }
   1046 
   1047 void
   1048 detail_nis_oid()
   1049 {
   1050 	showxdr_time("    Object created at %s");
   1051 	showxdr_time("     last modified at %s");
   1052 }
   1053 
   1054 static void
   1055 detail_nis_server(outdent)
   1056 	int outdent;	/* Crock to get indentation right.  Normally zero; */
   1057 			/*   set to four instead to remove leading spaces  */
   1058 {
   1059 	unsigned nep;
   1060 	unsigned ktype;
   1061 	int pos1;
   1062 
   1063 	SHOW_NIS_NAME(outdent + "        Hostname = %s");
   1064 	nep = getxdr_u_long();
   1065 	while (nep-- > 0) { /* list of endpoints */
   1066 		(void) showxdr_string(1024,
   1067 					outdent + "            Uaddr    = %s");
   1068 		(void) showxdr_string(1024,
   1069 					outdent + "            Family   = %s");
   1070 		(void) showxdr_string(1024,
   1071 					outdent + "            Protocol = %s");
   1072 	}
   1073 	/*
   1074 	 * The format of the public-key information has changed (Aug '91).
   1075 	 *   Ye olde style was a u_char[64] (which XDR's pretty inefficiently,
   1076 	 *   but that's another matter).  The new format has a key-type
   1077 	 *   enumeration followed by an opaque<NIS_MAXKEYLEN>.  For now
   1078 	 *   (hence probably for all time) we'll hard-code the values of the
   1079 	 *   enumeration and of NIS_MAXKEYLEN here rather than getting them
   1080 	 *   from a header file.  Tsk.  Also for now, we'll try to guess
   1081 	 *   whether we're looking at the new format or the old.
   1082 	 */
   1083 	pos1 = getxdr_pos();
   1084 	ktype = getxdr_u_long();
   1085 	if (ktype < KEYTYPE_LIMIT || ktype == NIS_PK_DHEXT) {
   1086 		int	 pos2;
   1087 		unsigned klen;
   1088 
   1089 		pos2 = getxdr_pos();
   1090 		klen = getxdr_u_long();
   1091 		if (klen < KEYLEN_LIMIT) {
   1092 			/*
   1093 			 * Smells like new-style key; let's hope so.  If we're
   1094 			 *   wrong, it may cause XDR underflow.  Ugh.  ====
   1095 			 */
   1096 			sprintf(get_line(pos1, pos2),
   1097 				outdent + "        Key type   = %s",
   1098 				stringof_ktype(ktype));
   1099 			sprintf(get_line(pos2, getxdr_pos()),
   1100 				outdent + "        Key length = %lu", klen);
   1101 			if (klen != 0) {
   1102 				if (ktype == NIS_PK_DHEXT) {
   1103 					int marker;
   1104 					int key_count;
   1105 					marker = getxdr_pos();
   1106 					key_count = 1;
   1107 					while (getxdr_pos() < marker + klen) {
   1108 						ulong_t  a_u_long;
   1109 						ushort_t keylen;
   1110 						ushort_t algtype;
   1111 						size_t  binlen;
   1112 						size_t  binpadlen;
   1113 
   1114 						a_u_long = getxdr_u_long();
   1115 						keylen = a_u_long >> 16;
   1116 						algtype = a_u_long &
   1117 							((ushort_t)0xffff);
   1118 						binlen = (keylen + 7) / 8;
   1119 						binpadlen = ((binlen + 3) / 4)
   1120 								* 4;
   1121 						sprintf(get_line(0, 0),
   1122 					"            Key %d length = %hu bits",
   1123 							key_count, keylen);
   1124 						sprintf(get_line(0, 0),
   1125 					"            Algorithm type = %hu",
   1126 							algtype);
   1127 						if (keylen != 0)
   1128 							showxdr_hex(binpadlen,
   1129 					"            Key value = %s");
   1130 						key_count++;
   1131 					}
   1132 					setxdr_pos(marker + klen);
   1133 				} else {
   1134 					showxdr_hex(klen, outdent +
   1135 					    "        Key value  = %s");
   1136 				}
   1137 			}
   1138 			return;
   1139 		}
   1140 	}
   1141 	/*
   1142 	 * It didn't smell new, so assume it's the old u_char[64], which
   1143 	 *   XDR's as 64 32-bit words (ouch).
   1144 	 */
   1145 	setxdr_pos(4*SZ_PKEY + pos1);
   1146 	sprintf(get_line(pos1, getxdr_pos()),
   1147 		outdent + "        [Public Key not displayed]");
   1148 }
   1149 
   1150 void
   1151 detail_directory_obj()
   1152 {
   1153 	unsigned nstyp;
   1154 	unsigned nserv, nar;
   1155 	int pos;
   1156 	char *line;
   1157 
   1158 	SHOW_NIS_NAME("    Name    = %s");
   1159 	pos = getxdr_pos();
   1160 	nstyp = getxdr_u_long();
   1161 	line = get_line(pos, getxdr_pos());
   1162 	(void) sprintf(line, "    NStype  = %lu", nstyp);
   1163 	line += strlen(line);
   1164 
   1165 	switch (nstyp) {
   1166 	    case NIS:
   1167 		sprintf(line, " (NIS+)");
   1168 		break;
   1169 	    case SUNYP:
   1170 		sprintf(line, " (NIS/YP)");
   1171 		break;
   1172 	    case DNS:
   1173 		sprintf(line, " (DNS)");
   1174 		break;
   1175 	    default:
   1176 		break;
   1177 	}
   1178 
   1179 	nserv = showxdr_u_long("    Servers = %lu");
   1180 	while (nserv-- > 0) {	/* array of servers */
   1181 		detail_nis_server(0);
   1182 	}
   1183 	(void) showxdr_u_long("    Time to live  = %lu (seconds)");
   1184 	/* === show time in more useful form too? */
   1185 	nar = showxdr_u_long("    Access Rights = %lu");
   1186 	while (nar-- > 0) {	/* list of ar masks */
   1187 		unsigned ar, otype;
   1188 		pos = getxdr_pos();
   1189 		ar = getxdr_u_long();
   1190 		otype = getxdr_u_long();
   1191 		sprintf(get_line(pos, getxdr_pos()), "        %s for %s",
   1192 			stringof_rights(ar), stringof_otype(otype));
   1193 	}
   1194 }
   1195 
   1196 void
   1197 detail_group_obj()
   1198 {
   1199 	unsigned val;
   1200 	int pos;
   1201 
   1202 	pos = getxdr_pos();
   1203 	val = getxdr_u_long();
   1204 	sprintf(get_line(pos, getxdr_pos()),
   1205 		"    Flags    = %s", stringof_groupflags(val));
   1206 	val = showxdr_u_long("    Number of Members = %lu");
   1207 	while (val-- > 0) {
   1208 		SHOW_NIS_NAME("        %s");
   1209 	}
   1210 }
   1211 
   1212 void
   1213 detail_table_obj()
   1214 {
   1215 	unsigned ncols;
   1216 	unsigned col;
   1217 	(void) showxdr_string(1024, "    Table Type  = %s");
   1218 	(void) showxdr_long  ("    Max Columns = %d");
   1219 	(void) showxdr_char  ("    Separator   = '%c'"); /* ==== improve */
   1220 	ncols = showxdr_u_long("    Num Columns = %lu");
   1221 	for (col = 1; col <= ncols; col++) {
   1222 		int pos1, pos2;
   1223 		unsigned val;
   1224 		char format[23];
   1225 		/* === Print all three on one line? */
   1226 		sprintf(format, "%6d: ColName = \"%%s\"", col);
   1227 		(void) showxdr_string(NIS_MAXATTRNAME, format);
   1228 		pos1 = getxdr_pos();
   1229 		val = getxdr_u_long();
   1230 		pos2 = getxdr_pos();
   1231 		sprintf(get_line(pos1, pos2),
   1232 			"        Flags   = %s", stringof_colflags(val));
   1233 		val = getxdr_u_long();
   1234 		sprintf(get_line(pos2, getxdr_pos()),
   1235 			"        Rights  = %s", stringof_rights(val));
   1236 	}
   1237 	(void) showxdr_string(NIS_MAXPATH, "    Search Path = \"%s\"");
   1238 }
   1239 
   1240 void
   1241 detail_entry_obj()
   1242 {
   1243 	unsigned col, ncols;
   1244 	char *entyp;
   1245 	int is_nis_object = 0;
   1246 
   1247 	entyp = showxdr_string(1024, "    Entry Type  = \"%s\"");
   1248 	is_nis_object = (entyp != 0 && strcmp(entyp, "NIS object") == 0);
   1249 	/* Don't rely on entyp[] remaining valid */
   1250 	ncols = showxdr_u_long("    Num Columns = %lu");
   1251 	for (col = 1; col <= ncols; col++) {
   1252 		int pos1, pos2, len;
   1253 		unsigned flags;
   1254 		/* === Print both on one line? */
   1255 		pos1 = getxdr_pos();
   1256 		flags = getxdr_u_long();
   1257 		pos2 = getxdr_pos();
   1258 		if (ncols == 1) {
   1259 			sprintf(get_line(pos1, pos2),
   1260 				"        Flags = %s",
   1261 				stringof_entryflags(flags));
   1262 		} else {
   1263 			sprintf(get_line(pos1, pos2),
   1264 				"%6d: Flags = %s",
   1265 				col, stringof_entryflags(flags));
   1266 		}
   1267 		if (flags & (EN_BINARY | EN_CRYPT | EN_XDR)) {
   1268 			len = getxdr_u_long();
   1269 			if (is_nis_object &&
   1270 			    len == sizeof (unsigned long) &&
   1271 			    (flags & EN_CRYPT) == 0) {
   1272 				/* Special case for type = "NIS object" */
   1273 				unsigned otype = getxdr_u_long();
   1274 				sprintf(get_line(pos2, getxdr_pos()),
   1275 					"        Value = %08x (ObjType = %s)",
   1276 					otype, stringof_otype(otype));
   1277 			} else {
   1278 				showxdr_longhex(len,
   1279 						"        Value = Binary %s");
   1280 			}
   1281 		} else {
   1282 			showxdr_string(NIS_MAXATTRVAL,
   1283 					"        Value = ASCII \"%s\"");
   1284 		}
   1285 	}
   1286 }
   1287 
   1288 void
   1289 detail_link_obj()
   1290 {
   1291 	int pos;
   1292 	int val;
   1293 
   1294 	pos = getxdr_pos();
   1295 	val = getxdr_long();
   1296 	sprintf(get_line(pos, getxdr_pos()),
   1297 		"    Real Type= %08x (%s)", val, stringof_otype(val));
   1298 	detail_nis_attrs(0);
   1299 	SHOW_NIS_NAME("    Real Name= %s");
   1300 }
   1301 
   1302 void
   1303 detail_private_obj()
   1304 {
   1305 	/* ==== Need something fancier than this to be really useful */
   1306 	showxdr_longhex(getxdr_u_long(), "    Data     = Binary %s");
   1307 }
   1308 
   1309 void
   1310 detail_nis_object()
   1311 {
   1312 	unsigned rights, otype;
   1313 	int pos;
   1314 
   1315 	detail_nis_oid();
   1316 	SHOW_NIS_NAME("    Name     = %s");
   1317 	SHOW_NIS_NAME("    Owner    = %s");
   1318 	SHOW_NIS_NAME("    Group    = %s");
   1319 	SHOW_NIS_NAME("    Domain   = %s");
   1320 	pos = getxdr_pos();
   1321 	rights = getxdr_u_long();
   1322 	sprintf(get_line(pos, getxdr_pos()), "    Rights   = %s",
   1323 		stringof_rights(rights));
   1324 	(void) showxdr_u_long("    Lifetime = %8lu (seconds)");
   1325 
   1326 	/* ?? show_space(); */
   1327 	pos = getxdr_pos();
   1328 	otype = getxdr_u_long();
   1329 	(void) sprintf(get_line(pos, getxdr_pos()),
   1330 			"    ObjType  = %08x (%s)",
   1331 			otype, stringof_otype(otype));
   1332 	switch (otype) {
   1333 	    case NIS_DIRECTORY_OBJ:
   1334 		detail_directory_obj();
   1335 		break;
   1336 	    case NIS_GROUP_OBJ:
   1337 		detail_group_obj();
   1338 		break;
   1339 	    case NIS_TABLE_OBJ:
   1340 		detail_table_obj();
   1341 		break;
   1342 	    case NIS_ENTRY_OBJ:
   1343 		detail_entry_obj();
   1344 		break;
   1345 	    case NIS_LINK_OBJ:
   1346 		detail_link_obj();
   1347 		break;
   1348 	    case NIS_PRIVATE_OBJ:
   1349 		detail_private_obj();
   1350 		break;
   1351 	    default:
   1352 		/* ==== Can't do anything clever, right? */
   1353 		break;
   1354 	}
   1355 }
   1356 
   1357 static void
   1358 detail_ns_request()
   1359 {
   1360 	unsigned nobjs;
   1361 
   1362 	SHOW_NAME();
   1363 	nobjs = getxdr_u_long();
   1364 	/* nobjs should be 0 or 1 only; === should check it ? */
   1365 	if (nobjs != 0) {
   1366 		sprintf(get_line(0, 0), "Object included:");
   1367 	}
   1368 	while (nobjs-- > 0) {
   1369 		detail_nis_object();
   1370 	}
   1371 }
   1372 
   1373 static void
   1374 detail_ib_request()
   1375 {
   1376 	unsigned len;
   1377 	unsigned flags;
   1378 	int pos;
   1379 
   1380 	SHOW_NAME();
   1381 	detail_nis_attrs(INDENT_SPACES);
   1382 	pos = getxdr_pos();
   1383 	flags = getxdr_u_long();
   1384 	sprintf(get_line(pos, getxdr_pos()),
   1385 		"Flags = %s", stringof_ib_flags(flags));
   1386 	pos = getxdr_pos();
   1387 	len = getxdr_u_long();
   1388 	if (len == 0) {
   1389 		sprintf(get_line(pos, getxdr_pos()), "No included object");
   1390 	} else {
   1391 		/* len should be 0 or 1 only; === should check it ? */
   1392 		sprintf(get_line(pos, getxdr_pos()), "Included object:");
   1393 		while (len-- > 0) {
   1394 			detail_nis_object();
   1395 		}
   1396 	}
   1397 	pos = getxdr_pos();
   1398 	len = getxdr_u_long();
   1399 	if (len == 0) {
   1400 		sprintf(get_line(pos, getxdr_pos()), "No callback");
   1401 	} else {
   1402 		/* len should be 0 or 1 only; === should check it ? */
   1403 		sprintf(get_line(pos, getxdr_pos()), "Callback info:");
   1404 		while (len-- > 0) {
   1405 			detail_nis_server(INDENT_SPACES);
   1406 		}
   1407 	}
   1408 	(void) showxdr_u_long("Bufsize = %lu");
   1409 	detail_cookie();
   1410 }
   1411 
   1412 static void
   1413 detail_fd_args()
   1414 {
   1415 	SHOW_NAME();
   1416 	SHOW_NIS_NAME("Requester = %s");
   1417 }
   1418 
   1419 static void
   1420 detail_nis_taglist()
   1421 {
   1422 	unsigned ntags;
   1423 	unsigned tagn;
   1424 
   1425 	ntags = showxdr_u_long("Number of tags = %lu");
   1426 	for (tagn = 1; tagn < ntags; tagn++) {
   1427 		unsigned ttype;
   1428 		int pos;
   1429 
   1430 		if (ntags != 1) {
   1431 			sprintf(get_line(0, 0), "  Tag %d:", tagn);
   1432 		}
   1433 		pos = getxdr_pos();
   1434 		ttype = getxdr_u_long();
   1435 		sprintf(get_line(pos, getxdr_pos()),
   1436 			"    Tag name  = %08x (%s)",
   1437 			ttype, stringof_tag(ttype));
   1438 		showxdr_string(1024, "    Tag value = \"%s\"");
   1439 		/* ^ ==== nis.x says it's a string rather than an opaque, */
   1440 		/*	  but is it really?				  */
   1441 	}
   1442 }
   1443 
   1444 static void
   1445 detail_dump_args()
   1446 {
   1447 	unsigned nserv;
   1448 	int pos;
   1449 
   1450 	SHOW_NAME();
   1451 	showxdr_time("Time = %s");
   1452 
   1453 	pos = getxdr_pos();
   1454 	nserv = getxdr_u_long();
   1455 	if (nserv == 1) {
   1456 		sprintf(get_line(pos, getxdr_pos()), "Callback:");
   1457 	} else if (nserv > 1) {
   1458 		sprintf(get_line(pos, getxdr_pos()),
   1459 			"Num callbacks = %lu (!?):", nserv);
   1460 	}
   1461 	while (nserv-- > 0) {
   1462 		detail_nis_server(INDENT_SPACES);
   1463 	}
   1464 }
   1465 
   1466 static void
   1467 detail_callback()	/* i.e. the netobj argument to NIS_CALLBACK */
   1468 {
   1469 	detail_cookie();
   1470 }
   1471 
   1472 static void
   1473 detail_cookie()
   1474 {
   1475 	unsigned len;
   1476 	int pos;
   1477 
   1478 	pos = getxdr_pos();
   1479 	len = getxdr_u_long();
   1480 	if (len == 0) {
   1481 		sprintf(get_line(pos, getxdr_pos()), "Cookie = NULL");
   1482 	} else {
   1483 		showxdr_longhex(len, "Cookie = %s");
   1484 	}
   1485 }
   1486 
   1487 static void
   1488 detail_ping_args()
   1489 {
   1490 	SHOW_NAME();
   1491 	showxdr_time("Time = %s");
   1492 }
   1493 
   1494 static void
   1495 detail_nis_name()
   1496 {
   1497 	SHOW_NAME();
   1498 }
   1499 
   1500 static void
   1501 detail_cback_data()
   1502 {
   1503 	unsigned nobjs, no;
   1504 	nobjs = showxdr_u_long("Number of entries = %lu");
   1505 	for (no = 1; no <= nobjs; no++) {
   1506 		if (getxdr_long() == 0) {
   1507 			sprintf(get_line(0, 0), "  Entry %d is NULL (!?)", no);
   1508 		} else {
   1509 			if (nobjs != 1) {
   1510 
   1511 				sprintf(get_line(0, 0), "  Entry %d:", no);
   1512 			}
   1513 			detail_nis_object();
   1514 		}
   1515 	}
   1516 }
   1517 
   1518 static void
   1519 detail_nis_result()
   1520 {
   1521 	int pos;
   1522 	unsigned no, nobjs;
   1523 
   1524 	detail_nis_error();
   1525 	pos = getxdr_pos();
   1526 	nobjs = getxdr_u_long();
   1527 	sprintf(get_line(pos, getxdr_pos()), "Number of objects = %lu", nobjs);
   1528 	for (no = 1; no <= nobjs; no++) {
   1529 		if (nobjs != 1) {
   1530 			sprintf(get_line(0, 0), "  Object %d:", no);
   1531 		}
   1532 		detail_nis_object();
   1533 	}
   1534 	detail_cookie();
   1535 	showxdr_u_long("Server   ticks = %8lu");
   1536 	showxdr_u_long("Database ticks = %8lu");
   1537 	showxdr_u_long("Cache    ticks = %8lu"); /* These should always be   */
   1538 	showxdr_u_long("Client   ticks = %8lu"); /*   zero on the wire, yes? */
   1539 }
   1540 
   1541 static void
   1542 detail_fd_result()
   1543 {
   1544 	int pos;
   1545 	unsigned len;
   1546 	int dir_pos;
   1547 	unsigned dir_len;
   1548 	int dir_end;
   1549 
   1550 	detail_nis_error();
   1551 	SHOW_NAME();
   1552 	/* nis.x doesn't say so, but the opaque contains a directory_obj */
   1553 	dir_len = getxdr_u_long(); /* Byte-count at start of opaque */
   1554 	if (dir_len == 0) {
   1555 		sprintf(get_line(0, 0), "Directory object = NULL");
   1556 	} else {
   1557 		sprintf(get_line(0, 0), "Directory object:");
   1558 		dir_pos = getxdr_pos();
   1559 		dir_end = ((dir_len + 3) & ~3) + dir_pos;
   1560 		/*		^^^^^^^^^^ Did XDR do this already? */
   1561 		detail_directory_obj();
   1562 		pos = getxdr_pos();
   1563 		if (pos != dir_end) {
   1564 			sprintf(get_line(pos, dir_end),
   1565 			"(Skipping %d unused bytes at end of directory object)",
   1566 				dir_end - pos);
   1567 			setxdr_pos(dir_end);
   1568 		}
   1569 	}
   1570 	len = getxdr_u_long();
   1571 	if (len == 0) {
   1572 		sprintf(get_line(pos, getxdr_pos()),
   1573 			"Signature = NULL");
   1574 	} else {
   1575 		if (len > 8) {
   1576 			sprintf(get_line(pos, getxdr_pos()),
   1577 				"Signature length is %lu, expected <= 8 bytes",
   1578 				len);
   1579 		}
   1580 		showxdr_longhex(len, "Signature = %s");
   1581 	}
   1582 }
   1583 
   1584 /*
   1585  * is_printable() -- looks for a string of (len-1) printable characters
   1586  *	followed by a NUL.  Characters are deemed printable iff ctype's
   1587  *	isprint() macro says so, thus tab (^I) is regarded as unprintable.
   1588  * === Is this test more stringent than we want, esp wrt placement of NUL?
   1589  */
   1590 static int
   1591 is_printable(str, len)
   1592 	char	 *str;
   1593 	unsigned len;
   1594 {
   1595 	while (len > 1) {
   1596 		if (!isprint(*str)) {
   1597 			return (0);
   1598 		}
   1599 		str++, len--;
   1600 	}
   1601 	return (len == 1 && *str == 0);
   1602 }
   1603 
   1604 static void
   1605 detail_nis_attrs(outdent)
   1606 	int outdent;	/* Crock to get indentation right.  Normally zero; */
   1607 			/*   set to four instead to remove leading spaces  */
   1608 {
   1609 	unsigned nattrs;
   1610 
   1611 	nattrs = showxdr_u_long(outdent + "    Number of attributes = %lu");
   1612 	while (nattrs-- > 0) {
   1613 		char	 *str;
   1614 		unsigned len;
   1615 		int pos;
   1616 
   1617 		showxdr_string(NIS_MAXATTRNAME,
   1618 				outdent + "        AttrName = %s");
   1619 		pos = getxdr_pos();
   1620 		str = getxdr_bytes(&len);
   1621 		if (is_printable(str, len)) {
   1622 			setxdr_pos(pos);
   1623 			showxdr_string(NIS_MAXATTRVAL, outdent +
   1624 					"        AttrVal  = ASCII \"%s\"");
   1625 		} else {
   1626 			setxdr_pos(pos);
   1627 			len = getxdr_u_long();	/* Yes, we knew it already */
   1628 			showxdr_longhex(len, outdent +
   1629 					"        AttrVal  = Binary %s");
   1630 		}
   1631 	}
   1632 }
   1633 
   1634 static void
   1635 detail_log_entry()
   1636 {
   1637 	int pos;
   1638 	long val;
   1639 
   1640 	showxdr_time("    Log Time  = %s");
   1641 	pos = getxdr_pos();
   1642 	val = getxdr_long();
   1643 	sprintf(get_line(pos, getxdr_pos()), "    Log Type  = %d (%s)",
   1644 		val, stringof_entry_t(val));
   1645 	SHOW_NIS_NAME("    Principal = %s");
   1646 	SHOW_NIS_NAME("    Table/dir = %s");
   1647 	detail_nis_attrs(0);
   1648 	sprintf(get_line(0, 0), "    Object Value:");
   1649 	detail_nis_object();
   1650 }
   1651 
   1652 static void
   1653 detail_log_result()
   1654 {
   1655 	unsigned len, nents, en;
   1656 	int pos;
   1657 
   1658 	detail_nis_error();
   1659 	pos = getxdr_pos();
   1660 	len = getxdr_u_long();
   1661 	detail_cookie();
   1662 	nents = showxdr_u_long("Number of log entries = %lu");
   1663 	for (en = 1; en <= nents; en++) {
   1664 		if (nents != 1) {
   1665 			sprintf(get_line(0, 0), "  Log Entry %d", en);
   1666 		}
   1667 		detail_log_entry();
   1668 	}
   1669 }
   1670 
   1671 static void
   1672 detail_bool()
   1673 {
   1674 	int pos;
   1675 	unsigned val;
   1676 
   1677 	pos = getxdr_pos();
   1678 	val = getxdr_u_long();
   1679 	sprintf(get_line(pos, getxdr_pos()),
   1680 		"Result = %s", val ? "true" : "false");
   1681 }
   1682 
   1683 static void
   1684 detail_cptime()
   1685 {
   1686 	showxdr_time("Time = %s");
   1687 }
   1688 
   1689 static void
   1690 detail_cp_result()
   1691 {
   1692 	detail_nis_error();
   1693 	showxdr_u_long("Ticks (Server) = %lu");
   1694 	showxdr_u_long("    (Database) = %lu");
   1695 }
   1696