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