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