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