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