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