xref: /illumos-gate/usr/src/cmd/rpcinfo/rpcinfo.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
22  * Use is subject to license terms.
23  */
24 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
25 /* All Rights Reserved */
26 /*
27  * University Copyright- Copyright (c) 1982, 1986, 1988
28  * The Regents of the University of California
29  * All Rights Reserved
30  *
31  * University Acknowledgment- Portions of this document are derived from
32  * software developed by the University of California, Berkeley, and its
33  * contributors.
34  */
35 
36 /*
37  * rpcinfo: ping a particular rpc program
38  * 	or dump the the registered programs on the remote machine.
39  */
40 
41 /*
42  * We are for now defining PORTMAP here.  It doesnt even compile
43  * unless it is defined.
44  */
45 #ifndef	PORTMAP
46 #define	PORTMAP
47 #endif
48 
49 /*
50  * If PORTMAP is defined, rpcinfo will talk to both portmapper and
51  * rpcbind programs; else it talks only to rpcbind. In the latter case
52  * all the portmapper specific options such as -u, -t, -p become void.
53  */
54 #include <rpc/rpc.h>
55 #include <stdio.h>
56 #include <rpc/rpcb_prot.h>
57 #include <rpc/nettype.h>
58 #include <netdir.h>
59 #include <rpc/rpcent.h>
60 #include <sys/utsname.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <ctype.h>
64 
65 #ifdef PORTMAP		/* Support for version 2 portmapper */
66 #include <netinet/in.h>
67 #include <sys/socket.h>
68 #include <netdb.h>
69 #include <arpa/inet.h>
70 #include <rpc/pmap_prot.h>
71 #include <rpc/pmap_clnt.h>
72 #endif
73 
74 #define	MAXHOSTLEN	256
75 #define	MIN_VERS	((ulong_t)0)
76 #define	MAX_VERS	(4294967295UL)
77 #define	UNKNOWN		"unknown"
78 
79 #define	MAX(a, b) (((a) > (b)) ? (a) : (b))
80 
81 extern int	t_errno;
82 extern long	strtol();
83 static char *spaces();
84 
85 #ifdef PORTMAP
86 static void	ip_ping(/*ushort_t portflag, char *trans,
87 				int argc, char **argv*/);
88 static CLIENT	*clnt_com_create(/* struct sockaddr_in *addr, long prog,
89 			long vers, int *fd, char *trans*/);
90 static void	pmapdump(/*int argc, char **argv*/);
91 static void	get_inet_address(/*struct sockaddr_in *addr, char *host*/);
92 #endif
93 
94 static bool_t	reply_proc(/*void *res, struct netbuf *who*,
95 			struct netconfig *nconf*/);
96 static void	brdcst(/*int argc, char **argv*/);
97 static void	addrping(/*char *address, char *netid,
98 				int argc, char **argv*/);
99 static void	progping(/* char *netid, int argc, char **argv*/);
100 static CLIENT	*clnt_addr_create(/* char *addr, struct netconfig *nconf,
101 				long prog, long vers*/);
102 static CLIENT   *clnt_rpcbind_create(/* char *host, int vers */);
103 static CLIENT   *getclnthandle(/* host, nconf, rpcbversnum */);
104 static int	pstatus(/*CLIENT *client, ulong_t prognum, ulong_t vers*/);
105 static void	rpcbdump(/*char *netid, int argc, char **argv*/);
106 static void	rpcbgetstat(/* int argc, char **argv*/);
107 static void	rpcbaddrlist(/*char *netid, int argc, char **argv*/);
108 static void	deletereg(/*char *netid, int argc, char **argv */);
109 static void	print_rmtcallstat(/* rtype, infp */);
110 static void	print_getaddrstat(/* rtype, infp */);
111 static void	usage(/*void*/);
112 static ulong_t	getprognum(/*char *arg*/);
113 static ulong_t	getvers(/*char *arg*/);
114 
115 /*
116  * Functions to be performed.
117  */
118 #define	NONE		0	/* no function */
119 #define	PMAPDUMP	1	/* dump portmapper registrations */
120 #define	TCPPING		2	/* ping TCP service */
121 #define	UDPPING		3	/* ping UDP service */
122 #define	BROADCAST	4	/* ping broadcast service */
123 #define	DELETES		5	/* delete registration for the service */
124 #define	ADDRPING	6	/* pings at the given address */
125 #define	PROGPING	7	/* pings a program on a given host */
126 #define	RPCBDUMP	8	/* dump rpcbind registrations */
127 #define	RPCBDUMP_SHORT	9	/* dump rpcbind registrations - short version */
128 #define	RPCBADDRLIST	10	/* dump addr list about one prog */
129 #define	RPCBGETSTAT	11	/* Get statistics */
130 
131 struct netidlist {
132 	char *netid;
133 	struct netidlist *next;
134 };
135 
136 struct verslist {
137 	int vers;
138 	struct verslist *next;
139 };
140 
141 struct rpcbdump_short {
142 	ulong_t prog;
143 	struct verslist *vlist;
144 	struct netidlist *nlist;
145 	struct rpcbdump_short *next;
146 	char *owner;
147 };
148 
149 
150 char *loopback_netid = NULL;
151 struct netconfig *loopback_nconf;
152 
153 int
154 main(argc, argv)
155 	int argc;
156 	char **argv;
157 {
158 	register int c;
159 	extern char *optarg;
160 	extern int optind;
161 	int errflg;
162 	int function;
163 	char *netid = NULL;
164 	char *address = NULL;
165 	void *handle;
166 #ifdef PORTMAP
167 	char *strptr;
168 	ushort_t portnum = 0;
169 #endif
170 
171 	function = NONE;
172 	errflg = 0;
173 #ifdef PORTMAP
174 	while ((c = getopt(argc, argv, "a:bdlmn:pstT:u")) != EOF) {
175 #else
176 	while ((c = getopt(argc, argv, "a:bdlmn:sT:")) != EOF) {
177 #endif
178 		switch (c) {
179 #ifdef PORTMAP
180 		case 'p':
181 			if (function != NONE)
182 				errflg = 1;
183 			else
184 				function = PMAPDUMP;
185 			break;
186 
187 		case 't':
188 			if (function != NONE)
189 				errflg = 1;
190 			else
191 				function = TCPPING;
192 			break;
193 
194 		case 'u':
195 			if (function != NONE)
196 				errflg = 1;
197 			else
198 				function = UDPPING;
199 			break;
200 
201 		case 'n':
202 			portnum = (ushort_t)strtol(optarg, &strptr, 10);
203 			if (strptr == optarg || *strptr != '\0') {
204 				(void) fprintf(stderr,
205 			"rpcinfo: %s is illegal port number\n",
206 					optarg);
207 				exit(1);
208 			}
209 			break;
210 #endif
211 		case 'a':
212 			address = optarg;
213 			if (function != NONE)
214 				errflg = 1;
215 			else
216 				function = ADDRPING;
217 			break;
218 		case 'b':
219 			if (function != NONE)
220 				errflg = 1;
221 			else
222 				function = BROADCAST;
223 			break;
224 
225 		case 'd':
226 			if (function != NONE)
227 				errflg = 1;
228 			else
229 				function = DELETES;
230 			break;
231 
232 		case 'l':
233 			if (function != NONE)
234 				errflg = 1;
235 			else
236 				function = RPCBADDRLIST;
237 			break;
238 
239 		case 'm':
240 			if (function != NONE)
241 				errflg = 1;
242 			else
243 				function = RPCBGETSTAT;
244 			break;
245 
246 		case 's':
247 			if (function != NONE)
248 				errflg = 1;
249 			else
250 				function = RPCBDUMP_SHORT;
251 			break;
252 
253 		case 'T':
254 			netid = optarg;
255 			break;
256 		case '?':
257 			errflg = 1;
258 			break;
259 		}
260 	}
261 
262 	if (errflg || ((function == ADDRPING) && !netid)) {
263 		usage();
264 		return (1);
265 	}
266 	if (netid == NULL) {	/* user has not selected transport to use */
267 		/*
268 		 * See if a COTS loopback transport is available, in case we
269 		 * will be talking to the local system.
270 		 */
271 		handle = setnetconfig();
272 		while ((loopback_nconf = getnetconfig(handle)) != NULL) {
273 			if (strcmp(loopback_nconf->nc_protofmly,
274 				NC_LOOPBACK) == 0 &&
275 			    (loopback_nconf->nc_semantics == NC_TPI_COTS ||
276 			    loopback_nconf->nc_semantics == NC_TPI_COTS_ORD)) {
277 				loopback_netid = loopback_nconf->nc_netid;
278 				break;
279 			}
280 		}
281 		if (loopback_netid == NULL) {
282 			(void) endnetconfig(handle);
283 		}
284 	}
285 	if (function == NONE) {
286 		if (argc - optind > 1)
287 			function = PROGPING;
288 		else
289 			function = RPCBDUMP;
290 	}
291 
292 	switch (function) {
293 #ifdef PORTMAP
294 	case PMAPDUMP:
295 		if (portnum != 0) {
296 			usage();
297 			return (1);
298 		}
299 		pmapdump(argc - optind, argv + optind);
300 		break;
301 
302 	case UDPPING:
303 		ip_ping(portnum, "udp", argc - optind, argv + optind);
304 		break;
305 
306 	case TCPPING:
307 		ip_ping(portnum, "tcp", argc - optind, argv + optind);
308 		break;
309 #endif
310 	case BROADCAST:
311 		brdcst(argc - optind, argv + optind);
312 		break;
313 	case DELETES:
314 		deletereg(netid, argc - optind, argv + optind);
315 		break;
316 	case ADDRPING:
317 		addrping(address, netid, argc - optind, argv + optind);
318 		break;
319 	case PROGPING:
320 		progping(netid, argc - optind, argv + optind);
321 		break;
322 	case RPCBDUMP:
323 	case RPCBDUMP_SHORT:
324 		rpcbdump(function, netid, argc - optind, argv + optind);
325 		break;
326 	case RPCBGETSTAT:
327 		rpcbgetstat(argc - optind, argv + optind);
328 		break;
329 	case RPCBADDRLIST:
330 		rpcbaddrlist(netid, argc - optind, argv + optind);
331 		break;
332 	}
333 	return (0);
334 }
335 
336 #ifdef PORTMAP
337 static CLIENT *
338 clnt_com_create(addr, prog, vers, fdp, trans)
339 	struct sockaddr_in *addr;
340 	ulong_t prog;
341 	ulong_t vers;
342 	int *fdp;
343 	char *trans;
344 {
345 	CLIENT *clnt;
346 
347 	if (strcmp(trans, "tcp") == 0) {
348 		clnt = clnttcp_create(addr, prog, vers, fdp, 0, 0);
349 	} else {
350 		struct timeval to;
351 
352 		to.tv_sec = 5;
353 		to.tv_usec = 0;
354 		clnt = clntudp_create(addr, prog, vers, to, fdp);
355 	}
356 	if (clnt == (CLIENT *)NULL) {
357 		clnt_pcreateerror("rpcinfo");
358 		if (vers == MIN_VERS)
359 			(void) printf("program %lu is not available\n", prog);
360 		else
361 			(void) printf(
362 				"program %lu version %lu is not available\n",
363 							prog, vers);
364 		exit(1);
365 	}
366 	return (clnt);
367 }
368 
369 /*
370  * If portnum is 0, then go and get the address from portmapper, which happens
371  * transparently through clnt*_create(); If version number is not given, it
372  * tries to find out the version number by making a call to version 0 and if
373  * that fails, it obtains the high order and the low order version number. If
374  * version 0 calls succeeds, it tries for MAXVERS call and repeats the same.
375  */
376 static void
377 ip_ping(portnum, trans, argc, argv)
378 	ushort_t portnum;
379 	char *trans;
380 	int argc;
381 	char **argv;
382 {
383 	CLIENT *client;
384 	int fd = RPC_ANYFD;
385 	struct timeval to;
386 	struct sockaddr_in addr;
387 	enum clnt_stat rpc_stat;
388 	ulong_t prognum, vers, minvers, maxvers;
389 	struct rpc_err rpcerr;
390 	int failure = 0;
391 
392 	if (argc < 2 || argc > 3) {
393 		usage();
394 		exit(1);
395 	}
396 	to.tv_sec = 10;
397 	to.tv_usec = 0;
398 	prognum = getprognum(argv[1]);
399 	get_inet_address(&addr, argv[0]);
400 	if (argc == 2) {	/* Version number not known */
401 		/*
402 		 * A call to version 0 should fail with a program/version
403 		 * mismatch, and give us the range of versions supported.
404 		 */
405 		vers = MIN_VERS;
406 	} else {
407 		vers = getvers(argv[2]);
408 	}
409 	addr.sin_port = htons(portnum);
410 	client = clnt_com_create(&addr, prognum, vers, &fd, trans);
411 	rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t)xdr_void,
412 			(char *)NULL, (xdrproc_t)xdr_void, (char *)NULL,
413 			to);
414 	if (argc != 2) {
415 		/* Version number was known */
416 		if (pstatus(client, prognum, vers) < 0)
417 			exit(1);
418 		(void) CLNT_DESTROY(client);
419 		return;
420 	}
421 	/* Version number not known */
422 	(void) CLNT_CONTROL(client, CLSET_FD_NCLOSE, (char *)NULL);
423 	if (rpc_stat == RPC_PROGVERSMISMATCH) {
424 		clnt_geterr(client, &rpcerr);
425 		minvers = rpcerr.re_vers.low;
426 		maxvers = rpcerr.re_vers.high;
427 	} else if (rpc_stat == RPC_SUCCESS) {
428 		/*
429 		 * Oh dear, it DOES support version 0.
430 		 * Let's try version MAX_VERS.
431 		 */
432 		(void) CLNT_DESTROY(client);
433 		addr.sin_port = htons(portnum);
434 		client = clnt_com_create(&addr, prognum, MAX_VERS, &fd, trans);
435 		rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t)xdr_void,
436 				(char *)NULL, (xdrproc_t)xdr_void,
437 				(char *)NULL, to);
438 		if (rpc_stat == RPC_PROGVERSMISMATCH) {
439 			clnt_geterr(client, &rpcerr);
440 			minvers = rpcerr.re_vers.low;
441 			maxvers = rpcerr.re_vers.high;
442 		} else if (rpc_stat == RPC_SUCCESS) {
443 			/*
444 			 * It also supports version MAX_VERS.
445 			 * Looks like we have a wise guy.
446 			 * OK, we give them information on all
447 			 * 4 billion versions they support...
448 			 */
449 			minvers = 0;
450 			maxvers = MAX_VERS;
451 		} else {
452 			(void) pstatus(client, prognum, MAX_VERS);
453 			exit(1);
454 		}
455 	} else {
456 		(void) pstatus(client, prognum, (ulong_t)0);
457 		exit(1);
458 	}
459 	(void) CLNT_DESTROY(client);
460 	for (vers = minvers; vers <= maxvers; vers++) {
461 		addr.sin_port = htons(portnum);
462 		client = clnt_com_create(&addr, prognum, vers, &fd, trans);
463 		rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t)xdr_void,
464 				(char *)NULL, (xdrproc_t)xdr_void,
465 				(char *)NULL, to);
466 		if (pstatus(client, prognum, vers) < 0)
467 				failure = 1;
468 		(void) CLNT_DESTROY(client);
469 	}
470 	if (failure)
471 		exit(1);
472 	(void) t_close(fd);
473 }
474 
475 /*
476  * Dump all the portmapper registerations
477  */
478 static void
479 pmapdump(argc, argv)
480 	int argc;
481 	char **argv;
482 {
483 	struct sockaddr_in server_addr;
484 	pmaplist_ptr head = NULL;
485 	int socket = RPC_ANYSOCK;
486 	struct timeval minutetimeout;
487 	register CLIENT *client;
488 	struct rpcent *rpc;
489 	enum clnt_stat clnt_st;
490 	struct rpc_err err;
491 	struct utsname utsname;
492 	char *host;
493 
494 	if (argc > 1) {
495 		usage();
496 		exit(1);
497 	}
498 	if (argc == 1) {
499 		host = argv[0];
500 		get_inet_address(&server_addr, host);
501 	} else {
502 		(void) uname(&utsname);
503 		host = utsname.nodename;
504 		get_inet_address(&server_addr, host);
505 	}
506 	minutetimeout.tv_sec = 60;
507 	minutetimeout.tv_usec = 0;
508 	server_addr.sin_port = htons(PMAPPORT);
509 	if ((client = clnttcp_create(&server_addr, PMAPPROG,
510 		PMAPVERS, &socket, 50, 500)) == NULL) {
511 		if (rpc_createerr.cf_stat == RPC_TLIERROR) {
512 			/*
513 			 * "Misc. TLI error" is not too helpful. Most likely
514 			 * the connection to the remote server timed out, so
515 			 * this error is at least less perplexing.
516 			 */
517 			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
518 			rpc_createerr.cf_error.re_status = RPC_FAILED;
519 		}
520 		clnt_pcreateerror("rpcinfo: can't contact portmapper");
521 		exit(1);
522 	}
523 	clnt_st = CLNT_CALL(client, PMAPPROC_DUMP, (xdrproc_t)xdr_void,
524 		NULL, (xdrproc_t)xdr_pmaplist_ptr, (char *)&head,
525 		minutetimeout);
526 	if (clnt_st != RPC_SUCCESS) {
527 		if ((clnt_st == RPC_PROGVERSMISMATCH) ||
528 		    (clnt_st == RPC_PROGUNAVAIL)) {
529 			CLNT_GETERR(client, &err);
530 			if (err.re_vers.low > PMAPVERS)
531 				(void) fprintf(stderr,
532 		"%s does not support portmapper.  Try rpcinfo %s instead\n",
533 					host, host);
534 			exit(1);
535 		}
536 		clnt_perror(client, "rpcinfo: can't contact portmapper");
537 		exit(1);
538 	}
539 	if (head == NULL) {
540 		(void) printf("No remote programs registered.\n");
541 	} else {
542 		(void) printf("   program vers proto   port  service\n");
543 		for (; head != NULL; head = head->pml_next) {
544 			(void) printf("%10ld%5ld",
545 				head->pml_map.pm_prog,
546 				head->pml_map.pm_vers);
547 			if (head->pml_map.pm_prot == IPPROTO_UDP)
548 				(void) printf("%6s", "udp");
549 			else if (head->pml_map.pm_prot == IPPROTO_TCP)
550 				(void) printf("%6s", "tcp");
551 			else
552 				(void) printf("%6ld", head->pml_map.pm_prot);
553 			(void) printf("%7ld", head->pml_map.pm_port);
554 			rpc = getrpcbynumber(head->pml_map.pm_prog);
555 			if (rpc)
556 				(void) printf("  %s\n", rpc->r_name);
557 			else
558 				(void) printf("\n");
559 		}
560 	}
561 }
562 
563 static void
564 get_inet_address(addr, host)
565 	struct sockaddr_in *addr;
566 	char *host;
567 {
568 	struct netconfig *nconf;
569 	struct nd_hostserv service;
570 	struct nd_addrlist *naddrs;
571 
572 	(void) memset((char *)addr, 0, sizeof (*addr));
573 	addr->sin_addr.s_addr = inet_addr(host);
574 	if (addr->sin_addr.s_addr == (uint32_t)-1 ||
575 	    addr->sin_addr.s_addr == 0) {
576 		if ((nconf = __rpc_getconfip("udp")) == NULL &&
577 		    (nconf = __rpc_getconfip("tcp")) == NULL) {
578 			(void) fprintf(stderr,
579 			"rpcinfo: couldn't find a suitable transport\n");
580 			exit(1);
581 		} else {
582 			service.h_host = host;
583 			service.h_serv = "rpcbind";
584 			if (netdir_getbyname(nconf, &service, &naddrs)) {
585 				(void) fprintf(stderr, "rpcinfo: %s: %s\n",
586 						host, netdir_sperror());
587 				exit(1);
588 			} else {
589 				(void) memcpy((caddr_t)addr,
590 				    naddrs->n_addrs->buf, naddrs->n_addrs->len);
591 				(void) netdir_free((char *)naddrs, ND_ADDRLIST);
592 			}
593 			(void) freenetconfigent(nconf);
594 		}
595 	} else {
596 		addr->sin_family = AF_INET;
597 	}
598 }
599 #endif /* PORTMAP */
600 
601 /*
602  * reply_proc collects replies from the broadcast.
603  * to get a unique list of responses the output of rpcinfo should
604  * be piped through sort(1) and then uniq(1).
605  */
606 
607 /*ARGSUSED*/
608 static bool_t
609 reply_proc(res, who, nconf)
610 	void *res;		/* Nothing comes back */
611 	struct netbuf *who;	/* Who sent us the reply */
612 	struct netconfig *nconf; /* On which transport the reply came */
613 {
614 	struct nd_hostservlist *serv;
615 	char *uaddr;
616 	char *hostname;
617 
618 	if (netdir_getbyaddr(nconf, &serv, who)) {
619 		hostname = UNKNOWN;
620 	} else {
621 		hostname = serv->h_hostservs->h_host;
622 	}
623 	if (!(uaddr = taddr2uaddr(nconf, who))) {
624 		uaddr = UNKNOWN;
625 	}
626 	(void) printf("%s\t%s\n", uaddr, hostname);
627 	if (strcmp(hostname, UNKNOWN))
628 		netdir_free((char *)serv, ND_HOSTSERVLIST);
629 	if (strcmp(uaddr, UNKNOWN))
630 		free((char *)uaddr);
631 	return (FALSE);
632 }
633 
634 static void
635 brdcst(argc, argv)
636 	int argc;
637 	char **argv;
638 {
639 	enum clnt_stat rpc_stat;
640 	ulong_t prognum, vers;
641 
642 	if (argc != 2) {
643 		usage();
644 		exit(1);
645 	}
646 	prognum = getprognum(argv[0]);
647 	vers = getvers(argv[1]);
648 	rpc_stat = rpc_broadcast(prognum, vers, NULLPROC,
649 		(xdrproc_t)xdr_void, (char *)NULL, (xdrproc_t)xdr_void,
650 		(char *)NULL, (resultproc_t)reply_proc, NULL);
651 	if ((rpc_stat != RPC_SUCCESS) && (rpc_stat != RPC_TIMEDOUT)) {
652 		(void) fprintf(stderr, "rpcinfo: broadcast failed: %s\n",
653 			clnt_sperrno(rpc_stat));
654 		exit(1);
655 	}
656 	exit(0);
657 }
658 
659 static bool_t
660 add_version(rs, vers)
661 	struct rpcbdump_short *rs;
662 	ulong_t vers;
663 {
664 	struct verslist *vl;
665 
666 	for (vl = rs->vlist; vl; vl = vl->next)
667 		if (vl->vers == vers)
668 			break;
669 	if (vl)
670 		return (TRUE);
671 	vl = (struct verslist *)malloc(sizeof (struct verslist));
672 	if (vl == NULL)
673 		return (FALSE);
674 	vl->vers = vers;
675 	vl->next = rs->vlist;
676 	rs->vlist = vl;
677 	return (TRUE);
678 }
679 
680 static bool_t
681 add_netid(rs, netid)
682 	struct rpcbdump_short *rs;
683 	char *netid;
684 {
685 	struct netidlist *nl;
686 
687 	for (nl = rs->nlist; nl; nl = nl->next)
688 		if (strcmp(nl->netid, netid) == 0)
689 			break;
690 	if (nl)
691 		return (TRUE);
692 	nl = (struct netidlist *)malloc(sizeof (struct netidlist));
693 	if (nl == NULL)
694 		return (FALSE);
695 	nl->netid = netid;
696 	nl->next = rs->nlist;
697 	rs->nlist = nl;
698 	return (TRUE);
699 }
700 
701 static void
702 rpcbdump(dumptype, netid, argc, argv)
703 	int dumptype;
704 	char *netid;
705 	int argc;
706 	char **argv;
707 {
708 	rpcblist_ptr head = NULL;
709 	struct timeval minutetimeout;
710 	register CLIENT *client;
711 	struct rpcent *rpc;
712 	char *host;
713 	struct netidlist *nl;
714 	struct verslist *vl;
715 	struct rpcbdump_short *rs, *rs_tail;
716 	enum clnt_stat clnt_st;
717 	struct rpc_err err;
718 	struct utsname utsname;
719 	struct rpcbdump_short *rs_head = NULL;
720 
721 	if (argc > 1) {
722 		usage();
723 		exit(1);
724 	}
725 	if (argc == 1) {
726 		host = argv[0];
727 	} else {
728 		(void) uname(&utsname);
729 		host = utsname.nodename;
730 	}
731 	if (netid == NULL) {
732 	    if (loopback_netid == NULL) {
733 		client = clnt_rpcbind_create(host, RPCBVERS, NULL);
734 	    } else {
735 		client = getclnthandle(host, loopback_nconf, RPCBVERS, NULL);
736 		if (client == NULL && rpc_createerr.cf_stat ==
737 				RPC_N2AXLATEFAILURE) {
738 			client = clnt_rpcbind_create(host, RPCBVERS, NULL);
739 		}
740 	    }
741 	} else {
742 		struct netconfig *nconf;
743 
744 		nconf = getnetconfigent(netid);
745 		if (nconf == NULL) {
746 			nc_perror("rpcinfo: invalid transport");
747 			exit(1);
748 		}
749 		client = getclnthandle(host, nconf, RPCBVERS, NULL);
750 		if (nconf)
751 			(void) freenetconfigent(nconf);
752 	}
753 	if (client == (CLIENT *)NULL) {
754 		clnt_pcreateerror("rpcinfo: can't contact rpcbind");
755 		exit(1);
756 	}
757 	minutetimeout.tv_sec = 60;
758 	minutetimeout.tv_usec = 0;
759 	clnt_st = CLNT_CALL(client, RPCBPROC_DUMP, (xdrproc_t)xdr_void,
760 		NULL, (xdrproc_t)xdr_rpcblist_ptr, (char *)&head,
761 		minutetimeout);
762 	if (clnt_st != RPC_SUCCESS) {
763 	    if ((clnt_st == RPC_PROGVERSMISMATCH) ||
764 		(clnt_st == RPC_PROGUNAVAIL)) {
765 		int vers;
766 
767 		CLNT_GETERR(client, &err);
768 		if (err.re_vers.low == RPCBVERS4) {
769 		    vers = RPCBVERS4;
770 		    clnt_control(client, CLSET_VERS, (char *)&vers);
771 		    clnt_st = CLNT_CALL(client, RPCBPROC_DUMP,
772 			(xdrproc_t)xdr_void, NULL,
773 			(xdrproc_t)xdr_rpcblist_ptr, (char *)&head,
774 			minutetimeout);
775 		    if (clnt_st != RPC_SUCCESS)
776 			goto failed;
777 		} else {
778 		    if (err.re_vers.high == PMAPVERS) {
779 			int high, low;
780 			pmaplist_ptr pmaphead = NULL;
781 			rpcblist_ptr list, prev = NULL;
782 
783 			vers = PMAPVERS;
784 			clnt_control(client, CLSET_VERS, (char *)&vers);
785 			clnt_st = CLNT_CALL(client, PMAPPROC_DUMP,
786 				(xdrproc_t)xdr_void, NULL,
787 				(xdrproc_t)xdr_pmaplist_ptr,
788 				(char *)&pmaphead, minutetimeout);
789 			if (clnt_st != RPC_SUCCESS)
790 				goto failed;
791 			/*
792 			 * convert to rpcblist_ptr format
793 			 */
794 			for (head = NULL; pmaphead != NULL;
795 				pmaphead = pmaphead->pml_next) {
796 			    list = (rpcblist *)malloc(sizeof (rpcblist));
797 			    if (list == NULL)
798 				goto error;
799 			    if (head == NULL)
800 				head = list;
801 			    else
802 				prev->rpcb_next = (rpcblist_ptr) list;
803 
804 			    list->rpcb_next = NULL;
805 			    list->rpcb_map.r_prog = pmaphead->pml_map.pm_prog;
806 			    list->rpcb_map.r_vers = pmaphead->pml_map.pm_vers;
807 			    if (pmaphead->pml_map.pm_prot == IPPROTO_UDP)
808 				list->rpcb_map.r_netid = "udp";
809 			    else if (pmaphead->pml_map.pm_prot == IPPROTO_TCP)
810 				list->rpcb_map.r_netid = "tcp";
811 			    else {
812 #define	MAXLONG_AS_STRING	"2147483648"
813 				list->rpcb_map.r_netid =
814 					malloc(strlen(MAXLONG_AS_STRING) + 1);
815 				if (list->rpcb_map.r_netid == NULL)
816 					goto error;
817 				(void) sprintf(list->rpcb_map.r_netid, "%6ld",
818 					pmaphead->pml_map.pm_prot);
819 			    }
820 			    list->rpcb_map.r_owner = UNKNOWN;
821 			    low = pmaphead->pml_map.pm_port & 0xff;
822 			    high = (pmaphead->pml_map.pm_port >> 8) & 0xff;
823 			    list->rpcb_map.r_addr = strdup("0.0.0.0.XXX.XXX");
824 			    (void) sprintf(&list->rpcb_map.r_addr[8], "%d.%d",
825 				high, low);
826 			    prev = list;
827 			}
828 		    }
829 		}
830 	    } else {	/* any other error */
831 failed:
832 		    clnt_perror(client, "rpcinfo: can't contact rpcbind: ");
833 		    exit(1);
834 	    }
835 	}
836 	if (head == NULL) {
837 		(void) printf("No remote programs registered.\n");
838 	} else if (dumptype == RPCBDUMP) {
839 		(void) printf(
840 "   program version netid     address             service    owner\n");
841 		for (; head != NULL; head = head->rpcb_next) {
842 			(void) printf("%10ld%5ld    ",
843 				head->rpcb_map.r_prog, head->rpcb_map.r_vers);
844 			(void) printf("%-9s ", head->rpcb_map.r_netid);
845 			(void) printf("%-19s", head->rpcb_map.r_addr);
846 			rpc = getrpcbynumber(head->rpcb_map.r_prog);
847 			if (rpc)
848 				(void) printf(" %-10s", rpc->r_name);
849 			else
850 				(void) printf(" %-10s", "-");
851 			(void) printf(" %s\n", head->rpcb_map.r_owner);
852 		}
853 	} else if (dumptype == RPCBDUMP_SHORT) {
854 		for (; head != NULL; head = head->rpcb_next) {
855 			for (rs = rs_head; rs; rs = rs->next)
856 				if (head->rpcb_map.r_prog == rs->prog)
857 					break;
858 			if (rs == NULL) {
859 				rs = (struct rpcbdump_short *)
860 					malloc(sizeof (struct rpcbdump_short));
861 				if (rs == NULL)
862 					goto error;
863 				rs->next = NULL;
864 				if (rs_head == NULL) {
865 					rs_head = rs;
866 					rs_tail = rs;
867 				} else {
868 					rs_tail->next = rs;
869 					rs_tail = rs;
870 				}
871 				rs->prog = head->rpcb_map.r_prog;
872 				rs->owner = head->rpcb_map.r_owner;
873 				rs->nlist = NULL;
874 				rs->vlist = NULL;
875 			}
876 			if (add_version(rs, head->rpcb_map.r_vers) == FALSE)
877 				goto error;
878 			if (add_netid(rs, head->rpcb_map.r_netid) == FALSE)
879 				goto error;
880 		}
881 		(void) printf(
882 "   program version(s) netid(s)                         service     owner\n");
883 		for (rs = rs_head; rs; rs = rs->next) {
884 			int bytes_trans = 0;
885 			int len;
886 
887 			(void) printf("%10ld  ", rs->prog);
888 			for (vl = rs->vlist; vl; vl = vl->next) {
889 				bytes_trans += (len = printf("%d", vl->vers))
890 				    < 0 ? 0 : len;
891 				if (vl->next)
892 					bytes_trans += (len = printf(",")) < 0
893 					    ? 0 : len;
894 			}
895 			/*
896 			 * If number of bytes transferred is less than 10,
897 			 * align 10 bytes for version(s) column. If bytes
898 			 * transferred is more than 10, add a trailing white
899 			 * space.
900 			 */
901 			if (bytes_trans < 10)
902 				(void) printf("%*s", (bytes_trans - 10), " ");
903 			else
904 				(void) printf(" ");
905 
906 			bytes_trans = 0;
907 			for (nl = rs->nlist; nl; nl = nl->next) {
908 				bytes_trans += (len = printf("%s", nl->netid))
909 				    < 0 ? 0 : len;
910 				if (nl->next)
911 					bytes_trans += (len = printf(",")) < 0
912 					    ? 0 : len;
913 			}
914 			/*
915 			 * Align netid(s) column output for 32 bytes.
916 			 */
917 			if (bytes_trans < 32)
918 				(void) printf("%*s", (bytes_trans - 32), " ");
919 
920 			rpc = getrpcbynumber(rs->prog);
921 			if (rpc)
922 				(void) printf(" %-11s", rpc->r_name);
923 			else
924 				(void) printf(" %-11s", "-");
925 			(void) printf(" %s\n", rs->owner);
926 		}
927 	}
928 	clnt_destroy(client);
929 	return;
930 
931 error:	(void) fprintf(stderr, "rpcinfo: no memory\n");
932 }
933 
934 static char nullstring[] = "\000";
935 
936 static void
937 rpcbaddrlist(netid, argc, argv)
938 	char *netid;
939 	int argc;
940 	char **argv;
941 {
942 	rpcb_entry_list_ptr head = NULL;
943 	struct timeval minutetimeout;
944 	register CLIENT *client;
945 	struct rpcent *rpc;
946 	char *host;
947 	RPCB parms;
948 	struct netbuf *targaddr;
949 
950 	if (argc != 3) {
951 		usage();
952 		exit(1);
953 	}
954 	host = argv[0];
955 	if (netid == NULL) {
956 	    if (loopback_netid == NULL) {
957 		client = clnt_rpcbind_create(host, RPCBVERS4, &targaddr);
958 	    } else {
959 		client = getclnthandle(host, loopback_nconf, RPCBVERS4,
960 			&targaddr);
961 		if (client == NULL && rpc_createerr.cf_stat ==
962 				RPC_N2AXLATEFAILURE) {
963 		    client = clnt_rpcbind_create(host, RPCBVERS4, &targaddr);
964 		}
965 	    }
966 	} else {
967 		struct netconfig *nconf;
968 
969 		nconf = getnetconfigent(netid);
970 		if (nconf == NULL) {
971 			nc_perror("rpcinfo: invalid transport");
972 			exit(1);
973 		}
974 		client = getclnthandle(host, nconf, RPCBVERS4, &targaddr);
975 		if (nconf)
976 			(void) freenetconfigent(nconf);
977 	}
978 	if (client == (CLIENT *)NULL) {
979 		clnt_pcreateerror("rpcinfo: can't contact rpcbind");
980 		exit(1);
981 	}
982 	minutetimeout.tv_sec = 60;
983 	minutetimeout.tv_usec = 0;
984 
985 	parms.r_prog = 	getprognum(argv[1]);
986 	parms.r_vers = 	getvers(argv[2]);
987 	parms.r_netid = client->cl_netid;
988 	if (targaddr == NULL) {
989 		parms.r_addr = nullstring;	/* for XDRing */
990 	} else {
991 		/*
992 		 * We also send the remote system the address we
993 		 * used to contact it in case it can help it
994 		 * connect back with us
995 		 */
996 		struct netconfig *nconf;
997 
998 		nconf = getnetconfigent(client->cl_netid);
999 		if (nconf != NULL) {
1000 			parms.r_addr = taddr2uaddr(nconf, targaddr);
1001 			if (parms.r_addr == NULL)
1002 				parms.r_addr = nullstring;
1003 			freenetconfigent(nconf);
1004 		} else {
1005 			parms.r_addr = nullstring;	/* for XDRing */
1006 		}
1007 		free(targaddr->buf);
1008 		free(targaddr);
1009 	}
1010 	parms.r_owner = nullstring;
1011 
1012 	if (CLNT_CALL(client, RPCBPROC_GETADDRLIST, (xdrproc_t)xdr_rpcb,
1013 		(char *)&parms, (xdrproc_t)xdr_rpcb_entry_list_ptr,
1014 		(char *)&head, minutetimeout) != RPC_SUCCESS) {
1015 		clnt_perror(client, "rpcinfo: can't contact rpcbind: ");
1016 		exit(1);
1017 	}
1018 	if (head == NULL) {
1019 		(void) printf("No remote programs registered.\n");
1020 	} else {
1021 		(void) printf(
1022 	"   program vers  tp_family/name/class    address\t\t  service\n");
1023 		for (; head != NULL; head = head->rpcb_entry_next) {
1024 			rpcb_entry *re;
1025 			char buf[128];
1026 
1027 			re = &head->rpcb_entry_map;
1028 			(void) printf("%10ld%3ld    ",
1029 				parms.r_prog, parms.r_vers);
1030 			(void) snprintf(buf, sizeof (buf), "%s/%s/%s ",
1031 				re->r_nc_protofmly, re->r_nc_proto,
1032 				re->r_nc_semantics == NC_TPI_CLTS ? "clts" :
1033 				re->r_nc_semantics == NC_TPI_COTS ? "cots" :
1034 						"cots_ord");
1035 			(void) printf("%-24s", buf);
1036 			(void) printf("%-24s", re->r_maddr);
1037 			rpc = getrpcbynumber(parms.r_prog);
1038 			if (rpc)
1039 				(void) printf(" %-13s", rpc->r_name);
1040 			else
1041 				(void) printf(" %-13s", "-");
1042 			(void) printf("\n");
1043 		}
1044 	}
1045 	clnt_destroy(client);
1046 }
1047 
1048 /*
1049  * monitor rpcbind
1050  */
1051 static void
1052 rpcbgetstat(argc, argv)
1053 	int argc;
1054 	char **argv;
1055 {
1056 	rpcb_stat_byvers inf;
1057 	struct timeval minutetimeout;
1058 	register CLIENT *client;
1059 	char *host;
1060 	int i, j;
1061 	rpcbs_addrlist *pa;
1062 	rpcbs_rmtcalllist *pr;
1063 	int cnt, flen;
1064 	struct utsname utsname;
1065 #define	MAXFIELD	64
1066 	char fieldbuf[MAXFIELD];
1067 #define	MAXLINE		256
1068 	char linebuf[MAXLINE];
1069 	char *cp, *lp;
1070 	char *pmaphdr[] = {
1071 		"NULL", "SET", "UNSET", "GETPORT",
1072 		"DUMP", "CALLIT"
1073 	};
1074 	char *rpcb3hdr[] = {
1075 		"NULL", "SET", "UNSET", "GETADDR", "DUMP", "CALLIT", "TIME",
1076 		"U2T", "T2U"
1077 	};
1078 	char *rpcb4hdr[] = {
1079 		"NULL", "SET", "UNSET", "GETADDR", "DUMP", "CALLIT", "TIME",
1080 		"U2T",  "T2U", "VERADDR", "INDRECT", "GETLIST", "GETSTAT"
1081 	};
1082 
1083 #define	TABSTOP	8
1084 
1085 	if (argc >= 1) {
1086 		host = argv[0];
1087 	} else {
1088 		(void) uname(&utsname);
1089 		host = utsname.nodename;
1090 	}
1091 	if (loopback_netid != NULL) {
1092 		client = getclnthandle(host, loopback_nconf, RPCBVERS4, NULL);
1093 		if (client == NULL && rpc_createerr.cf_stat ==
1094 				RPC_N2AXLATEFAILURE) {
1095 			client = clnt_rpcbind_create(host, RPCBVERS4, NULL);
1096 		}
1097 	} else {
1098 		client = clnt_rpcbind_create(host, RPCBVERS4, NULL);
1099 	}
1100 	if (client == (CLIENT *)NULL) {
1101 		clnt_pcreateerror("rpcinfo: can't contact rpcbind");
1102 		exit(1);
1103 	}
1104 	minutetimeout.tv_sec = 60;
1105 	minutetimeout.tv_usec = 0;
1106 	(void) memset((char *)&inf, 0, sizeof (rpcb_stat_byvers));
1107 	if (CLNT_CALL(client, RPCBPROC_GETSTAT, (xdrproc_t)xdr_void, NULL,
1108 		(xdrproc_t)xdr_rpcb_stat_byvers, (char *)&inf, minutetimeout)
1109 			!= RPC_SUCCESS) {
1110 		clnt_perror(client, "rpcinfo: can't contact rpcbind: ");
1111 		exit(1);
1112 	}
1113 	(void) printf("PORTMAP (version 2) statistics\n");
1114 	lp = linebuf;
1115 	for (i = 0; i <= rpcb_highproc_2; i++) {
1116 		fieldbuf[0] = '\0';
1117 		switch (i) {
1118 		case PMAPPROC_SET:
1119 			(void) sprintf(fieldbuf, "%d/",
1120 				inf[RPCBVERS_2_STAT].setinfo);
1121 			break;
1122 		case PMAPPROC_UNSET:
1123 			(void) sprintf(fieldbuf, "%d/",
1124 				inf[RPCBVERS_2_STAT].unsetinfo);
1125 			break;
1126 		case PMAPPROC_GETPORT:
1127 			cnt = 0;
1128 			for (pa = inf[RPCBVERS_2_STAT].addrinfo; pa;
1129 				pa = pa->next)
1130 				cnt += pa->success;
1131 			(void) sprintf(fieldbuf, "%d/", cnt);
1132 			break;
1133 		case PMAPPROC_CALLIT:
1134 			cnt = 0;
1135 			for (pr = inf[RPCBVERS_2_STAT].rmtinfo; pr;
1136 				pr = pr->next)
1137 				cnt += pr->success;
1138 			(void) sprintf(fieldbuf, "%d/", cnt);
1139 			break;
1140 		default: break;  /* For the remaining ones */
1141 		}
1142 		cp = &fieldbuf[0] + strlen(fieldbuf);
1143 		(void) sprintf(cp, "%d", inf[RPCBVERS_2_STAT].info[i]);
1144 		flen = strlen(fieldbuf);
1145 		(void) printf("%s%s", pmaphdr[i],
1146 			spaces((int)((TABSTOP * (1 + flen / TABSTOP))
1147 			- strlen(pmaphdr[i]))));
1148 		(void) snprintf(lp, (MAXLINE - (lp - linebuf)), "%s%s",
1149 			fieldbuf, spaces(cnt = ((TABSTOP * (1 + flen / TABSTOP))
1150 			- flen)));
1151 		lp += (flen + cnt);
1152 	}
1153 	(void) printf("\n%s\n\n", linebuf);
1154 
1155 	if (inf[RPCBVERS_2_STAT].info[PMAPPROC_CALLIT]) {
1156 		(void) printf("PMAP_RMTCALL call statistics\n");
1157 		print_rmtcallstat(RPCBVERS_2_STAT, &inf[RPCBVERS_2_STAT]);
1158 		(void) printf("\n");
1159 	}
1160 
1161 	if (inf[RPCBVERS_2_STAT].info[PMAPPROC_GETPORT]) {
1162 		(void) printf("PMAP_GETPORT call statistics\n");
1163 		print_getaddrstat(RPCBVERS_2_STAT, &inf[RPCBVERS_2_STAT]);
1164 		(void) printf("\n");
1165 	}
1166 
1167 	(void) printf("RPCBIND (version 3) statistics\n");
1168 	lp = linebuf;
1169 	for (i = 0; i <= rpcb_highproc_3; i++) {
1170 		fieldbuf[0] = '\0';
1171 		switch (i) {
1172 		case RPCBPROC_SET:
1173 			(void) sprintf(fieldbuf, "%d/",
1174 				inf[RPCBVERS_3_STAT].setinfo);
1175 			break;
1176 		case RPCBPROC_UNSET:
1177 			(void) sprintf(fieldbuf, "%d/",
1178 				inf[RPCBVERS_3_STAT].unsetinfo);
1179 			break;
1180 		case RPCBPROC_GETADDR:
1181 			cnt = 0;
1182 			for (pa = inf[RPCBVERS_3_STAT].addrinfo; pa;
1183 				pa = pa->next)
1184 				cnt += pa->success;
1185 			(void) sprintf(fieldbuf, "%d/", cnt);
1186 			break;
1187 		case RPCBPROC_CALLIT:
1188 			cnt = 0;
1189 			for (pr = inf[RPCBVERS_3_STAT].rmtinfo; pr;
1190 				pr = pr->next)
1191 				cnt += pr->success;
1192 			(void) sprintf(fieldbuf, "%d/", cnt);
1193 			break;
1194 		default: break;  /* For the remaining ones */
1195 		}
1196 		cp = &fieldbuf[0] + strlen(fieldbuf);
1197 		(void) sprintf(cp, "%d", inf[RPCBVERS_3_STAT].info[i]);
1198 		flen = strlen(fieldbuf);
1199 		(void) printf("%s%s", rpcb3hdr[i],
1200 			spaces((int)((TABSTOP * (1 + flen / TABSTOP))
1201 			- strlen(rpcb3hdr[i]))));
1202 		(void) snprintf(lp, (MAXLINE - (lp - linebuf)), "%s%s",
1203 			fieldbuf, spaces(cnt = ((TABSTOP * (1 + flen / TABSTOP))
1204 			- flen)));
1205 		lp += (flen + cnt);
1206 	}
1207 	(void) printf("\n%s\n\n", linebuf);
1208 
1209 	if (inf[RPCBVERS_3_STAT].info[RPCBPROC_CALLIT]) {
1210 		(void) printf("RPCB_RMTCALL (version 3) call statistics\n");
1211 		print_rmtcallstat(RPCBVERS_3_STAT, &inf[RPCBVERS_3_STAT]);
1212 		(void) printf("\n");
1213 	}
1214 
1215 	if (inf[RPCBVERS_3_STAT].info[RPCBPROC_GETADDR]) {
1216 		(void) printf("RPCB_GETADDR (version 3) call statistics\n");
1217 		print_getaddrstat(RPCBVERS_3_STAT, &inf[RPCBVERS_3_STAT]);
1218 		(void) printf("\n");
1219 	}
1220 
1221 	(void) printf("RPCBIND (version 4) statistics\n");
1222 
1223 	for (j = 0; j <= 9; j += 9) { /* Just two iterations for printing */
1224 		lp = linebuf;
1225 		for (i = j; i <= MAX(8, rpcb_highproc_4 - 9 + j); i++) {
1226 			fieldbuf[0] = '\0';
1227 			switch (i) {
1228 			case RPCBPROC_SET:
1229 				(void) sprintf(fieldbuf, "%d/",
1230 					inf[RPCBVERS_4_STAT].setinfo);
1231 				break;
1232 			case RPCBPROC_UNSET:
1233 				(void) sprintf(fieldbuf, "%d/",
1234 					inf[RPCBVERS_4_STAT].unsetinfo);
1235 				break;
1236 			case RPCBPROC_GETADDR:
1237 				cnt = 0;
1238 				for (pa = inf[RPCBVERS_4_STAT].addrinfo; pa;
1239 					pa = pa->next)
1240 					cnt += pa->success;
1241 				(void) sprintf(fieldbuf, "%d/", cnt);
1242 				break;
1243 			case RPCBPROC_CALLIT:
1244 				cnt = 0;
1245 				for (pr = inf[RPCBVERS_4_STAT].rmtinfo; pr;
1246 					pr = pr->next)
1247 					cnt += pr->success;
1248 				(void) sprintf(fieldbuf, "%d/", cnt);
1249 				break;
1250 			default: break;  /* For the remaining ones */
1251 			}
1252 			cp = &fieldbuf[0] + strlen(fieldbuf);
1253 			/*
1254 			 * XXX: We also add RPCBPROC_GETADDRLIST queries to
1255 			 * RPCB_GETADDR because rpcbind includes the
1256 			 * RPCB_GETADDRLIST successes in RPCB_GETADDR.
1257 			 */
1258 			if (i != RPCBPROC_GETADDR)
1259 			    (void) sprintf(cp, "%d",
1260 				inf[RPCBVERS_4_STAT].info[i]);
1261 			else
1262 			    (void) sprintf(cp, "%d",
1263 			    inf[RPCBVERS_4_STAT].info[i] +
1264 			    inf[RPCBVERS_4_STAT].info[RPCBPROC_GETADDRLIST]);
1265 			flen = strlen(fieldbuf);
1266 			(void) printf("%s%s", rpcb4hdr[i],
1267 				spaces((int)((TABSTOP * (1 + flen / TABSTOP))
1268 				- strlen(rpcb4hdr[i]))));
1269 			(void) snprintf(lp, MAXLINE - (lp - linebuf), "%s%s",
1270 				fieldbuf, spaces(cnt =
1271 				((TABSTOP * (1 + flen / TABSTOP)) - flen)));
1272 			lp += (flen + cnt);
1273 		}
1274 		(void) printf("\n%s\n", linebuf);
1275 	}
1276 
1277 	if (inf[RPCBVERS_4_STAT].info[RPCBPROC_CALLIT] ||
1278 			    inf[RPCBVERS_4_STAT].info[RPCBPROC_INDIRECT]) {
1279 		(void) printf("\n");
1280 		(void) printf("RPCB_RMTCALL (version 4) call statistics\n");
1281 		print_rmtcallstat(RPCBVERS_4_STAT, &inf[RPCBVERS_4_STAT]);
1282 	}
1283 
1284 	if (inf[RPCBVERS_4_STAT].info[RPCBPROC_GETADDR]) {
1285 		(void) printf("\n");
1286 		(void) printf("RPCB_GETADDR (version 4) call statistics\n");
1287 		print_getaddrstat(RPCBVERS_4_STAT, &inf[RPCBVERS_4_STAT]);
1288 	}
1289 	clnt_destroy(client);
1290 }
1291 
1292 /*
1293  * Delete registeration for this (prog, vers, netid)
1294  */
1295 static void
1296 deletereg(netid, argc, argv)
1297 	char *netid;
1298 	int argc;
1299 	char **argv;
1300 {
1301 	struct netconfig *nconf = NULL;
1302 
1303 	if (argc != 2) {
1304 		usage();
1305 		exit(1);
1306 	}
1307 	if (netid) {
1308 		nconf = getnetconfigent(netid);
1309 		if (nconf == NULL) {
1310 			(void) fprintf(stderr,
1311 				"rpcinfo: netid %s not supported\n", netid);
1312 			exit(1);
1313 		}
1314 	}
1315 	if ((rpcb_unset(getprognum(argv[0]), getvers(argv[1]), nconf)) == 0) {
1316 		(void) fprintf(stderr,
1317 	"rpcinfo: Could not delete registration for prog %s version %s\n",
1318 			argv[0], argv[1]);
1319 		exit(1);
1320 	}
1321 }
1322 
1323 /*
1324  * Create and return a handle for the given nconf.
1325  * Exit if cannot create handle.
1326  */
1327 static CLIENT *
1328 clnt_addr_create(address, nconf, prog, vers)
1329 	char *address;
1330 	struct netconfig *nconf;
1331 	ulong_t prog;
1332 	ulong_t vers;
1333 {
1334 	CLIENT *client;
1335 	static struct netbuf *nbuf;
1336 	static int fd = RPC_ANYFD;
1337 	struct t_info tinfo;
1338 
1339 	if (fd == RPC_ANYFD) {
1340 		if ((fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) == -1) {
1341 			rpc_createerr.cf_stat = RPC_TLIERROR;
1342 			rpc_createerr.cf_error.re_terrno = t_errno;
1343 			clnt_pcreateerror("rpcinfo");
1344 			exit(1);
1345 		}
1346 		/* Convert the uaddr to taddr */
1347 		nbuf = uaddr2taddr(nconf, address);
1348 		if (nbuf == NULL) {
1349 			netdir_perror("rpcinfo");
1350 			exit(1);
1351 		}
1352 	}
1353 	client = clnt_tli_create(fd, nconf, nbuf, prog, vers, 0, 0);
1354 	if (client == (CLIENT *)NULL) {
1355 		clnt_pcreateerror("rpcinfo");
1356 		exit(1);
1357 	}
1358 	return (client);
1359 }
1360 
1361 /*
1362  * If the version number is given, ping that (prog, vers); else try to find
1363  * the version numbers supported for that prog and ping all the versions.
1364  * Remote rpcbind is not contacted for this service. The requests are
1365  * sent directly to the services themselves.
1366  */
1367 static void
1368 addrping(address, netid, argc, argv)
1369 	char *address;
1370 	char *netid;
1371 	int argc;
1372 	char **argv;
1373 {
1374 	CLIENT *client;
1375 	struct timeval to;
1376 	enum clnt_stat rpc_stat;
1377 	ulong_t prognum, versnum, minvers, maxvers;
1378 	struct rpc_err rpcerr;
1379 	int failure = 0;
1380 	struct netconfig *nconf;
1381 	int fd;
1382 
1383 	if (argc < 1 || argc > 2 || (netid == NULL)) {
1384 		usage();
1385 		exit(1);
1386 	}
1387 	nconf = getnetconfigent(netid);
1388 	if (nconf == (struct netconfig *)NULL) {
1389 		(void) fprintf(stderr, "rpcinfo: Could not find %s\n", netid);
1390 		exit(1);
1391 	}
1392 	to.tv_sec = 10;
1393 	to.tv_usec = 0;
1394 	prognum = getprognum(argv[0]);
1395 	if (argc == 1) {	/* Version number not known */
1396 		/*
1397 		 * A call to version 0 should fail with a program/version
1398 		 * mismatch, and give us the range of versions supported.
1399 		 */
1400 		versnum = MIN_VERS;
1401 	} else {
1402 		versnum = getvers(argv[1]);
1403 	}
1404 	client = clnt_addr_create(address, nconf, prognum, versnum);
1405 	rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t)xdr_void,
1406 			(char *)NULL, (xdrproc_t)xdr_void,
1407 			(char *)NULL, to);
1408 	if (argc == 2) {
1409 		/* Version number was known */
1410 		if (pstatus(client, prognum, versnum) < 0)
1411 			failure = 1;
1412 		(void) CLNT_DESTROY(client);
1413 		if (failure)
1414 			exit(1);
1415 		return;
1416 	}
1417 	/* Version number not known */
1418 	(void) CLNT_CONTROL(client, CLSET_FD_NCLOSE, (char *)NULL);
1419 	(void) CLNT_CONTROL(client, CLGET_FD, (char *)&fd);
1420 	if (rpc_stat == RPC_PROGVERSMISMATCH) {
1421 		clnt_geterr(client, &rpcerr);
1422 		minvers = rpcerr.re_vers.low;
1423 		maxvers = rpcerr.re_vers.high;
1424 	} else if (rpc_stat == RPC_SUCCESS) {
1425 		/*
1426 		 * Oh dear, it DOES support version 0.
1427 		 * Let's try version MAX_VERS.
1428 		 */
1429 		(void) CLNT_DESTROY(client);
1430 		client = clnt_addr_create(address, nconf, prognum, MAX_VERS);
1431 		rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t)xdr_void,
1432 				(char *)NULL, (xdrproc_t)xdr_void,
1433 				(char *)NULL, to);
1434 		if (rpc_stat == RPC_PROGVERSMISMATCH) {
1435 			clnt_geterr(client, &rpcerr);
1436 			minvers = rpcerr.re_vers.low;
1437 			maxvers = rpcerr.re_vers.high;
1438 		} else if (rpc_stat == RPC_SUCCESS) {
1439 			/*
1440 			 * It also supports version MAX_VERS.
1441 			 * Looks like we have a wise guy.
1442 			 * OK, we give them information on all
1443 			 * 4 billion versions they support...
1444 			 */
1445 			minvers = 0;
1446 			maxvers = MAX_VERS;
1447 		} else {
1448 			(void) pstatus(client, prognum, MAX_VERS);
1449 			exit(1);
1450 		}
1451 	} else {
1452 		(void) pstatus(client, prognum, (ulong_t)0);
1453 		exit(1);
1454 	}
1455 	(void) CLNT_DESTROY(client);
1456 	for (versnum = minvers; versnum <= maxvers; versnum++) {
1457 		client = clnt_addr_create(address, nconf, prognum, versnum);
1458 		rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t)xdr_void,
1459 				(char *)NULL, (xdrproc_t)xdr_void,
1460 				(char *)NULL, to);
1461 		if (pstatus(client, prognum, versnum) < 0)
1462 				failure = 1;
1463 		(void) CLNT_DESTROY(client);
1464 	}
1465 	(void) t_close(fd);
1466 	if (failure)
1467 		exit(1);
1468 }
1469 
1470 /*
1471  * If the version number is given, ping that (prog, vers); else try to find
1472  * the version numbers supported for that prog and ping all the versions.
1473  * Remote rpcbind is *contacted* for this service. The requests are
1474  * then sent directly to the services themselves.
1475  */
1476 static void
1477 progping(netid, argc, argv)
1478 	char *netid;
1479 	int argc;
1480 	char **argv;
1481 {
1482 	CLIENT *client;
1483 	struct timeval to;
1484 	enum clnt_stat rpc_stat;
1485 	ulong_t prognum, versnum, minvers, maxvers;
1486 	struct rpc_err rpcerr;
1487 	int failure = 0;
1488 	struct netconfig *nconf;
1489 
1490 	if (argc < 2 || argc > 3 || (netid == NULL)) {
1491 		usage();
1492 		exit(1);
1493 	}
1494 	prognum = getprognum(argv[1]);
1495 	if (argc == 2) { /* Version number not known */
1496 		/*
1497 		 * A call to version 0 should fail with a program/version
1498 		 * mismatch, and give us the range of versions supported.
1499 		 */
1500 		versnum = MIN_VERS;
1501 	} else {
1502 		versnum = getvers(argv[2]);
1503 	}
1504 	if (netid) {
1505 		nconf = getnetconfigent(netid);
1506 		if (nconf == (struct netconfig *)NULL) {
1507 			(void) fprintf(stderr,
1508 				"rpcinfo: Could not find %s\n", netid);
1509 			exit(1);
1510 		}
1511 		client = clnt_tp_create(argv[0], prognum, versnum, nconf);
1512 	} else {
1513 		client = clnt_create(argv[0], prognum, versnum, "NETPATH");
1514 	}
1515 	if (client == (CLIENT *)NULL) {
1516 		clnt_pcreateerror("rpcinfo");
1517 		exit(1);
1518 	}
1519 	to.tv_sec = 10;
1520 	to.tv_usec = 0;
1521 	rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t)xdr_void,
1522 			(char *)NULL, (xdrproc_t)xdr_void,
1523 			(char *)NULL, to);
1524 	if (argc == 3) {
1525 		/* Version number was known */
1526 		if (pstatus(client, prognum, versnum) < 0)
1527 			failure = 1;
1528 		(void) CLNT_DESTROY(client);
1529 		if (failure)
1530 			exit(1);
1531 		return;
1532 	}
1533 	/* Version number not known */
1534 	if (rpc_stat == RPC_PROGVERSMISMATCH) {
1535 		clnt_geterr(client, &rpcerr);
1536 		minvers = rpcerr.re_vers.low;
1537 		maxvers = rpcerr.re_vers.high;
1538 	} else if (rpc_stat == RPC_SUCCESS) {
1539 		/*
1540 		 * Oh dear, it DOES support version 0.
1541 		 * Let's try version MAX_VERS.
1542 		 */
1543 		versnum = MAX_VERS;
1544 		(void) CLNT_CONTROL(client, CLSET_VERS, (char *)&versnum);
1545 		rpc_stat = CLNT_CALL(client, NULLPROC,
1546 				(xdrproc_t)xdr_void, (char *)NULL,
1547 				(xdrproc_t)xdr_void, (char *)NULL, to);
1548 		if (rpc_stat == RPC_PROGVERSMISMATCH) {
1549 			clnt_geterr(client, &rpcerr);
1550 			minvers = rpcerr.re_vers.low;
1551 			maxvers = rpcerr.re_vers.high;
1552 		} else if (rpc_stat == RPC_SUCCESS) {
1553 			/*
1554 			 * It also supports version MAX_VERS.
1555 			 * Looks like we have a wise guy.
1556 			 * OK, we give them information on all
1557 			 * 4 billion versions they support...
1558 			 */
1559 			minvers = 0;
1560 			maxvers = MAX_VERS;
1561 		} else {
1562 			(void) pstatus(client, prognum, MAX_VERS);
1563 			exit(1);
1564 		}
1565 	} else {
1566 		(void) pstatus(client, prognum, (ulong_t)0);
1567 		exit(1);
1568 	}
1569 	for (versnum = minvers; versnum <= maxvers; versnum++) {
1570 		(void) CLNT_CONTROL(client, CLSET_VERS, (char *)&versnum);
1571 		rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t)xdr_void,
1572 					(char *)NULL, (xdrproc_t)xdr_void,
1573 					(char *)NULL, to);
1574 		if (pstatus(client, prognum, versnum) < 0)
1575 				failure = 1;
1576 	}
1577 	(void) CLNT_DESTROY(client);
1578 	if (failure)
1579 		exit(1);
1580 }
1581 
1582 static void
1583 usage()
1584 {
1585 	(void) fprintf(stderr, "Usage: rpcinfo [-m | -s] [host]\n");
1586 #ifdef PORTMAP
1587 	(void) fprintf(stderr, "       rpcinfo -p [host]\n");
1588 #endif
1589 	(void) fprintf(stderr,
1590 	    "       rpcinfo -T netid host prognum [versnum]\n");
1591 	(void) fprintf(stderr, "       rpcinfo -l host prognum versnum\n");
1592 #ifdef PORTMAP
1593 	(void) fprintf(stderr,
1594 "       rpcinfo [-n portnum] -u | -t host prognum [versnum]\n");
1595 #endif
1596 	(void) fprintf(stderr,
1597 "       rpcinfo -a serv_address -T netid prognum [version]\n");
1598 	(void) fprintf(stderr, "       rpcinfo -b prognum versnum\n");
1599 	(void) fprintf(stderr,
1600 	    "       rpcinfo -d [-T netid] prognum versnum\n");
1601 }
1602 
1603 static ulong_t
1604 getprognum  (arg)
1605 	char *arg;
1606 {
1607 	char *strptr;
1608 	register struct rpcent *rpc;
1609 	register ulong_t prognum;
1610 	char *tptr = arg;
1611 
1612 	while (*tptr && isdigit(*tptr++));
1613 	if (*tptr || isalpha(*(tptr - 1))) {
1614 		rpc = getrpcbyname(arg);
1615 		if (rpc == NULL) {
1616 			(void) fprintf(stderr,
1617 				"rpcinfo: %s is unknown service\n", arg);
1618 			exit(1);
1619 		}
1620 		prognum = rpc->r_number;
1621 	} else {
1622 		prognum = strtol(arg, &strptr, 10);
1623 		if (strptr == arg || *strptr != '\0') {
1624 			(void) fprintf(stderr,
1625 		"rpcinfo: %s is illegal program number\n", arg);
1626 			exit(1);
1627 		}
1628 	}
1629 	return (prognum);
1630 }
1631 
1632 static ulong_t
1633 getvers(arg)
1634 	char *arg;
1635 {
1636 	char *strptr;
1637 	register ulong_t vers;
1638 
1639 	vers = (int)strtol(arg, &strptr, 10);
1640 	if (strptr == arg || *strptr != '\0') {
1641 		(void) fprintf(stderr,
1642 			"rpcinfo: %s is illegal version number\n", arg);
1643 		exit(1);
1644 	}
1645 	return (vers);
1646 }
1647 
1648 /*
1649  * This routine should take a pointer to an "rpc_err" structure, rather than
1650  * a pointer to a CLIENT structure, but "clnt_perror" takes a pointer to
1651  * a CLIENT structure rather than a pointer to an "rpc_err" structure.
1652  * As such, we have to keep the CLIENT structure around in order to print
1653  * a good error message.
1654  */
1655 static int
1656 pstatus(client, prog, vers)
1657 	register CLIENT *client;
1658 	ulong_t prog;
1659 	ulong_t vers;
1660 {
1661 	struct rpc_err rpcerr;
1662 
1663 	clnt_geterr(client, &rpcerr);
1664 	if (rpcerr.re_status != RPC_SUCCESS) {
1665 		clnt_perror(client, "rpcinfo");
1666 		(void) printf("program %lu version %lu is not available\n",
1667 			prog, vers);
1668 		return (-1);
1669 	} else {
1670 		(void) printf("program %lu version %lu ready and waiting\n",
1671 			prog, vers);
1672 		return (0);
1673 	}
1674 }
1675 
1676 static CLIENT *
1677 clnt_rpcbind_create(host, rpcbversnum, targaddr)
1678 	char *host;
1679 	ulong_t rpcbversnum;
1680 	struct netbuf **targaddr;
1681 {
1682 	static char *tlist[3] = {
1683 		"circuit_n", "circuit_v", "datagram_v"
1684 	};
1685 	int i;
1686 	struct netconfig *nconf;
1687 	CLIENT *clnt = NULL;
1688 	void *handle;
1689 
1690 	rpc_createerr.cf_stat = RPC_SUCCESS;
1691 	for (i = 0; i < 3; i++) {
1692 		if ((handle = __rpc_setconf(tlist[i])) == NULL)
1693 			continue;
1694 		while (clnt == (CLIENT *)NULL) {
1695 			if ((nconf = __rpc_getconf(handle)) == NULL) {
1696 				if (rpc_createerr.cf_stat == RPC_SUCCESS)
1697 				    rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1698 				break;
1699 			}
1700 			clnt = getclnthandle(host, nconf, rpcbversnum,
1701 					targaddr);
1702 		}
1703 		if (clnt)
1704 			break;
1705 		__rpc_endconf(handle);
1706 	}
1707 	return (clnt);
1708 }
1709 
1710 static CLIENT*
1711 getclnthandle(host, nconf, rpcbversnum, targaddr)
1712 	char *host;
1713 	struct netconfig *nconf;
1714 	ulong_t rpcbversnum;
1715 	struct netbuf **targaddr;
1716 {
1717 	struct netbuf *addr;
1718 	struct nd_addrlist *nas;
1719 	struct nd_hostserv rpcbind_hs;
1720 	CLIENT *client = NULL;
1721 
1722 	/* Get the address of the rpcbind */
1723 	rpcbind_hs.h_host = host;
1724 	rpcbind_hs.h_serv = "rpcbind";
1725 	if (netdir_getbyname(nconf, &rpcbind_hs, &nas)) {
1726 		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
1727 		return (NULL);
1728 	}
1729 	addr = nas->n_addrs;
1730 	client = clnt_tli_create(RPC_ANYFD, nconf, addr, RPCBPROG,
1731 			rpcbversnum, 0, 0);
1732 	if (client) {
1733 		if (targaddr != NULL) {
1734 			*targaddr =
1735 			    (struct netbuf *)malloc(sizeof (struct netbuf));
1736 			if (*targaddr != NULL) {
1737 				(*targaddr)->maxlen = addr->maxlen;
1738 				(*targaddr)->len = addr->len;
1739 				(*targaddr)->buf = (char *)malloc(addr->len);
1740 				if ((*targaddr)->buf != NULL) {
1741 					(void) memcpy((*targaddr)->buf,
1742 						addr->buf, addr->len);
1743 				}
1744 			}
1745 		}
1746 	} else {
1747 		if (rpc_createerr.cf_stat == RPC_TLIERROR) {
1748 			/*
1749 			 * Assume that the other system is dead; this is a
1750 			 * better error to display to the user.
1751 			 */
1752 			rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1753 			rpc_createerr.cf_error.re_status = RPC_FAILED;
1754 		}
1755 	}
1756 	netdir_free((char *)nas, ND_ADDRLIST);
1757 	return (client);
1758 }
1759 
1760 static void
1761 print_rmtcallstat(rtype, infp)
1762 	int rtype;
1763 	rpcb_stat *infp;
1764 {
1765 	register rpcbs_rmtcalllist_ptr pr;
1766 	struct rpcent *rpc;
1767 
1768 	if (rtype == RPCBVERS_4_STAT)
1769 		(void) printf(
1770 		"prog\t\tvers\tproc\tnetid\tindirect success failure\n");
1771 	else
1772 		(void) printf("prog\t\tvers\tproc\tnetid\tsuccess\tfailure\n");
1773 	for (pr = infp->rmtinfo; pr; pr = pr->next) {
1774 		rpc = getrpcbynumber(pr->prog);
1775 		if (rpc)
1776 			(void) printf("%-16s", rpc->r_name);
1777 		else
1778 #if defined(_LP64) || defined(_I32LPx)
1779 			(void) printf("%-16u", pr->prog);
1780 		(void) printf("%u\t%u\t%-7s ",
1781 #else
1782 			(void) printf("%-16lu", pr->prog);
1783 		(void) printf("%lu\t%lu\t%-7s ",
1784 #endif
1785 			pr->vers, pr->proc, pr->netid);
1786 		if (rtype == RPCBVERS_4_STAT)
1787 			(void) printf("%d\t ", pr->indirect);
1788 		(void) printf("%d\t%d\n", pr->success, pr->failure);
1789 	}
1790 }
1791 
1792 static void
1793 /* LINTED E_FUNC_ARG_UNUSED for 1st arg rtype */
1794 print_getaddrstat(rtype, infp)
1795 	int rtype;
1796 	rpcb_stat *infp;
1797 {
1798 	rpcbs_addrlist_ptr al;
1799 	register struct rpcent *rpc;
1800 
1801 	(void) printf("prog\t\tvers\tnetid\t  success\tfailure\n");
1802 	for (al = infp->addrinfo; al; al = al->next) {
1803 		rpc = getrpcbynumber(al->prog);
1804 		if (rpc)
1805 			(void) printf("%-16s", rpc->r_name);
1806 		else
1807 #if defined(_LP64) || defined(_I32LPx)
1808 			(void) printf("%-16u", al->prog);
1809 		(void) printf("%u\t%-9s %-12d\t%d\n",
1810 #else
1811 			(void) printf("%-16lu", al->prog);
1812 		(void) printf("%lu\t%-9s %-12d\t%d\n",
1813 #endif
1814 			al->vers, al->netid,
1815 			al->success, al->failure);
1816 	}
1817 }
1818 
1819 static char *
1820 spaces(howmany)
1821 int howmany;
1822 {
1823 	static char space_array[] =		/* 64 spaces */
1824 	"                                                                ";
1825 
1826 	if (howmany <= 0 || howmany > sizeof (space_array)) {
1827 		return ("");
1828 	}
1829 	return (&space_array[sizeof (space_array) - howmany - 1]);
1830 }
1831