xref: /freebsd/usr.bin/rpcinfo/rpcinfo.c (revision e99a5be3952a324ef039e8fe70761aa29850d62e)
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";*/
4 static 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 
59 static void	udpping(/*u_short portflag, int argc, char **argv*/);
60 static void	tcpping(/*u_short portflag, int argc, char **argv*/);
61 static int	pstatus(/*CLIENT *client, u_long prognum, u_long vers*/);
62 static void	pmapdump(/*int argc, char **argv*/);
63 static bool_t	reply_proc(/*void *res, struct sockaddr_in *who*/);
64 static void	brdcst(/*int argc, char **argv*/);
65 static void	deletereg(/* int argc, char **argv */) ;
66 static void	usage(/*void*/);
67 static u_long	getprognum(/*char *arg*/);
68 static u_long	getvers(/*char *arg*/);
69 static void	get_inet_address(/*struct sockaddr_in *addr, char *host*/);
70 extern u_long inet_addr();  /* in 4.2BSD, arpa/inet.h called that a in_addr */
71 extern 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 
83 int
84 main(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 
184 static void
185 udpping(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 
320 static void
321 tcpping(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  */
452 static int
453 pstatus(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 
473 static void
474 pmapdump(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*/
546 static bool_t
547 reply_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 
560 static void
561 brdcst(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 
584 static void
585 deletereg(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 
607 static void
608 usage()
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 
617 static u_long
618 getprognum(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 
639 static u_long
640 getvers(arg)
641 	char *arg;
642 {
643 	register u_long vers;
644 
645 	vers = (int) atoi(arg);
646 	return (vers);
647 }
648 
649 static void
650 get_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