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