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