rpcinfo.c revision 1900
1#ifndef lint
2/*static char sccsid[] = "from: @(#)rpcinfo.c 1.22 87/08/12 SMI";*/
3/*static char sccsid[] = "from: @(#)rpcinfo.c	2.2 88/08/11 4.0 RPCSRC";*/
4static char rcsid[] = "$Id: rpcinfo.c,v 1.1 1993/09/13 23:22:42 jtc Exp $";
5#endif
6
7/*
8 * Copyright (C) 1986, Sun Microsystems, Inc.
9 */
10
11/*
12 * rpcinfo: ping a particular rpc program
13 *     or dump the portmapper
14 */
15
16/*
17 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
18 * unrestricted use provided that this legend is included on all tape
19 * media and as a part of the software program in whole or part.  Users
20 * may copy or modify Sun RPC without charge, but are not authorized
21 * to license or distribute it to anyone else except as part of a product or
22 * program developed by the user.
23 *
24 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
25 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
27 *
28 * Sun RPC is provided with no support and without any obligation on the
29 * part of Sun Microsystems, Inc. to assist in its use, correction,
30 * modification or enhancement.
31 *
32 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
33 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
34 * OR ANY PART THEREOF.
35 *
36 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
37 * or profits or other special, indirect and consequential damages, even if
38 * Sun has been advised of the possibility of such damages.
39 *
40 * Sun Microsystems, Inc.
41 * 2550 Garcia Avenue
42 * Mountain View, California  94043
43 */
44
45#include <rpc/rpc.h>
46#include <stdio.h>
47#include <sys/socket.h>
48#include <netdb.h>
49#include <rpc/pmap_prot.h>
50#include <rpc/pmap_clnt.h>
51#include <signal.h>
52#include <ctype.h>
53
54#define MAXHOSTLEN 256
55
56#define	MIN_VERS	((u_long) 0)
57#define	MAX_VERS	((u_long) 4294967295UL)
58
59static void	udpping(/*u_short portflag, int argc, char **argv*/);
60static void	tcpping(/*u_short portflag, int argc, char **argv*/);
61static int	pstatus(/*CLIENT *client, u_long prognum, u_long vers*/);
62static void	pmapdump(/*int argc, char **argv*/);
63static bool_t	reply_proc(/*void *res, struct sockaddr_in *who*/);
64static void	brdcst(/*int argc, char **argv*/);
65static void	deletereg(/* int argc, char **argv */) ;
66static void	usage(/*void*/);
67static u_long	getprognum(/*char *arg*/);
68static u_long	getvers(/*char *arg*/);
69static void	get_inet_address(/*struct sockaddr_in *addr, char *host*/);
70extern u_long inet_addr();  /* in 4.2BSD, arpa/inet.h called that a in_addr */
71extern char *inet_ntoa();
72
73/*
74 * Functions to be performed.
75 */
76#define	NONE		0	/* no function */
77#define	PMAPDUMP	1	/* dump portmapper registrations */
78#define	TCPPING		2	/* ping TCP service */
79#define	UDPPING		3	/* ping UDP service */
80#define	BRDCST		4	/* ping broadcast UDP service */
81#define DELETES		5	/* delete registration for the service */
82
83int
84main(argc, argv)
85	int argc;
86	char **argv;
87{
88	register int c;
89	extern char *optarg;
90	extern int optind;
91	int errflg;
92	int function;
93	u_short portnum;
94
95	function = NONE;
96	portnum = 0;
97	errflg = 0;
98	while ((c = getopt(argc, argv, "ptubdn:")) != EOF) {
99		switch (c) {
100
101		case 'p':
102			if (function != NONE)
103				errflg = 1;
104			else
105				function = PMAPDUMP;
106			break;
107
108		case 't':
109			if (function != NONE)
110				errflg = 1;
111			else
112				function = TCPPING;
113			break;
114
115		case 'u':
116			if (function != NONE)
117				errflg = 1;
118			else
119				function = UDPPING;
120			break;
121
122		case 'b':
123			if (function != NONE)
124				errflg = 1;
125			else
126				function = BRDCST;
127			break;
128
129		case 'n':
130			portnum = (u_short) atoi(optarg);   /* hope we don't get bogus # */
131			break;
132
133		case 'd':
134			if (function != NONE)
135				errflg = 1;
136			else
137				function = DELETES;
138			break;
139
140		case '?':
141			errflg = 1;
142		}
143	}
144
145	if (errflg || function == NONE) {
146		usage();
147		return (1);
148	}
149
150	switch (function) {
151
152	case PMAPDUMP:
153		if (portnum != 0) {
154			usage();
155			return (1);
156		}
157		pmapdump(argc - optind, argv + optind);
158		break;
159
160	case UDPPING:
161		udpping(portnum, argc - optind, argv + optind);
162		break;
163
164	case TCPPING:
165		tcpping(portnum, argc - optind, argv + optind);
166		break;
167
168	case BRDCST:
169		if (portnum != 0) {
170			usage();
171			return (1);
172		}
173		brdcst(argc - optind, argv + optind);
174		break;
175
176	case DELETES:
177		deletereg(argc - optind, argv + optind);
178		break;
179	}
180
181	return (0);
182}
183
184static void
185udpping(portnum, argc, argv)
186	u_short portnum;
187	int argc;
188	char **argv;
189{
190	struct timeval to;
191	struct sockaddr_in addr;
192	enum clnt_stat rpc_stat;
193	CLIENT *client;
194	u_long prognum, vers, minvers, maxvers;
195	int sock = RPC_ANYSOCK;
196	struct rpc_err rpcerr;
197	int failure;
198
199	if (argc < 2 || argc > 3) {
200		usage();
201		exit(1);
202	}
203	prognum = getprognum(argv[1]);
204	get_inet_address(&addr, argv[0]);
205	/* Open the socket here so it will survive calls to clnt_destroy */
206	sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP);
207	if (sock < 0) {
208		perror("rpcinfo: socket");
209		exit(1);
210	}
211	failure = 0;
212	if (argc == 2) {
213		/*
214		 * A call to version 0 should fail with a program/version
215		 * mismatch, and give us the range of versions supported.
216		 */
217		addr.sin_port = htons(portnum);
218		to.tv_sec = 5;
219		to.tv_usec = 0;
220		if ((client = clntudp_create(&addr, prognum, (u_long)0,
221		    to, &sock)) == NULL) {
222			clnt_pcreateerror("rpcinfo");
223			printf("program %lu is not available\n",
224			    prognum);
225			exit(1);
226		}
227		to.tv_sec = 10;
228		to.tv_usec = 0;
229		rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL,
230		    xdr_void, (char *)NULL, to);
231		if (rpc_stat == RPC_PROGVERSMISMATCH) {
232			clnt_geterr(client, &rpcerr);
233			minvers = rpcerr.re_vers.low;
234			maxvers = rpcerr.re_vers.high;
235		} else if (rpc_stat == RPC_SUCCESS) {
236			/*
237			 * Oh dear, it DOES support version 0.
238			 * Let's try version MAX_VERS.
239			 */
240			addr.sin_port = htons(portnum);
241			to.tv_sec = 5;
242			to.tv_usec = 0;
243			if ((client = clntudp_create(&addr, prognum, MAX_VERS,
244			    to, &sock)) == NULL) {
245				clnt_pcreateerror("rpcinfo");
246				printf("program %lu version %lu is not available\n",
247				    prognum, MAX_VERS);
248				exit(1);
249			}
250			to.tv_sec = 10;
251			to.tv_usec = 0;
252			rpc_stat = clnt_call(client, NULLPROC, xdr_void,
253			    (char *)NULL, xdr_void, (char *)NULL, to);
254			if (rpc_stat == RPC_PROGVERSMISMATCH) {
255				clnt_geterr(client, &rpcerr);
256				minvers = rpcerr.re_vers.low;
257				maxvers = rpcerr.re_vers.high;
258			} else if (rpc_stat == RPC_SUCCESS) {
259				/*
260				 * It also supports version MAX_VERS.
261				 * Looks like we have a wise guy.
262				 * OK, we give them information on all
263				 * 4 billion versions they support...
264				 */
265				minvers = 0;
266				maxvers = MAX_VERS;
267			} else {
268				(void) pstatus(client, prognum, MAX_VERS);
269				exit(1);
270			}
271		} else {
272			(void) pstatus(client, prognum, (u_long)0);
273			exit(1);
274		}
275		clnt_destroy(client);
276		for (vers = minvers; vers <= maxvers; vers++) {
277			addr.sin_port = htons(portnum);
278			to.tv_sec = 5;
279			to.tv_usec = 0;
280			if ((client = clntudp_create(&addr, prognum, vers,
281			    to, &sock)) == NULL) {
282				clnt_pcreateerror("rpcinfo");
283				printf("program %lu version %lu is not available\n",
284				    prognum, vers);
285				exit(1);
286			}
287			to.tv_sec = 10;
288			to.tv_usec = 0;
289			rpc_stat = clnt_call(client, NULLPROC, xdr_void,
290			    (char *)NULL, xdr_void, (char *)NULL, to);
291			if (pstatus(client, prognum, vers) < 0)
292				failure = 1;
293			clnt_destroy(client);
294		}
295	}
296	else {
297		vers = getvers(argv[2]);
298		addr.sin_port = htons(portnum);
299		to.tv_sec = 5;
300		to.tv_usec = 0;
301		if ((client = clntudp_create(&addr, prognum, vers,
302		    to, &sock)) == NULL) {
303			clnt_pcreateerror("rpcinfo");
304			printf("program %lu version %lu is not available\n",
305			    prognum, vers);
306			exit(1);
307		}
308		to.tv_sec = 10;
309		to.tv_usec = 0;
310		rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL,
311		    xdr_void, (char *)NULL, to);
312		if (pstatus(client, prognum, vers) < 0)
313			failure = 1;
314	}
315	(void) close(sock); /* Close it up again */
316	if (failure)
317		exit(1);
318}
319
320static void
321tcpping(portnum, argc, argv)
322	u_short portnum;
323	int argc;
324	char **argv;
325{
326	struct timeval to;
327	struct sockaddr_in addr;
328	enum clnt_stat rpc_stat;
329	CLIENT *client;
330	u_long prognum, vers, minvers, maxvers;
331	int sock = RPC_ANYSOCK;
332	struct rpc_err rpcerr;
333	int failure;
334
335	if (argc < 2 || argc > 3) {
336		usage();
337		exit(1);
338	}
339	prognum = getprognum(argv[1]);
340	get_inet_address(&addr, argv[0]);
341	failure = 0;
342	if (argc == 2) {
343		/*
344		 * A call to version 0 should fail with a program/version
345		 * mismatch, and give us the range of versions supported.
346		 */
347		addr.sin_port = htons(portnum);
348		if ((client = clnttcp_create(&addr, prognum, MIN_VERS,
349		    &sock, 0, 0)) == NULL) {
350			clnt_pcreateerror("rpcinfo");
351			printf("program %lu is not available\n",
352			    prognum);
353			exit(1);
354		}
355		to.tv_sec = 10;
356		to.tv_usec = 0;
357		rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL,
358		    xdr_void, (char *)NULL, to);
359		if (rpc_stat == RPC_PROGVERSMISMATCH) {
360			clnt_geterr(client, &rpcerr);
361			minvers = rpcerr.re_vers.low;
362			maxvers = rpcerr.re_vers.high;
363		} else if (rpc_stat == RPC_SUCCESS) {
364			/*
365			 * Oh dear, it DOES support version 0.
366			 * Let's try version MAX_VERS.
367			 */
368			addr.sin_port = htons(portnum);
369			if ((client = clnttcp_create(&addr, prognum, MAX_VERS,
370			    &sock, 0, 0)) == NULL) {
371				clnt_pcreateerror("rpcinfo");
372				printf("program %lu version %lu is not available\n",
373				    prognum, MAX_VERS);
374				exit(1);
375			}
376			to.tv_sec = 10;
377			to.tv_usec = 0;
378			rpc_stat = clnt_call(client, NULLPROC, xdr_void,
379			    (char *)NULL, xdr_void, (char *)NULL, to);
380			if (rpc_stat == RPC_PROGVERSMISMATCH) {
381				clnt_geterr(client, &rpcerr);
382				minvers = rpcerr.re_vers.low;
383				maxvers = rpcerr.re_vers.high;
384			} else if (rpc_stat == RPC_SUCCESS) {
385				/*
386				 * It also supports version MAX_VERS.
387				 * Looks like we have a wise guy.
388				 * OK, we give them information on all
389				 * 4 billion versions they support...
390				 */
391				minvers = 0;
392				maxvers = MAX_VERS;
393			} else {
394				(void) pstatus(client, prognum, MAX_VERS);
395				exit(1);
396			}
397		} else {
398			(void) pstatus(client, prognum, MIN_VERS);
399			exit(1);
400		}
401		clnt_destroy(client);
402		(void) close(sock);
403		sock = RPC_ANYSOCK; /* Re-initialize it for later */
404		for (vers = minvers; vers <= maxvers; vers++) {
405			addr.sin_port = htons(portnum);
406			if ((client = clnttcp_create(&addr, prognum, vers,
407			    &sock, 0, 0)) == NULL) {
408				clnt_pcreateerror("rpcinfo");
409				printf("program %lu version %lu is not available\n",
410				    prognum, vers);
411				exit(1);
412			}
413			to.tv_usec = 0;
414			to.tv_sec = 10;
415			rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL,
416			    xdr_void, (char *)NULL, to);
417			if (pstatus(client, prognum, vers) < 0)
418				failure = 1;
419			clnt_destroy(client);
420			(void) close(sock);
421			sock = RPC_ANYSOCK;
422		}
423	}
424	else {
425		vers = getvers(argv[2]);
426		addr.sin_port = htons(portnum);
427		if ((client = clnttcp_create(&addr, prognum, vers, &sock,
428		    0, 0)) == NULL) {
429			clnt_pcreateerror("rpcinfo");
430			printf("program %lu version %lu is not available\n",
431			    prognum, vers);
432			exit(1);
433		}
434		to.tv_usec = 0;
435		to.tv_sec = 10;
436		rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL,
437		    xdr_void, (char *)NULL, to);
438		if (pstatus(client, prognum, vers) < 0)
439			failure = 1;
440	}
441	if (failure)
442		exit(1);
443}
444
445/*
446 * This routine should take a pointer to an "rpc_err" structure, rather than
447 * a pointer to a CLIENT structure, but "clnt_perror" takes a pointer to
448 * a CLIENT structure rather than a pointer to an "rpc_err" structure.
449 * As such, we have to keep the CLIENT structure around in order to print
450 * a good error message.
451 */
452static int
453pstatus(client, prognum, vers)
454	register CLIENT *client;
455	u_long prognum;
456	u_long vers;
457{
458	struct rpc_err rpcerr;
459
460	clnt_geterr(client, &rpcerr);
461	if (rpcerr.re_status != RPC_SUCCESS) {
462		clnt_perror(client, "rpcinfo");
463		printf("program %lu version %lu is not available\n",
464		    prognum, vers);
465		return (-1);
466	} else {
467		printf("program %lu version %lu ready and waiting\n",
468		    prognum, vers);
469		return (0);
470	}
471}
472
473static void
474pmapdump(argc, argv)
475	int argc;
476	char **argv;
477{
478	struct sockaddr_in server_addr;
479	register struct hostent *hp;
480	struct pmaplist *head = NULL;
481	int socket = RPC_ANYSOCK;
482	struct timeval minutetimeout;
483	register CLIENT *client;
484	struct rpcent *rpc;
485
486	if (argc > 1) {
487		usage();
488		exit(1);
489	}
490	if (argc == 1)
491		get_inet_address(&server_addr, argv[0]);
492	else {
493		bzero((char *)&server_addr, sizeof server_addr);
494		server_addr.sin_family = AF_INET;
495		if ((hp = gethostbyname("localhost")) != NULL)
496			bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr,
497			    hp->h_length);
498		else
499			server_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
500	}
501	minutetimeout.tv_sec = 60;
502	minutetimeout.tv_usec = 0;
503	server_addr.sin_port = htons(PMAPPORT);
504	if ((client = clnttcp_create(&server_addr, PMAPPROG,
505	    PMAPVERS, &socket, 50, 500)) == NULL) {
506		clnt_pcreateerror("rpcinfo: can't contact portmapper");
507		exit(1);
508	}
509	if (clnt_call(client, PMAPPROC_DUMP, xdr_void, NULL,
510	    xdr_pmaplist, &head, minutetimeout) != RPC_SUCCESS) {
511		fprintf(stderr, "rpcinfo: can't contact portmapper: ");
512		clnt_perror(client, "rpcinfo");
513		exit(1);
514	}
515	if (head == NULL) {
516		printf("No remote programs registered.\n");
517	} else {
518		printf("   program vers proto   port\n");
519		for (; head != NULL; head = head->pml_next) {
520			printf("%10ld%5ld",
521			    head->pml_map.pm_prog,
522			    head->pml_map.pm_vers);
523			if (head->pml_map.pm_prot == IPPROTO_UDP)
524				printf("%6s",  "udp");
525			else if (head->pml_map.pm_prot == IPPROTO_TCP)
526				printf("%6s", "tcp");
527			else
528				printf("%6ld",  head->pml_map.pm_prot);
529			printf("%7ld",  head->pml_map.pm_port);
530			rpc = getrpcbynumber(head->pml_map.pm_prog);
531			if (rpc)
532				printf("  %s\n", rpc->r_name);
533			else
534				printf("\n");
535		}
536	}
537}
538
539/*
540 * reply_proc collects replies from the broadcast.
541 * to get a unique list of responses the output of rpcinfo should
542 * be piped through sort(1) and then uniq(1).
543 */
544
545/*ARGSUSED*/
546static bool_t
547reply_proc(res, who)
548	void *res;		/* Nothing comes back */
549	struct sockaddr_in *who; /* Who sent us the reply */
550{
551	register struct hostent *hp;
552
553	hp = gethostbyaddr((char *) &who->sin_addr, sizeof who->sin_addr,
554	    AF_INET);
555	printf("%s %s\n", inet_ntoa(who->sin_addr),
556	    (hp == NULL) ? "(unknown)" : hp->h_name);
557	return(FALSE);
558}
559
560static void
561brdcst(argc, argv)
562	int argc;
563	char **argv;
564{
565	enum clnt_stat rpc_stat;
566	u_long prognum, vers;
567
568	if (argc != 2) {
569		usage();
570		exit(1);
571	}
572	prognum = getprognum(argv[0]);
573	vers = getvers(argv[1]);
574	rpc_stat = clnt_broadcast(prognum, vers, NULLPROC, xdr_void,
575	    (char *)NULL, xdr_void, (char *)NULL, reply_proc);
576	if ((rpc_stat != RPC_SUCCESS) && (rpc_stat != RPC_TIMEDOUT)) {
577		fprintf(stderr, "rpcinfo: broadcast failed: %s\n",
578		    clnt_sperrno(rpc_stat));
579		exit(1);
580	}
581	exit(0);
582}
583
584static void
585deletereg(argc, argv)
586	int argc;
587	char **argv;
588{	u_long prog_num, version_num ;
589
590	if (argc != 2) {
591		usage() ;
592		exit(1) ;
593	}
594	if (getuid()) { /* This command allowed only to root */
595		fprintf(stderr, "Sorry. You are not root\n") ;
596		exit(1) ;
597	}
598	prog_num = getprognum(argv[0]);
599	version_num = getvers(argv[1]);
600	if ((pmap_unset(prog_num, version_num)) == 0) {
601		fprintf(stderr, "rpcinfo: Could not delete registration for prog %s version %s\n",
602			argv[0], argv[1]) ;
603		exit(1) ;
604	}
605}
606
607static void
608usage()
609{
610	fprintf(stderr, "Usage: rpcinfo [ -n portnum ] -u host prognum [ versnum ]\n");
611	fprintf(stderr, "       rpcinfo [ -n portnum ] -t host prognum [ versnum ]\n");
612	fprintf(stderr, "       rpcinfo -p [ host ]\n");
613	fprintf(stderr, "       rpcinfo -b prognum versnum\n");
614	fprintf(stderr, "       rpcinfo -d prognum versnum\n") ;
615}
616
617static u_long
618getprognum(arg)
619	char *arg;
620{
621	register struct rpcent *rpc;
622	register u_long prognum;
623
624	if (isalpha(*arg)) {
625		rpc = getrpcbyname(arg);
626		if (rpc == NULL) {
627			fprintf(stderr, "rpcinfo: %s is unknown service\n",
628			    arg);
629			exit(1);
630		}
631		prognum = rpc->r_number;
632	} else {
633		prognum = (u_long) atoi(arg);
634	}
635
636	return (prognum);
637}
638
639static u_long
640getvers(arg)
641	char *arg;
642{
643	register u_long vers;
644
645	vers = (int) atoi(arg);
646	return (vers);
647}
648
649static void
650get_inet_address(addr, host)
651	struct sockaddr_in *addr;
652	char *host;
653{
654	register struct hostent *hp;
655
656	bzero((char *)addr, sizeof *addr);
657	addr->sin_addr.s_addr = (u_long) inet_addr(host);
658	if (addr->sin_addr.s_addr == -1 || addr->sin_addr.s_addr == 0) {
659		if ((hp = gethostbyname(host)) == NULL) {
660			fprintf(stderr, "rpcinfo: %s is unknown host\n", host);
661			exit(1);
662		}
663		bcopy(hp->h_addr, (char *)&addr->sin_addr, hp->h_length);
664	}
665	addr->sin_family = AF_INET;
666}
667