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