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