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