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