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