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