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