blob: 2b55eb4a9c99f0909919d733833be3ab41158bc3 [file] [log] [blame]
xf.libdd93d52023-05-12 07:10:14 -07001/*
2 * Copyright (c) 2010, Oracle America, Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 * * Neither the name of the "Oracle America, Inc." nor the names of its
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * rpcinfo: ping a particular rpc program
34 * or dump the portmapper
35 */
36
37#include <getopt.h>
38#include <string.h>
39#include <unistd.h>
40#include <rpc/rpc.h>
41#include <stdio.h>
42#include <sys/socket.h>
43#include <netinet/in.h>
44#include <arpa/inet.h>
45#include <netdb.h>
46#include <rpc/pmap_prot.h>
47#include <rpc/pmap_clnt.h>
48#include <signal.h>
49#include <ctype.h>
50#include <locale.h>
51#include <libintl.h>
52
53#include "../version.h"
54#define PACKAGE _libc_intl_domainname
55
56#define MAXHOSTLEN 256
57
58#define MIN_VERS ((u_long) 0)
59#define MAX_VERS ((u_long) 4294967295UL)
60
61static void udpping (u_short portflag, int argc, char **argv);
62static void tcpping (u_short portflag, int argc, char **argv);
63static int pstatus (CLIENT *client, u_long prognum, u_long vers);
64static void pmapdump (int argc, char **argv);
65static bool_t reply_proc (void *res, struct sockaddr_in *who);
66static void brdcst (int argc, char **argv) __attribute__ ((noreturn));
67static void deletereg (int argc, char **argv);
68static void usage (FILE *stream);
69static void print_version (void);
70static u_long getprognum (char *arg);
71static u_long getvers (char *arg);
72static 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
84int
85main (int argc, char **argv)
86{
87 register int c;
88 int errflg;
89 int function;
90 u_short portnum;
91 static const struct option long_options[] = {
92 { "help", no_argument, NULL, 'H' },
93 { "version", no_argument, NULL, 'V' },
94 { NULL, 0, NULL, 0 }
95 };
96
97 setlocale (LC_ALL, "");
98 textdomain (_libc_intl_domainname);
99
100 function = NONE;
101 portnum = 0;
102 errflg = 0;
103 while ((c = getopt_long (argc, argv, "ptubdn:", long_options, NULL)) != -1)
104 {
105 switch (c)
106 {
107
108 case 'p':
109 if (function != NONE)
110 errflg = 1;
111 else
112 function = PMAPDUMP;
113 break;
114
115 case 't':
116 if (function != NONE)
117 errflg = 1;
118 else
119 function = TCPPING;
120 break;
121
122 case 'u':
123 if (function != NONE)
124 errflg = 1;
125 else
126 function = UDPPING;
127 break;
128
129 case 'b':
130 if (function != NONE)
131 errflg = 1;
132 else
133 function = BRDCST;
134 break;
135
136 case 'n':
137 portnum = (u_short) atoi (optarg); /* hope we don't get bogus # */
138 break;
139
140 case 'd':
141 if (function != NONE)
142 errflg = 1;
143 else
144 function = DELETES;
145 break;
146
147 case 'H':
148 usage (stdout);
149 return 0;
150
151 case 'V':
152 print_version ();
153 return 0;
154
155 case '?':
156 errflg = 1;
157 }
158 }
159
160 if (errflg || function == NONE)
161 {
162 usage (stderr);
163 return 1;
164 }
165
166 switch (function)
167 {
168
169 case PMAPDUMP:
170 if (portnum != 0)
171 {
172 usage (stderr);
173 return 1;
174 }
175 pmapdump (argc - optind, argv + optind);
176 break;
177
178 case UDPPING:
179 udpping (portnum, argc - optind, argv + optind);
180 break;
181
182 case TCPPING:
183 tcpping (portnum, argc - optind, argv + optind);
184 break;
185
186 case BRDCST:
187 if (portnum != 0)
188 {
189 usage (stderr);
190 return 1;
191 }
192 brdcst (argc - optind, argv + optind);
193 break;
194
195 case DELETES:
196 deletereg (argc - optind, argv + optind);
197 break;
198 }
199
200 return 0;
201}
202
203static void
204udpping (u_short portnum, int argc, char **argv)
205{
206 struct timeval to;
207 struct sockaddr_in addr;
208 enum clnt_stat rpc_stat;
209 CLIENT *client;
210 u_long prognum, vers, minvers, maxvers;
211 int sock = RPC_ANYSOCK;
212 struct rpc_err rpcerr;
213 int failure;
214
215 if (argc < 2 || argc > 3)
216 {
217 usage (stderr);
218 exit (1);
219 }
220 prognum = getprognum (argv[1]);
221 get_inet_address (&addr, argv[0]);
222 /* Open the socket here so it will survive calls to clnt_destroy */
223 sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
224 if (sock < 0)
225 {
226 perror ("rpcinfo: socket");
227 exit (1);
228 }
229 failure = 0;
230 if (argc == 2)
231 {
232 /*
233 * A call to version 0 should fail with a program/version
234 * mismatch, and give us the range of versions supported.
235 */
236 addr.sin_port = htons (portnum);
237 to.tv_sec = 5;
238 to.tv_usec = 0;
239 if ((client = clntudp_create (&addr, prognum, (u_long) 0,
240 to, &sock)) == NULL)
241 {
242 clnt_pcreateerror ("rpcinfo");
243 printf (_("program %lu is not available\n"), prognum);
244 exit (1);
245 }
246 to.tv_sec = 10;
247 to.tv_usec = 0;
248 rpc_stat = clnt_call (client, NULLPROC, (xdrproc_t) xdr_void,
249 (char *) NULL, (xdrproc_t) xdr_void,
250 (char *) NULL, to);
251 if (rpc_stat == RPC_PROGVERSMISMATCH)
252 {
253 clnt_geterr (client, &rpcerr);
254 minvers = rpcerr.re_vers.low;
255 maxvers = rpcerr.re_vers.high;
256 }
257 else if (rpc_stat == RPC_SUCCESS)
258 {
259 /*
260 * Oh dear, it DOES support version 0.
261 * Let's try version MAX_VERS.
262 */
263 addr.sin_port = htons (portnum);
264 to.tv_sec = 5;
265 to.tv_usec = 0;
266 if ((client = clntudp_create (&addr, prognum, MAX_VERS,
267 to, &sock)) == NULL)
268 {
269 clnt_pcreateerror ("rpcinfo");
270 printf (_("program %lu version %lu is not available\n"),
271 prognum, MAX_VERS);
272 exit (1);
273 }
274 to.tv_sec = 10;
275 to.tv_usec = 0;
276 rpc_stat = clnt_call (client, NULLPROC, (xdrproc_t) xdr_void,
277 NULL, (xdrproc_t) xdr_void, NULL, to);
278 if (rpc_stat == RPC_PROGVERSMISMATCH)
279 {
280 clnt_geterr (client, &rpcerr);
281 minvers = rpcerr.re_vers.low;
282 maxvers = rpcerr.re_vers.high;
283 }
284 else if (rpc_stat == RPC_SUCCESS)
285 {
286 /*
287 * It also supports version MAX_VERS.
288 * Looks like we have a wise guy.
289 * OK, we give them information on all
290 * 4 billion versions they support...
291 */
292 minvers = 0;
293 maxvers = MAX_VERS;
294 }
295 else
296 {
297 (void) pstatus (client, prognum, MAX_VERS);
298 exit (1);
299 }
300 }
301 else
302 {
303 (void) pstatus (client, prognum, (u_long) 0);
304 exit (1);
305 }
306 clnt_destroy (client);
307 for (vers = minvers; vers <= maxvers; vers++)
308 {
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 {
315 clnt_pcreateerror ("rpcinfo");
316 printf (_("program %lu version %lu is not available\n"),
317 prognum, vers);
318 exit (1);
319 }
320 to.tv_sec = 10;
321 to.tv_usec = 0;
322 rpc_stat = clnt_call (client, NULLPROC, (xdrproc_t) xdr_void,
323 NULL, (xdrproc_t) xdr_void, NULL, to);
324 if (pstatus (client, prognum, vers) < 0)
325 failure = 1;
326 clnt_destroy (client);
327 }
328 }
329 else
330 {
331 vers = getvers (argv[2]);
332 addr.sin_port = htons (portnum);
333 to.tv_sec = 5;
334 to.tv_usec = 0;
335 if ((client = clntudp_create (&addr, prognum, vers,
336 to, &sock)) == NULL)
337 {
338 clnt_pcreateerror ("rpcinfo");
339 printf (_("program %lu version %lu is not available\n"),
340 prognum, vers);
341 exit (1);
342 }
343 to.tv_sec = 10;
344 to.tv_usec = 0;
345 rpc_stat = clnt_call (client, 0, (xdrproc_t) xdr_void, NULL,
346 (xdrproc_t) xdr_void, NULL, to);
347 if (pstatus (client, prognum, vers) < 0)
348 failure = 1;
349 }
350 (void) close (sock); /* Close it up again */
351 if (failure)
352 exit (1);
353}
354
355static void
356tcpping (u_short portnum, int argc, char **argv)
357{
358 struct timeval to;
359 struct sockaddr_in addr;
360 enum clnt_stat rpc_stat;
361 CLIENT *client;
362 u_long prognum, vers, minvers, maxvers;
363 int sock = RPC_ANYSOCK;
364 struct rpc_err rpcerr;
365 int failure;
366
367 if (argc < 2 || argc > 3)
368 {
369 usage (stderr);
370 exit (1);
371 }
372 prognum = getprognum (argv[1]);
373 get_inet_address (&addr, argv[0]);
374 failure = 0;
375 if (argc == 2)
376 {
377 /*
378 * A call to version 0 should fail with a program/version
379 * mismatch, and give us the range of versions supported.
380 */
381 addr.sin_port = htons (portnum);
382 if ((client = clnttcp_create (&addr, prognum, MIN_VERS,
383 &sock, 0, 0)) == NULL)
384 {
385 clnt_pcreateerror ("rpcinfo");
386 printf (_("program %lu is not available\n"), prognum);
387 exit (1);
388 }
389 to.tv_sec = 10;
390 to.tv_usec = 0;
391 rpc_stat = clnt_call (client, NULLPROC, (xdrproc_t) xdr_void, NULL,
392 (xdrproc_t) xdr_void, NULL, to);
393 if (rpc_stat == RPC_PROGVERSMISMATCH)
394 {
395 clnt_geterr (client, &rpcerr);
396 minvers = rpcerr.re_vers.low;
397 maxvers = rpcerr.re_vers.high;
398 }
399 else if (rpc_stat == RPC_SUCCESS)
400 {
401 /*
402 * Oh dear, it DOES support version 0.
403 * Let's try version MAX_VERS.
404 */
405 addr.sin_port = htons (portnum);
406 if ((client = clnttcp_create (&addr, prognum, MAX_VERS,
407 &sock, 0, 0)) == NULL)
408 {
409 clnt_pcreateerror ("rpcinfo");
410 printf (_("program %lu version %lu is not available\n"),
411 prognum, MAX_VERS);
412 exit (1);
413 }
414 to.tv_sec = 10;
415 to.tv_usec = 0;
416 rpc_stat = clnt_call (client, NULLPROC, (xdrproc_t) xdr_void,
417 NULL, (xdrproc_t) xdr_void, NULL, to);
418 if (rpc_stat == RPC_PROGVERSMISMATCH)
419 {
420 clnt_geterr (client, &rpcerr);
421 minvers = rpcerr.re_vers.low;
422 maxvers = rpcerr.re_vers.high;
423 }
424 else if (rpc_stat == RPC_SUCCESS)
425 {
426 /*
427 * It also supports version MAX_VERS.
428 * Looks like we have a wise guy.
429 * OK, we give them information on all
430 * 4 billion versions they support...
431 */
432 minvers = 0;
433 maxvers = MAX_VERS;
434 }
435 else
436 {
437 (void) pstatus (client, prognum, MAX_VERS);
438 exit (1);
439 }
440 }
441 else
442 {
443 (void) pstatus (client, prognum, MIN_VERS);
444 exit (1);
445 }
446 clnt_destroy (client);
447 (void) close (sock);
448 sock = RPC_ANYSOCK; /* Re-initialize it for later */
449 for (vers = minvers; vers <= maxvers; vers++)
450 {
451 addr.sin_port = htons (portnum);
452 if ((client = clnttcp_create (&addr, prognum, vers,
453 &sock, 0, 0)) == NULL)
454 {
455 clnt_pcreateerror ("rpcinfo");
456 printf (_("program %lu version %lu is not available\n"),
457 prognum, vers);
458 exit (1);
459 }
460 to.tv_usec = 0;
461 to.tv_sec = 10;
462 rpc_stat = clnt_call (client, 0, (xdrproc_t) xdr_void, NULL,
463 (xdrproc_t) xdr_void, NULL, to);
464 if (pstatus (client, prognum, vers) < 0)
465 failure = 1;
466 clnt_destroy (client);
467 (void) close (sock);
468 sock = RPC_ANYSOCK;
469 }
470 }
471 else
472 {
473 vers = getvers (argv[2]);
474 addr.sin_port = htons (portnum);
475 if ((client = clnttcp_create (&addr, prognum, vers, &sock,
476 0, 0)) == NULL)
477 {
478 clnt_pcreateerror ("rpcinfo");
479 printf (_("program %lu version %lu is not available\n"),
480 prognum, vers);
481 exit (1);
482 }
483 to.tv_usec = 0;
484 to.tv_sec = 10;
485 rpc_stat = clnt_call (client, 0, (xdrproc_t) xdr_void, NULL,
486 (xdrproc_t) xdr_void, NULL, to);
487 if (pstatus (client, prognum, vers) < 0)
488 failure = 1;
489 }
490 if (failure)
491 exit (1);
492}
493
494/*
495 * This routine should take a pointer to an "rpc_err" structure, rather than
496 * a pointer to a CLIENT structure, but "clnt_perror" takes a pointer to
497 * a CLIENT structure rather than a pointer to an "rpc_err" structure.
498 * As such, we have to keep the CLIENT structure around in order to print
499 * a good error message.
500 */
501static int
502pstatus (register CLIENT *client, u_long prognum, u_long vers)
503{
504 struct rpc_err rpcerr;
505
506 clnt_geterr (client, &rpcerr);
507 if (rpcerr.re_status != RPC_SUCCESS)
508 {
509 clnt_perror (client, "rpcinfo");
510 printf (_("program %lu version %lu is not available\n"), prognum, vers);
511 return -1;
512 }
513 else
514 {
515 printf (_("program %lu version %lu ready and waiting\n"), prognum, vers);
516 return 0;
517 }
518}
519
520static void
521pmapdump (int argc, char **argv)
522{
523 struct sockaddr_in server_addr;
524 register struct hostent *hp;
525 struct pmaplist *head = NULL;
526 int socket = RPC_ANYSOCK;
527 struct timeval minutetimeout;
528 register CLIENT *client;
529 struct rpcent *rpc;
530
531 if (argc > 1)
532 {
533 usage (stderr);
534 exit (1);
535 }
536 if (argc == 1)
537 get_inet_address (&server_addr, argv[0]);
538 else
539 {
540 bzero ((char *) &server_addr, sizeof server_addr);
541 server_addr.sin_family = AF_INET;
542 if ((hp = gethostbyname ("localhost")) != NULL)
543 memcpy ((caddr_t) & server_addr.sin_addr, hp->h_addr,
544 hp->h_length);
545 else
546 server_addr.sin_addr.s_addr = inet_addr ("0.0.0.0");
547 }
548 minutetimeout.tv_sec = 60;
549 minutetimeout.tv_usec = 0;
550 server_addr.sin_port = htons (PMAPPORT);
551 if ((client = clnttcp_create (&server_addr, PMAPPROG,
552 PMAPVERS, &socket, 50, 500)) == NULL)
553 {
554 clnt_pcreateerror (_("rpcinfo: can't contact portmapper"));
555 exit (1);
556 }
557 if (clnt_call (client, PMAPPROC_DUMP, (xdrproc_t) xdr_void, NULL,
558 (xdrproc_t) xdr_pmaplist, (caddr_t) &head,
559 minutetimeout) != RPC_SUCCESS)
560 {
561 fputs (_("rpcinfo: can't contact portmapper"), stderr);
562 fputs (": ", stderr);
563 clnt_perror (client, "rpcinfo");
564 exit (1);
565 }
566 if (head == NULL)
567 {
568 fputs (_("No remote programs registered.\n"), stdout);
569 }
570 else
571 {
572 fputs (_(" program vers proto port\n"), stdout);
573 for (; head != NULL; head = head->pml_next)
574 {
575 printf ("%10ld%5ld",
576 head->pml_map.pm_prog,
577 head->pml_map.pm_vers);
578 if (head->pml_map.pm_prot == IPPROTO_UDP)
579 printf ("%6s", "udp");
580 else if (head->pml_map.pm_prot == IPPROTO_TCP)
581 printf ("%6s", "tcp");
582 else
583 printf ("%6ld", head->pml_map.pm_prot);
584 printf ("%7ld", head->pml_map.pm_port);
585 rpc = getrpcbynumber (head->pml_map.pm_prog);
586 if (rpc)
587 printf (" %s\n", rpc->r_name);
588 else
589 printf ("\n");
590 }
591 }
592}
593
594/*
595 * reply_proc collects replies from the broadcast.
596 * to get a unique list of responses the output of rpcinfo should
597 * be piped through sort(1) and then uniq(1).
598 */
599
600/*ARGSUSED */
601static bool_t
602reply_proc (res, who)
603 void *res; /* Nothing comes back */
604 struct sockaddr_in *who; /* Who sent us the reply */
605{
606 register struct hostent *hp;
607
608 hp = gethostbyaddr ((char *) &who->sin_addr, sizeof who->sin_addr,
609 AF_INET);
610 printf ("%s %s\n", inet_ntoa (who->sin_addr),
611 (hp == NULL) ? _("(unknown)") : hp->h_name);
612 return FALSE;
613}
614
615static void
616brdcst (int argc, char **argv)
617{
618 enum clnt_stat rpc_stat;
619 u_long prognum, vers;
620
621 if (argc != 2)
622 {
623 usage (stderr);
624 exit (1);
625 }
626 prognum = getprognum (argv[0]);
627 vers = getvers (argv[1]);
628 rpc_stat = clnt_broadcast (prognum, vers, NULLPROC, (xdrproc_t) xdr_void,
629 NULL, (xdrproc_t) xdr_void, NULL,
630 (resultproc_t) reply_proc);
631 if ((rpc_stat != RPC_SUCCESS) && (rpc_stat != RPC_TIMEDOUT))
632 {
633 fprintf (stderr, _("rpcinfo: broadcast failed: %s\n"),
634 clnt_sperrno (rpc_stat));
635 exit (1);
636 }
637 exit (0);
638}
639
640static void
641deletereg (int argc, char **argv)
642{
643 u_long prog_num, version_num;
644
645 if (argc != 2)
646 {
647 usage (stderr);
648 exit (1);
649 }
650 if (getuid ())
651 { /* This command allowed only to root */
652 fputs (_("Sorry. You are not root\n"), stderr);
653 exit (1);
654 }
655 prog_num = getprognum (argv[0]);
656 version_num = getvers (argv[1]);
657 if ((pmap_unset (prog_num, version_num)) == 0)
658 {
659 fprintf (stderr, _("rpcinfo: Could not delete registration for prog %s version %s\n"),
660 argv[0], argv[1]);
661 exit (1);
662 }
663}
664
665static void
666usage (FILE *stream)
667{
668 fputs (_("Usage: rpcinfo [ -n portnum ] -u host prognum [ versnum ]\n"),
669 stream);
670 fputs (_(" rpcinfo [ -n portnum ] -t host prognum [ versnum ]\n"),
671 stream);
672 fputs (_(" rpcinfo -p [ host ]\n"), stream);
673 fputs (_(" rpcinfo -b prognum versnum\n"), stream);
674 fputs (_(" rpcinfo -d prognum versnum\n"), stream);
675 fputc ('\n', stream);
676 fprintf (stream, _("\
677For bug reporting instructions, please see:\n\
678%s.\n"), REPORT_BUGS_TO);
679}
680
681static void
682print_version (void)
683{
684 printf ("rpcinfo %s%s\n", PKGVERSION, VERSION);
685}
686
687static u_long
688getprognum (char *arg)
689{
690 register struct rpcent *rpc;
691 register u_long prognum;
692
693 if (isalpha (*arg))
694 {
695 rpc = getrpcbyname (arg);
696 if (rpc == NULL)
697 {
698 fprintf (stderr, _("rpcinfo: %s is unknown service\n"), arg);
699 exit (1);
700 }
701 prognum = rpc->r_number;
702 }
703 else
704 {
705 prognum = (u_long) atoi (arg);
706 }
707
708 return prognum;
709}
710
711static u_long
712getvers (char *arg)
713{
714 register u_long vers;
715
716 vers = (int) atoi (arg);
717 return vers;
718}
719
720static void
721get_inet_address (struct sockaddr_in *addr, char *host)
722{
723 register struct hostent *hp;
724
725 bzero ((char *) addr, sizeof *addr);
726 addr->sin_addr.s_addr = (u_long) inet_addr (host);
727 if (addr->sin_addr.s_addr == INADDR_NONE
728 || addr->sin_addr.s_addr == INADDR_ANY)
729 {
730 if ((hp = gethostbyname (host)) == NULL)
731 {
732 fprintf (stderr, _("rpcinfo: %s is unknown host\n"),
733 host);
734 exit (1);
735 }
736 memmove ((char *) &addr->sin_addr, hp->h_addr, hp->h_length);
737 }
738 addr->sin_family = AF_INET;
739}