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