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: stable/11/usr.bin/rpcinfo/rpcinfo.c 317438 2017-04-26 14:43:21Z asomers $");
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 == INADDR_NONE ||
567	    addr->sin_addr.s_addr == INADDR_ANY) {
568		if ((nconf = __rpc_getconfip("udp")) == NULL &&
569		    (nconf = __rpc_getconfip("tcp")) == NULL)
570			errx(1, "couldn't find a suitable transport");
571		else {
572			memset(&hints, 0, sizeof hints);
573			hints.ai_family = AF_INET;
574			if ((error = getaddrinfo(host, "rpcbind", &hints, &res))
575			    != 0)
576				errx(1, "%s: %s", host, gai_strerror(error));
577			else {
578				memcpy(addr, res->ai_addr, res->ai_addrlen);
579				freeaddrinfo(res);
580			}
581			(void) freenetconfigent(nconf);
582		}
583	} else {
584		addr->sin_family = AF_INET;
585	}
586}
587#endif /* PORTMAP */
588
589/*
590 * reply_proc collects replies from the broadcast.
591 * to get a unique list of responses the output of rpcinfo should
592 * be piped through sort(1) and then uniq(1).
593 */
594
595/*ARGSUSED*/
596static bool_t
597reply_proc(void *res, struct netbuf *who, struct netconfig *nconf)
598	/* void *res;			Nothing comes back */
599	/* struct netbuf *who;		Who sent us the reply */
600	/* struct netconfig *nconf; 	On which transport the reply came */
601{
602	char *uaddr;
603	char hostbuf[NI_MAXHOST];
604	const char *hostname;
605	struct sockaddr *sa = (struct sockaddr *)who->buf;
606
607	if (getnameinfo(sa, sa->sa_len, hostbuf, NI_MAXHOST, NULL, 0, 0)) {
608		hostname = UNKNOWN;
609	} else {
610		hostname = hostbuf;
611	}
612	uaddr = taddr2uaddr(nconf, who);
613	if (uaddr == NULL) {
614		printf("%s\t%s\n", UNKNOWN, hostname);
615	} else {
616		printf("%s\t%s\n", uaddr, hostname);
617		free((char *)uaddr);
618	}
619	return (FALSE);
620}
621
622static void
623brdcst(int argc, char **argv)
624{
625	enum clnt_stat rpc_stat;
626	u_long prognum, vers;
627
628	if (argc != 2)
629		usage();
630	prognum = getprognum(argv[0]);
631	vers = getvers(argv[1]);
632	rpc_stat = rpc_broadcast(prognum, vers, NULLPROC,
633		(xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_void,
634		(char *)NULL, (resultproc_t) reply_proc, NULL);
635	if ((rpc_stat != RPC_SUCCESS) && (rpc_stat != RPC_TIMEDOUT))
636		errx(1, "broadcast failed: %s", clnt_sperrno(rpc_stat));
637	exit(0);
638}
639
640static bool_t
641add_version(struct rpcbdump_short *rs, u_long vers)
642{
643	struct verslist *vl;
644
645	for (vl = rs->vlist; vl; vl = vl->next)
646		if (vl->vers == vers)
647			break;
648	if (vl)
649		return (TRUE);
650	vl = (struct verslist *)malloc(sizeof (struct verslist));
651	if (vl == NULL)
652		return (FALSE);
653	vl->vers = vers;
654	vl->next = rs->vlist;
655	rs->vlist = vl;
656	return (TRUE);
657}
658
659static bool_t
660add_netid(struct rpcbdump_short *rs, char *netid)
661{
662	struct netidlist *nl;
663
664	for (nl = rs->nlist; nl; nl = nl->next)
665		if (strcmp(nl->netid, netid) == 0)
666			break;
667	if (nl)
668		return (TRUE);
669	nl = (struct netidlist *)malloc(sizeof (struct netidlist));
670	if (nl == NULL)
671		return (FALSE);
672	nl->netid = netid;
673	nl->next = rs->nlist;
674	rs->nlist = nl;
675	return (TRUE);
676}
677
678static void
679rpcbdump(int dumptype, char *netid, int argc, char **argv)
680{
681	rpcblist_ptr head = NULL;
682	struct timeval minutetimeout;
683	register CLIENT *client;
684	struct rpcent *rpc;
685	char *host;
686	struct netidlist *nl;
687	struct verslist *vl;
688	struct rpcbdump_short *rs, *rs_tail;
689	char buf[256];
690	enum clnt_stat clnt_st;
691	struct rpc_err err;
692	struct rpcbdump_short *rs_head = NULL;
693
694	if (argc > 1)
695		usage();
696	if (argc == 1) {
697		host = argv[0];
698		if (netid == NULL) {
699			client = clnt_rpcbind_create(host, RPCBVERS, NULL);
700		} else {
701			struct netconfig *nconf;
702
703			nconf = getnetconfigent(netid);
704			if (nconf == NULL) {
705				nc_perror("rpcinfo: invalid transport");
706				exit(1);
707			}
708			client = getclnthandle(host, nconf, RPCBVERS, NULL);
709			if (nconf)
710				(void) freenetconfigent(nconf);
711		}
712	} else
713		client = local_rpcb(PMAPPROG, RPCBVERS);
714
715	if (client == (CLIENT *)NULL) {
716		clnt_pcreateerror("rpcinfo: can't contact rpcbind");
717		exit(1);
718	}
719
720	minutetimeout.tv_sec = 60;
721	minutetimeout.tv_usec = 0;
722	clnt_st = CLNT_CALL(client, RPCBPROC_DUMP, (xdrproc_t) xdr_void,
723		NULL, (xdrproc_t) xdr_rpcblist_ptr, (char *) &head,
724		minutetimeout);
725	if (clnt_st != RPC_SUCCESS) {
726	    if ((clnt_st == RPC_PROGVERSMISMATCH) ||
727		(clnt_st == RPC_PROGUNAVAIL)) {
728		int vers;
729
730		CLNT_GETERR(client, &err);
731		if (err.re_vers.low == RPCBVERS4) {
732		    vers = RPCBVERS4;
733		    clnt_control(client, CLSET_VERS, (char *)&vers);
734		    clnt_st = CLNT_CALL(client, RPCBPROC_DUMP,
735			(xdrproc_t) xdr_void, NULL,
736			(xdrproc_t) xdr_rpcblist_ptr, (char *) &head,
737			minutetimeout);
738		    if (clnt_st != RPC_SUCCESS)
739			goto failed;
740		} else {
741		    if (err.re_vers.high == PMAPVERS) {
742			int high, low;
743			struct pmaplist *pmaphead = NULL;
744			rpcblist_ptr list, prev;
745
746			vers = PMAPVERS;
747			clnt_control(client, CLSET_VERS, (char *)&vers);
748			clnt_st = CLNT_CALL(client, PMAPPROC_DUMP,
749				(xdrproc_t) xdr_void, NULL,
750				(xdrproc_t) xdr_pmaplist_ptr,
751				(char *)&pmaphead, minutetimeout);
752			if (clnt_st != RPC_SUCCESS)
753				goto failed;
754			/*
755			 * convert to rpcblist_ptr format
756			 */
757			for (head = NULL; pmaphead != NULL;
758				pmaphead = pmaphead->pml_next) {
759			    list = (rpcblist *)malloc(sizeof (rpcblist));
760			    if (list == NULL)
761				goto error;
762			    if (head == NULL)
763				head = list;
764			    else
765				prev->rpcb_next = (rpcblist_ptr) list;
766
767			    list->rpcb_next = NULL;
768			    list->rpcb_map.r_prog = pmaphead->pml_map.pm_prog;
769			    list->rpcb_map.r_vers = pmaphead->pml_map.pm_vers;
770			    if (pmaphead->pml_map.pm_prot == IPPROTO_UDP)
771				list->rpcb_map.r_netid = "udp";
772			    else if (pmaphead->pml_map.pm_prot == IPPROTO_TCP)
773				list->rpcb_map.r_netid = "tcp";
774			    else {
775#define	MAXLONG_AS_STRING	"2147483648"
776				list->rpcb_map.r_netid =
777					malloc(strlen(MAXLONG_AS_STRING) + 1);
778				if (list->rpcb_map.r_netid == NULL)
779					goto error;
780				sprintf(list->rpcb_map.r_netid, "%6ld",
781					pmaphead->pml_map.pm_prot);
782			    }
783			    list->rpcb_map.r_owner = UNKNOWN;
784			    low = pmaphead->pml_map.pm_port & 0xff;
785			    high = (pmaphead->pml_map.pm_port >> 8) & 0xff;
786			    list->rpcb_map.r_addr = strdup("0.0.0.0.XXX.XXX");
787			    sprintf(&list->rpcb_map.r_addr[8], "%d.%d",
788				high, low);
789			    prev = list;
790			}
791		    }
792		}
793	    } else {	/* any other error */
794failed:
795		    clnt_perror(client, "rpcinfo: can't contact rpcbind: ");
796		    exit(1);
797	    }
798	}
799	if (head == NULL) {
800		printf("No remote programs registered.\n");
801	} else if (dumptype == RPCBDUMP) {
802		printf(
803"   program version netid     address                service    owner\n");
804		for (; head != NULL; head = head->rpcb_next) {
805			printf("%10u%5u    ",
806				head->rpcb_map.r_prog, head->rpcb_map.r_vers);
807			printf("%-9s ", head->rpcb_map.r_netid);
808			printf("%-22s", head->rpcb_map.r_addr);
809			rpc = getrpcbynumber(head->rpcb_map.r_prog);
810			if (rpc)
811				printf(" %-10s", rpc->r_name);
812			else
813				printf(" %-10s", "-");
814			printf(" %s\n", head->rpcb_map.r_owner);
815		}
816	} else if (dumptype == RPCBDUMP_SHORT) {
817		for (; head != NULL; head = head->rpcb_next) {
818			for (rs = rs_head; rs; rs = rs->next)
819				if (head->rpcb_map.r_prog == rs->prog)
820					break;
821			if (rs == NULL) {
822				rs = (struct rpcbdump_short *)
823					malloc(sizeof (struct rpcbdump_short));
824				if (rs == NULL)
825					goto error;
826				rs->next = NULL;
827				if (rs_head == NULL) {
828					rs_head = rs;
829					rs_tail = rs;
830				} else {
831					rs_tail->next = rs;
832					rs_tail = rs;
833				}
834				rs->prog = head->rpcb_map.r_prog;
835				rs->owner = head->rpcb_map.r_owner;
836				rs->nlist = NULL;
837				rs->vlist = NULL;
838			}
839			if (add_version(rs, head->rpcb_map.r_vers) == FALSE)
840				goto error;
841			if (add_netid(rs, head->rpcb_map.r_netid) == FALSE)
842				goto error;
843		}
844		printf(
845"   program version(s) netid(s)                         service     owner\n");
846		for (rs = rs_head; rs; rs = rs->next) {
847			char *p = buf;
848
849			printf("%10ld  ", rs->prog);
850			for (vl = rs->vlist; vl; vl = vl->next) {
851				sprintf(p, "%d", vl->vers);
852				p = p + strlen(p);
853				if (vl->next)
854					sprintf(p++, ",");
855			}
856			printf("%-10s", buf);
857			buf[0] = '\0';
858			for (nl = rs->nlist; nl; nl = nl->next) {
859				strlcat(buf, nl->netid, sizeof(buf));
860				if (nl->next)
861					strlcat(buf, ",", sizeof(buf));
862			}
863			printf("%-32s", buf);
864			rpc = getrpcbynumber(rs->prog);
865			if (rpc)
866				printf(" %-11s", rpc->r_name);
867			else
868				printf(" %-11s", "-");
869			printf(" %s\n", rs->owner);
870		}
871	}
872	clnt_destroy(client);
873	return;
874error:	warnx("no memory");
875	return;
876}
877
878static char nullstring[] = "\000";
879
880static void
881rpcbaddrlist(char *netid, int argc, char **argv)
882{
883	rpcb_entry_list_ptr head = NULL;
884	struct timeval minutetimeout;
885	register CLIENT *client;
886	struct rpcent *rpc;
887	char *host;
888	RPCB parms;
889	struct netbuf *targaddr;
890
891	if (argc != 3)
892		usage();
893	host = argv[0];
894	if (netid == NULL) {
895		client = clnt_rpcbind_create(host, RPCBVERS4, &targaddr);
896	} else {
897		struct netconfig *nconf;
898
899		nconf = getnetconfigent(netid);
900		if (nconf == NULL) {
901			nc_perror("rpcinfo: invalid transport");
902			exit(1);
903		}
904		client = getclnthandle(host, nconf, RPCBVERS4, &targaddr);
905		if (nconf)
906			(void) freenetconfigent(nconf);
907	}
908	if (client == (CLIENT *)NULL) {
909		clnt_pcreateerror("rpcinfo: can't contact rpcbind");
910		exit(1);
911	}
912	minutetimeout.tv_sec = 60;
913	minutetimeout.tv_usec = 0;
914
915	parms.r_prog = 	getprognum(argv[1]);
916	parms.r_vers = 	getvers(argv[2]);
917	parms.r_netid = client->cl_netid;
918	if (targaddr == NULL) {
919		parms.r_addr = nullstring;	/* for XDRing */
920	} else {
921		/*
922		 * We also send the remote system the address we
923		 * used to contact it in case it can help it
924		 * connect back with us
925		 */
926		struct netconfig *nconf;
927
928		nconf = getnetconfigent(client->cl_netid);
929		if (nconf != NULL) {
930			parms.r_addr = taddr2uaddr(nconf, targaddr);
931			if (parms.r_addr == NULL)
932				parms.r_addr = nullstring;
933			freenetconfigent(nconf);
934		} else {
935			parms.r_addr = nullstring;	/* for XDRing */
936		}
937		free(targaddr->buf);
938		free(targaddr);
939	}
940	parms.r_owner = nullstring;
941
942	if (CLNT_CALL(client, RPCBPROC_GETADDRLIST, (xdrproc_t) xdr_rpcb,
943		(char *) &parms, (xdrproc_t) xdr_rpcb_entry_list_ptr,
944		(char *) &head, minutetimeout) != RPC_SUCCESS) {
945		clnt_perror(client, "rpcinfo: can't contact rpcbind: ");
946		exit(1);
947	}
948	if (head == NULL) {
949		printf("No remote programs registered.\n");
950	} else {
951		printf(
952	"   program vers  tp_family/name/class    address\t\t  service\n");
953		for (; head != NULL; head = head->rpcb_entry_next) {
954			rpcb_entry *re;
955			char buf[128];
956
957			re = &head->rpcb_entry_map;
958			printf("%10u%3u    ",
959				parms.r_prog, parms.r_vers);
960			sprintf(buf, "%s/%s/%s ",
961				re->r_nc_protofmly, re->r_nc_proto,
962				re->r_nc_semantics == NC_TPI_CLTS ? "clts" :
963				re->r_nc_semantics == NC_TPI_COTS ? "cots" :
964						"cots_ord");
965			printf("%-24s", buf);
966			printf("%-24s", re->r_maddr);
967			rpc = getrpcbynumber(parms.r_prog);
968			if (rpc)
969				printf(" %-13s", rpc->r_name);
970			else
971				printf(" %-13s", "-");
972			printf("\n");
973		}
974	}
975	clnt_destroy(client);
976	return;
977}
978
979/*
980 * monitor rpcbind
981 */
982static void
983rpcbgetstat(int argc, char **argv)
984{
985	rpcb_stat_byvers inf;
986	struct timeval minutetimeout;
987	register CLIENT *client;
988	char *host;
989	int i, j;
990	rpcbs_addrlist *pa;
991	rpcbs_rmtcalllist *pr;
992	int cnt, flen;
993#define	MAXFIELD	64
994	char fieldbuf[MAXFIELD];
995#define	MAXLINE		256
996	char linebuf[MAXLINE];
997	char *cp, *lp;
998	const char *pmaphdr[] = {
999		"NULL", "SET", "UNSET", "GETPORT",
1000		"DUMP", "CALLIT"
1001	};
1002	const char *rpcb3hdr[] = {
1003		"NULL", "SET", "UNSET", "GETADDR", "DUMP", "CALLIT", "TIME",
1004		"U2T", "T2U"
1005	};
1006	const char *rpcb4hdr[] = {
1007		"NULL", "SET", "UNSET", "GETADDR", "DUMP", "CALLIT", "TIME",
1008		"U2T",  "T2U", "VERADDR", "INDRECT", "GETLIST", "GETSTAT"
1009	};
1010
1011#define	TABSTOP	8
1012
1013	if (argc >= 1) {
1014		host = argv[0];
1015		client = clnt_rpcbind_create(host, RPCBVERS4, NULL);
1016	} else
1017		client = local_rpcb(PMAPPROG, RPCBVERS4);
1018	if (client == (CLIENT *)NULL) {
1019		clnt_pcreateerror("rpcinfo: can't contact rpcbind");
1020		exit(1);
1021	}
1022	minutetimeout.tv_sec = 60;
1023	minutetimeout.tv_usec = 0;
1024	memset((char *)&inf, 0, sizeof (rpcb_stat_byvers));
1025	if (CLNT_CALL(client, RPCBPROC_GETSTAT, (xdrproc_t) xdr_void, NULL,
1026		(xdrproc_t) xdr_rpcb_stat_byvers, (char *)&inf, minutetimeout)
1027			!= RPC_SUCCESS) {
1028		clnt_perror(client, "rpcinfo: can't contact rpcbind: ");
1029		exit(1);
1030	}
1031	printf("PORTMAP (version 2) statistics\n");
1032	lp = linebuf;
1033	for (i = 0; i <= rpcb_highproc_2; i++) {
1034		fieldbuf[0] = '\0';
1035		switch (i) {
1036		case PMAPPROC_SET:
1037			sprintf(fieldbuf, "%d/", inf[RPCBVERS_2_STAT].setinfo);
1038			break;
1039		case PMAPPROC_UNSET:
1040			sprintf(fieldbuf, "%d/",
1041				inf[RPCBVERS_2_STAT].unsetinfo);
1042			break;
1043		case PMAPPROC_GETPORT:
1044			cnt = 0;
1045			for (pa = inf[RPCBVERS_2_STAT].addrinfo; pa;
1046				pa = pa->next)
1047				cnt += pa->success;
1048			sprintf(fieldbuf, "%d/", cnt);
1049			break;
1050		case PMAPPROC_CALLIT:
1051			cnt = 0;
1052			for (pr = inf[RPCBVERS_2_STAT].rmtinfo; pr;
1053				pr = pr->next)
1054				cnt += pr->success;
1055			sprintf(fieldbuf, "%d/", cnt);
1056			break;
1057		default: break;  /* For the remaining ones */
1058		}
1059		cp = &fieldbuf[0] + strlen(fieldbuf);
1060		sprintf(cp, "%d", inf[RPCBVERS_2_STAT].info[i]);
1061		flen = strlen(fieldbuf);
1062		printf("%s%s", pmaphdr[i],
1063			spaces((TABSTOP * (1 + flen / TABSTOP))
1064			- strlen(pmaphdr[i])));
1065		sprintf(lp, "%s%s", fieldbuf,
1066			spaces(cnt = ((TABSTOP * (1 + flen / TABSTOP))
1067			- flen)));
1068		lp += (flen + cnt);
1069	}
1070	printf("\n%s\n\n", linebuf);
1071
1072	if (inf[RPCBVERS_2_STAT].info[PMAPPROC_CALLIT]) {
1073		printf("PMAP_RMTCALL call statistics\n");
1074		print_rmtcallstat(RPCBVERS_2_STAT, &inf[RPCBVERS_2_STAT]);
1075		printf("\n");
1076	}
1077
1078	if (inf[RPCBVERS_2_STAT].info[PMAPPROC_GETPORT]) {
1079		printf("PMAP_GETPORT call statistics\n");
1080		print_getaddrstat(RPCBVERS_2_STAT, &inf[RPCBVERS_2_STAT]);
1081		printf("\n");
1082	}
1083
1084	printf("RPCBIND (version 3) statistics\n");
1085	lp = linebuf;
1086	for (i = 0; i <= rpcb_highproc_3; i++) {
1087		fieldbuf[0] = '\0';
1088		switch (i) {
1089		case RPCBPROC_SET:
1090			sprintf(fieldbuf, "%d/", inf[RPCBVERS_3_STAT].setinfo);
1091			break;
1092		case RPCBPROC_UNSET:
1093			sprintf(fieldbuf, "%d/",
1094				inf[RPCBVERS_3_STAT].unsetinfo);
1095			break;
1096		case RPCBPROC_GETADDR:
1097			cnt = 0;
1098			for (pa = inf[RPCBVERS_3_STAT].addrinfo; pa;
1099				pa = pa->next)
1100				cnt += pa->success;
1101			sprintf(fieldbuf, "%d/", cnt);
1102			break;
1103		case RPCBPROC_CALLIT:
1104			cnt = 0;
1105			for (pr = inf[RPCBVERS_3_STAT].rmtinfo; pr;
1106				pr = pr->next)
1107				cnt += pr->success;
1108			sprintf(fieldbuf, "%d/", cnt);
1109			break;
1110		default: break;  /* For the remaining ones */
1111		}
1112		cp = &fieldbuf[0] + strlen(fieldbuf);
1113		sprintf(cp, "%d", inf[RPCBVERS_3_STAT].info[i]);
1114		flen = strlen(fieldbuf);
1115		printf("%s%s", rpcb3hdr[i],
1116			spaces((TABSTOP * (1 + flen / TABSTOP))
1117			- strlen(rpcb3hdr[i])));
1118		sprintf(lp, "%s%s", fieldbuf,
1119			spaces(cnt = ((TABSTOP * (1 + flen / TABSTOP))
1120			- flen)));
1121		lp += (flen + cnt);
1122	}
1123	printf("\n%s\n\n", linebuf);
1124
1125	if (inf[RPCBVERS_3_STAT].info[RPCBPROC_CALLIT]) {
1126		printf("RPCB_RMTCALL (version 3) call statistics\n");
1127		print_rmtcallstat(RPCBVERS_3_STAT, &inf[RPCBVERS_3_STAT]);
1128		printf("\n");
1129	}
1130
1131	if (inf[RPCBVERS_3_STAT].info[RPCBPROC_GETADDR]) {
1132		printf("RPCB_GETADDR (version 3) call statistics\n");
1133		print_getaddrstat(RPCBVERS_3_STAT, &inf[RPCBVERS_3_STAT]);
1134		printf("\n");
1135	}
1136
1137	printf("RPCBIND (version 4) statistics\n");
1138
1139	for (j = 0; j <= 9; j += 9) { /* Just two iterations for printing */
1140		lp = linebuf;
1141		for (i = j; i <= MAX(8, rpcb_highproc_4 - 9 + j); i++) {
1142			fieldbuf[0] = '\0';
1143			switch (i) {
1144			case RPCBPROC_SET:
1145				sprintf(fieldbuf, "%d/",
1146					inf[RPCBVERS_4_STAT].setinfo);
1147				break;
1148			case RPCBPROC_UNSET:
1149				sprintf(fieldbuf, "%d/",
1150					inf[RPCBVERS_4_STAT].unsetinfo);
1151				break;
1152			case RPCBPROC_GETADDR:
1153				cnt = 0;
1154				for (pa = inf[RPCBVERS_4_STAT].addrinfo; pa;
1155					pa = pa->next)
1156					cnt += pa->success;
1157				sprintf(fieldbuf, "%d/", cnt);
1158				break;
1159			case RPCBPROC_CALLIT:
1160				cnt = 0;
1161				for (pr = inf[RPCBVERS_4_STAT].rmtinfo; pr;
1162					pr = pr->next)
1163					cnt += pr->success;
1164				sprintf(fieldbuf, "%d/", cnt);
1165				break;
1166			default: break;  /* For the remaining ones */
1167			}
1168			cp = &fieldbuf[0] + strlen(fieldbuf);
1169			/*
1170			 * XXX: We also add RPCBPROC_GETADDRLIST queries to
1171			 * RPCB_GETADDR because rpcbind includes the
1172			 * RPCB_GETADDRLIST successes in RPCB_GETADDR.
1173			 */
1174			if (i != RPCBPROC_GETADDR)
1175			    sprintf(cp, "%d", inf[RPCBVERS_4_STAT].info[i]);
1176			else
1177			    sprintf(cp, "%d", inf[RPCBVERS_4_STAT].info[i] +
1178			    inf[RPCBVERS_4_STAT].info[RPCBPROC_GETADDRLIST]);
1179			flen = strlen(fieldbuf);
1180			printf("%s%s", rpcb4hdr[i],
1181				spaces((TABSTOP * (1 + flen / TABSTOP))
1182				- strlen(rpcb4hdr[i])));
1183			sprintf(lp, "%s%s", fieldbuf,
1184				spaces(cnt = ((TABSTOP * (1 + flen / TABSTOP))
1185				- flen)));
1186			lp += (flen + cnt);
1187		}
1188		printf("\n%s\n", linebuf);
1189	}
1190
1191	if (inf[RPCBVERS_4_STAT].info[RPCBPROC_CALLIT] ||
1192			    inf[RPCBVERS_4_STAT].info[RPCBPROC_INDIRECT]) {
1193		printf("\n");
1194		printf("RPCB_RMTCALL (version 4) call statistics\n");
1195		print_rmtcallstat(RPCBVERS_4_STAT, &inf[RPCBVERS_4_STAT]);
1196	}
1197
1198	if (inf[RPCBVERS_4_STAT].info[RPCBPROC_GETADDR]) {
1199		printf("\n");
1200		printf("RPCB_GETADDR (version 4) call statistics\n");
1201		print_getaddrstat(RPCBVERS_4_STAT, &inf[RPCBVERS_4_STAT]);
1202	}
1203	clnt_destroy(client);
1204}
1205
1206/*
1207 * Delete registeration for this (prog, vers, netid)
1208 */
1209static void
1210deletereg(char *netid, int argc, char **argv)
1211{
1212	struct netconfig *nconf = NULL;
1213
1214	if (argc != 2)
1215		usage();
1216	if (netid) {
1217		nconf = getnetconfigent(netid);
1218		if (nconf == NULL)
1219			errx(1, "netid %s not supported", netid);
1220	}
1221	if ((rpcb_unset(getprognum(argv[0]), getvers(argv[1]), nconf)) == 0)
1222		errx(1,
1223	"could not delete registration for prog %s version %s",
1224			argv[0], argv[1]);
1225}
1226
1227/*
1228 * Create and return a handle for the given nconf.
1229 * Exit if cannot create handle.
1230 */
1231static CLIENT *
1232clnt_addr_create(char *address, struct netconfig *nconf,
1233    u_long prog, u_long vers)
1234{
1235	CLIENT *client;
1236	static struct netbuf *nbuf;
1237	static int fd = RPC_ANYFD;
1238
1239	if (fd == RPC_ANYFD) {
1240		if ((fd = __rpc_nconf2fd(nconf)) == -1) {
1241			rpc_createerr.cf_stat = RPC_TLIERROR;
1242			clnt_pcreateerror("rpcinfo");
1243			exit(1);
1244		}
1245		/* Convert the uaddr to taddr */
1246		nbuf = uaddr2taddr(nconf, address);
1247		if (nbuf == NULL)
1248			errx(1, "no address for client handle");
1249	}
1250	client = clnt_tli_create(fd, nconf, nbuf, prog, vers, 0, 0);
1251	if (client == (CLIENT *)NULL) {
1252		clnt_pcreateerror("rpcinfo");
1253		exit(1);
1254	}
1255	return (client);
1256}
1257
1258/*
1259 * If the version number is given, ping that (prog, vers); else try to find
1260 * the version numbers supported for that prog and ping all the versions.
1261 * Remote rpcbind is not contacted for this service. The requests are
1262 * sent directly to the services themselves.
1263 */
1264static void
1265addrping(char *address, char *netid, int argc, char **argv)
1266{
1267	CLIENT *client;
1268	struct timeval to;
1269	enum clnt_stat rpc_stat;
1270	u_long prognum, versnum, minvers, maxvers;
1271	struct rpc_err rpcerr;
1272	int failure = 0;
1273	struct netconfig *nconf;
1274	int fd;
1275
1276	if (argc < 1 || argc > 2 || (netid == NULL))
1277		usage();
1278	nconf = getnetconfigent(netid);
1279	if (nconf == (struct netconfig *)NULL)
1280		errx(1, "could not find %s", netid);
1281	to.tv_sec = 10;
1282	to.tv_usec = 0;
1283	prognum = getprognum(argv[0]);
1284	if (argc == 1) {	/* Version number not known */
1285		/*
1286		 * A call to version 0 should fail with a program/version
1287		 * mismatch, and give us the range of versions supported.
1288		 */
1289		versnum = MIN_VERS;
1290	} else {
1291		versnum = getvers(argv[1]);
1292	}
1293	client = clnt_addr_create(address, nconf, prognum, versnum);
1294	rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void,
1295			(char *)NULL, (xdrproc_t) xdr_void,
1296			(char *)NULL, to);
1297	if (argc == 2) {
1298		/* Version number was known */
1299		if (pstatus(client, prognum, versnum) < 0)
1300			failure = 1;
1301		(void) CLNT_DESTROY(client);
1302		if (failure)
1303			exit(1);
1304		return;
1305	}
1306	/* Version number not known */
1307	(void) CLNT_CONTROL(client, CLSET_FD_NCLOSE, (char *)NULL);
1308	(void) CLNT_CONTROL(client, CLGET_FD, (char *)&fd);
1309	if (rpc_stat == RPC_PROGVERSMISMATCH) {
1310		clnt_geterr(client, &rpcerr);
1311		minvers = rpcerr.re_vers.low;
1312		maxvers = rpcerr.re_vers.high;
1313	} else if (rpc_stat == RPC_SUCCESS) {
1314		/*
1315		 * Oh dear, it DOES support version 0.
1316		 * Let's try version MAX_VERS.
1317		 */
1318		(void) CLNT_DESTROY(client);
1319		client = clnt_addr_create(address, nconf, prognum, MAX_VERS);
1320		rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void,
1321				(char *)NULL, (xdrproc_t) xdr_void,
1322				(char *)NULL, to);
1323		if (rpc_stat == RPC_PROGVERSMISMATCH) {
1324			clnt_geterr(client, &rpcerr);
1325			minvers = rpcerr.re_vers.low;
1326			maxvers = rpcerr.re_vers.high;
1327		} else if (rpc_stat == RPC_SUCCESS) {
1328			/*
1329			 * It also supports version MAX_VERS.
1330			 * Looks like we have a wise guy.
1331			 * OK, we give them information on all
1332			 * 4 billion versions they support...
1333			 */
1334			minvers = 0;
1335			maxvers = MAX_VERS;
1336		} else {
1337			(void) pstatus(client, prognum, MAX_VERS);
1338			exit(1);
1339		}
1340	} else {
1341		(void) pstatus(client, prognum, (u_long)0);
1342		exit(1);
1343	}
1344	(void) CLNT_DESTROY(client);
1345	for (versnum = minvers; versnum <= maxvers; versnum++) {
1346		client = clnt_addr_create(address, nconf, prognum, versnum);
1347		rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void,
1348				(char *)NULL, (xdrproc_t) xdr_void,
1349				(char *)NULL, to);
1350		if (pstatus(client, prognum, versnum) < 0)
1351				failure = 1;
1352		(void) CLNT_DESTROY(client);
1353	}
1354	(void) close(fd);
1355	if (failure)
1356		exit(1);
1357	return;
1358}
1359
1360/*
1361 * If the version number is given, ping that (prog, vers); else try to find
1362 * the version numbers supported for that prog and ping all the versions.
1363 * Remote rpcbind is *contacted* for this service. The requests are
1364 * then sent directly to the services themselves.
1365 */
1366static void
1367progping(char *netid, int argc, char **argv)
1368{
1369	CLIENT *client;
1370	struct timeval to;
1371	enum clnt_stat rpc_stat;
1372	u_long prognum, versnum, minvers, maxvers;
1373	struct rpc_err rpcerr;
1374	int failure = 0;
1375	struct netconfig *nconf;
1376
1377	if (argc < 2 || argc > 3 || (netid == NULL))
1378		usage();
1379	prognum = getprognum(argv[1]);
1380	if (argc == 2) { /* Version number not known */
1381		/*
1382		 * A call to version 0 should fail with a program/version
1383		 * mismatch, and give us the range of versions supported.
1384		 */
1385		versnum = MIN_VERS;
1386	} else {
1387		versnum = getvers(argv[2]);
1388	}
1389	if (netid) {
1390		nconf = getnetconfigent(netid);
1391		if (nconf == (struct netconfig *)NULL)
1392			errx(1, "could not find %s", netid);
1393		client = clnt_tp_create(argv[0], prognum, versnum, nconf);
1394	} else {
1395		client = clnt_create(argv[0], prognum, versnum, "NETPATH");
1396	}
1397	if (client == (CLIENT *)NULL) {
1398		clnt_pcreateerror("rpcinfo");
1399		exit(1);
1400	}
1401	to.tv_sec = 10;
1402	to.tv_usec = 0;
1403	rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void,
1404			(char *)NULL, (xdrproc_t) xdr_void,
1405			(char *)NULL, to);
1406	if (argc == 3) {
1407		/* Version number was known */
1408		if (pstatus(client, prognum, versnum) < 0)
1409			failure = 1;
1410		(void) CLNT_DESTROY(client);
1411		if (failure)
1412			exit(1);
1413		return;
1414	}
1415	/* Version number not known */
1416	if (rpc_stat == RPC_PROGVERSMISMATCH) {
1417		clnt_geterr(client, &rpcerr);
1418		minvers = rpcerr.re_vers.low;
1419		maxvers = rpcerr.re_vers.high;
1420	} else if (rpc_stat == RPC_SUCCESS) {
1421		/*
1422		 * Oh dear, it DOES support version 0.
1423		 * Let's try version MAX_VERS.
1424		 */
1425		versnum = MAX_VERS;
1426		(void) CLNT_CONTROL(client, CLSET_VERS, (char *)&versnum);
1427		rpc_stat = CLNT_CALL(client, NULLPROC,
1428				(xdrproc_t) xdr_void, (char *)NULL,
1429				(xdrproc_t)  xdr_void, (char *)NULL, to);
1430		if (rpc_stat == RPC_PROGVERSMISMATCH) {
1431			clnt_geterr(client, &rpcerr);
1432			minvers = rpcerr.re_vers.low;
1433			maxvers = rpcerr.re_vers.high;
1434		} else if (rpc_stat == RPC_SUCCESS) {
1435			/*
1436			 * It also supports version MAX_VERS.
1437			 * Looks like we have a wise guy.
1438			 * OK, we give them information on all
1439			 * 4 billion versions they support...
1440			 */
1441			minvers = 0;
1442			maxvers = MAX_VERS;
1443		} else {
1444			(void) pstatus(client, prognum, MAX_VERS);
1445			exit(1);
1446		}
1447	} else {
1448		(void) pstatus(client, prognum, (u_long)0);
1449		exit(1);
1450	}
1451	for (versnum = minvers; versnum <= maxvers; versnum++) {
1452		(void) CLNT_CONTROL(client, CLSET_VERS, (char *)&versnum);
1453		rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void,
1454					(char *)NULL, (xdrproc_t) xdr_void,
1455					(char *)NULL, to);
1456		if (pstatus(client, prognum, versnum) < 0)
1457				failure = 1;
1458	}
1459	(void) CLNT_DESTROY(client);
1460	if (failure)
1461		exit(1);
1462	return;
1463}
1464
1465static void
1466usage(void)
1467{
1468	fprintf(stderr, "usage: rpcinfo [-m | -s] [host]\n");
1469#ifdef PORTMAP
1470	fprintf(stderr, "       rpcinfo -p [host]\n");
1471#endif
1472	fprintf(stderr, "       rpcinfo -T netid host prognum [versnum]\n");
1473	fprintf(stderr, "       rpcinfo -l host prognum versnum\n");
1474#ifdef PORTMAP
1475	fprintf(stderr,
1476"       rpcinfo [-n portnum] -u | -t host prognum [versnum]\n");
1477#endif
1478	fprintf(stderr,
1479"       rpcinfo -a serv_address -T netid prognum [version]\n");
1480	fprintf(stderr, "       rpcinfo -b prognum versnum\n");
1481	fprintf(stderr, "       rpcinfo -d [-T netid] prognum versnum\n");
1482	exit(1);
1483}
1484
1485static u_long
1486getprognum (char *arg)
1487{
1488	char *strptr;
1489	register struct rpcent *rpc;
1490	register u_long prognum;
1491	char *tptr = arg;
1492
1493	while (*tptr && isdigit(*tptr++));
1494	if (*tptr || isalpha(*(tptr - 1))) {
1495		rpc = getrpcbyname(arg);
1496		if (rpc == NULL)
1497			errx(1, "%s is unknown service", arg);
1498		prognum = rpc->r_number;
1499	} else {
1500		prognum = strtol(arg, &strptr, 10);
1501		if (strptr == arg || *strptr != '\0')
1502			errx(1, "%s is illegal program number", arg);
1503	}
1504	return (prognum);
1505}
1506
1507static u_long
1508getvers(char *arg)
1509{
1510	char *strptr;
1511	register u_long vers;
1512
1513	vers = (int) strtol(arg, &strptr, 10);
1514	if (strptr == arg || *strptr != '\0')
1515		errx(1, "%s is illegal version number", arg);
1516	return (vers);
1517}
1518
1519/*
1520 * This routine should take a pointer to an "rpc_err" structure, rather than
1521 * a pointer to a CLIENT structure, but "clnt_perror" takes a pointer to
1522 * a CLIENT structure rather than a pointer to an "rpc_err" structure.
1523 * As such, we have to keep the CLIENT structure around in order to print
1524 * a good error message.
1525 */
1526static int
1527pstatus(register CLIENT *client, u_long prog, u_long vers)
1528{
1529	struct rpc_err rpcerr;
1530
1531	clnt_geterr(client, &rpcerr);
1532	if (rpcerr.re_status != RPC_SUCCESS) {
1533		clnt_perror(client, "rpcinfo");
1534		printf("program %lu version %lu is not available\n",
1535			prog, vers);
1536		return (-1);
1537	} else {
1538		printf("program %lu version %lu ready and waiting\n",
1539			prog, vers);
1540		return (0);
1541	}
1542}
1543
1544static CLIENT *
1545clnt_rpcbind_create(char *host, int rpcbversnum, struct netbuf **targaddr)
1546{
1547	static const char *tlist[3] = {
1548		"circuit_n", "circuit_v", "datagram_v"
1549	};
1550	int i;
1551	struct netconfig *nconf;
1552	CLIENT *clnt = NULL;
1553	void *handle;
1554
1555	rpc_createerr.cf_stat = RPC_SUCCESS;
1556	for (i = 0; i < 3; i++) {
1557		if ((handle = __rpc_setconf(tlist[i])) == NULL)
1558			continue;
1559		while (clnt == (CLIENT *)NULL) {
1560			if ((nconf = __rpc_getconf(handle)) == NULL) {
1561				if (rpc_createerr.cf_stat == RPC_SUCCESS)
1562				    rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1563				break;
1564			}
1565			clnt = getclnthandle(host, nconf, rpcbversnum,
1566					targaddr);
1567		}
1568		if (clnt)
1569			break;
1570		__rpc_endconf(handle);
1571	}
1572	return (clnt);
1573}
1574
1575static CLIENT*
1576getclnthandle(char *host, struct netconfig *nconf,
1577    u_long rpcbversnum, struct netbuf **targaddr)
1578{
1579	struct netbuf addr;
1580	struct addrinfo hints, *res;
1581	CLIENT *client = NULL;
1582
1583	/* Get the address of the rpcbind */
1584	memset(&hints, 0, sizeof hints);
1585	if (getaddrinfo(host, "rpcbind", &hints, &res) != 0) {
1586		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
1587		return (NULL);
1588	}
1589	addr.len = addr.maxlen = res->ai_addrlen;
1590	addr.buf = res->ai_addr;
1591	client = clnt_tli_create(RPC_ANYFD, nconf, &addr, RPCBPROG,
1592			rpcbversnum, 0, 0);
1593	if (client) {
1594		if (targaddr != NULL) {
1595			*targaddr =
1596			    (struct netbuf *)malloc(sizeof (struct netbuf));
1597			if (*targaddr != NULL) {
1598				(*targaddr)->maxlen = addr.maxlen;
1599				(*targaddr)->len = addr.len;
1600				(*targaddr)->buf = (char *)malloc(addr.len);
1601				if ((*targaddr)->buf != NULL) {
1602					memcpy((*targaddr)->buf, addr.buf,
1603						addr.len);
1604				}
1605			}
1606		}
1607	} else {
1608		if (rpc_createerr.cf_stat == RPC_TLIERROR) {
1609			/*
1610			 * Assume that the other system is dead; this is a
1611			 * better error to display to the user.
1612			 */
1613			rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1614			rpc_createerr.cf_error.re_status = RPC_FAILED;
1615		}
1616	}
1617	freeaddrinfo(res);
1618	return (client);
1619}
1620
1621static void
1622print_rmtcallstat(int rtype, rpcb_stat *infp)
1623{
1624	register rpcbs_rmtcalllist_ptr pr;
1625	struct rpcent *rpc;
1626
1627	if (rtype == RPCBVERS_4_STAT)
1628		printf(
1629		"prog\t\tvers\tproc\tnetid\tindirect success failure\n");
1630	else
1631		printf("prog\t\tvers\tproc\tnetid\tsuccess\tfailure\n");
1632	for (pr = infp->rmtinfo; pr; pr = pr->next) {
1633		rpc = getrpcbynumber(pr->prog);
1634		if (rpc)
1635			printf("%-16s", rpc->r_name);
1636		else
1637			printf("%-16d", pr->prog);
1638		printf("%d\t%d\t%s\t",
1639			pr->vers, pr->proc, pr->netid);
1640		if (rtype == RPCBVERS_4_STAT)
1641			printf("%d\t ", pr->indirect);
1642		printf("%d\t%d\n", pr->success, pr->failure);
1643	}
1644}
1645
1646static void
1647print_getaddrstat(int rtype, rpcb_stat *infp)
1648{
1649	rpcbs_addrlist_ptr al;
1650	register struct rpcent *rpc;
1651
1652	printf("prog\t\tvers\tnetid\t  success\tfailure\n");
1653	for (al = infp->addrinfo; al; al = al->next) {
1654		rpc = getrpcbynumber(al->prog);
1655		if (rpc)
1656			printf("%-16s", rpc->r_name);
1657		else
1658			printf("%-16d", al->prog);
1659		printf("%d\t%s\t  %-12d\t%d\n",
1660			al->vers, al->netid,
1661			al->success, al->failure);
1662	}
1663}
1664
1665static char *
1666spaces(int howmany)
1667{
1668	static char space_array[] =		/* 64 spaces */
1669	"                                                                ";
1670
1671	if (howmany <= 0 || howmany > sizeof (space_array)) {
1672		return ("");
1673	}
1674	return (&space_array[sizeof (space_array) - howmany - 1]);
1675}
1676