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