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