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