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