/* * Copyright (c) 2010, Oracle America, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the "Oracle America, Inc." nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * rpcinfo: ping a particular rpc program * or dump the portmapper */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../version.h" #define PACKAGE _libc_intl_domainname #define MAXHOSTLEN 256 #define MIN_VERS ((u_long) 0) #define MAX_VERS ((u_long) 4294967295UL) static void udpping (u_short portflag, int argc, char **argv); static void tcpping (u_short portflag, int argc, char **argv); static int pstatus (CLIENT *client, u_long prognum, u_long vers); static void pmapdump (int argc, char **argv); static bool_t reply_proc (void *res, struct sockaddr_in *who); static void brdcst (int argc, char **argv) __attribute__ ((noreturn)); static void deletereg (int argc, char **argv); static void usage (FILE *stream); static void print_version (void); static u_long getprognum (char *arg); static u_long getvers (char *arg); static void get_inet_address (struct sockaddr_in *addr, char *host); /* * Functions to be performed. */ #define NONE 0 /* no function */ #define PMAPDUMP 1 /* dump portmapper registrations */ #define TCPPING 2 /* ping TCP service */ #define UDPPING 3 /* ping UDP service */ #define BRDCST 4 /* ping broadcast UDP service */ #define DELETES 5 /* delete registration for the service */ int main (int argc, char **argv) { register int c; int errflg; int function; u_short portnum; static const struct option long_options[] = { { "help", no_argument, NULL, 'H' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; setlocale (LC_ALL, ""); textdomain (_libc_intl_domainname); function = NONE; portnum = 0; errflg = 0; while ((c = getopt_long (argc, argv, "ptubdn:", long_options, NULL)) != -1) { switch (c) { case 'p': if (function != NONE) errflg = 1; else function = PMAPDUMP; break; case 't': if (function != NONE) errflg = 1; else function = TCPPING; break; case 'u': if (function != NONE) errflg = 1; else function = UDPPING; break; case 'b': if (function != NONE) errflg = 1; else function = BRDCST; break; case 'n': portnum = (u_short) atoi (optarg); /* hope we don't get bogus # */ break; case 'd': if (function != NONE) errflg = 1; else function = DELETES; break; case 'H': usage (stdout); return 0; case 'V': print_version (); return 0; case '?': errflg = 1; } } if (errflg || function == NONE) { usage (stderr); return 1; } switch (function) { case PMAPDUMP: if (portnum != 0) { usage (stderr); return 1; } pmapdump (argc - optind, argv + optind); break; case UDPPING: udpping (portnum, argc - optind, argv + optind); break; case TCPPING: tcpping (portnum, argc - optind, argv + optind); break; case BRDCST: if (portnum != 0) { usage (stderr); return 1; } brdcst (argc - optind, argv + optind); break; case DELETES: deletereg (argc - optind, argv + optind); break; } return 0; } static void udpping (portnum, argc, argv) u_short portnum; int argc; char **argv; { struct timeval to; struct sockaddr_in addr; enum clnt_stat rpc_stat; CLIENT *client; u_long prognum, vers, minvers, maxvers; int sock = RPC_ANYSOCK; struct rpc_err rpcerr; int failure; if (argc < 2 || argc > 3) { usage (stderr); exit (1); } prognum = getprognum (argv[1]); get_inet_address (&addr, argv[0]); /* Open the socket here so it will survive calls to clnt_destroy */ sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { perror ("rpcinfo: socket"); exit (1); } failure = 0; if (argc == 2) { /* * A call to version 0 should fail with a program/version * mismatch, and give us the range of versions supported. */ addr.sin_port = htons (portnum); to.tv_sec = 5; to.tv_usec = 0; if ((client = clntudp_create (&addr, prognum, (u_long) 0, to, &sock)) == NULL) { clnt_pcreateerror ("rpcinfo"); printf (_("program %lu is not available\n"), prognum); exit (1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = clnt_call (client, NULLPROC, (xdrproc_t) xdr_void, (char *) NULL, (xdrproc_t) xdr_void, (char *) NULL, to); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr (client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * Oh dear, it DOES support version 0. * Let's try version MAX_VERS. */ addr.sin_port = htons (portnum); to.tv_sec = 5; to.tv_usec = 0; if ((client = clntudp_create (&addr, prognum, MAX_VERS, to, &sock)) == NULL) { clnt_pcreateerror ("rpcinfo"); printf (_("program %lu version %lu is not available\n"), prognum, MAX_VERS); exit (1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = clnt_call (client, NULLPROC, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL, to); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr (client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * It also supports version MAX_VERS. * Looks like we have a wise guy. * OK, we give them information on all * 4 billion versions they support... */ minvers = 0; maxvers = MAX_VERS; } else { (void) pstatus (client, prognum, MAX_VERS); exit (1); } } else { (void) pstatus (client, prognum, (u_long) 0); exit (1); } clnt_destroy (client); for (vers = minvers; vers <= maxvers; vers++) { addr.sin_port = htons (portnum); to.tv_sec = 5; to.tv_usec = 0; if ((client = clntudp_create (&addr, prognum, vers, to, &sock)) == NULL) { clnt_pcreateerror ("rpcinfo"); printf (_("program %lu version %lu is not available\n"), prognum, vers); exit (1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = clnt_call (client, NULLPROC, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL, to); if (pstatus (client, prognum, vers) < 0) failure = 1; clnt_destroy (client); } } else { vers = getvers (argv[2]); addr.sin_port = htons (portnum); to.tv_sec = 5; to.tv_usec = 0; if ((client = clntudp_create (&addr, prognum, vers, to, &sock)) == NULL) { clnt_pcreateerror ("rpcinfo"); printf (_("program %lu version %lu is not available\n"), prognum, vers); exit (1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = clnt_call (client, 0, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL, to); if (pstatus (client, prognum, vers) < 0) failure = 1; } (void) close (sock); /* Close it up again */ if (failure) exit (1); } static void tcpping (portnum, argc, argv) u_short portnum; int argc; char **argv; { struct timeval to; struct sockaddr_in addr; enum clnt_stat rpc_stat; CLIENT *client; u_long prognum, vers, minvers, maxvers; int sock = RPC_ANYSOCK; struct rpc_err rpcerr; int failure; if (argc < 2 || argc > 3) { usage (stderr); exit (1); } prognum = getprognum (argv[1]); get_inet_address (&addr, argv[0]); failure = 0; if (argc == 2) { /* * A call to version 0 should fail with a program/version * mismatch, and give us the range of versions supported. */ addr.sin_port = htons (portnum); if ((client = clnttcp_create (&addr, prognum, MIN_VERS, &sock, 0, 0)) == NULL) { clnt_pcreateerror ("rpcinfo"); printf (_("program %lu is not available\n"), prognum); exit (1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = clnt_call (client, NULLPROC, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL, to); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr (client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * Oh dear, it DOES support version 0. * Let's try version MAX_VERS. */ addr.sin_port = htons (portnum); if ((client = clnttcp_create (&addr, prognum, MAX_VERS, &sock, 0, 0)) == NULL) { clnt_pcreateerror ("rpcinfo"); printf (_("program %lu version %lu is not available\n"), prognum, MAX_VERS); exit (1); } to.tv_sec = 10; to.tv_usec = 0; rpc_stat = clnt_call (client, NULLPROC, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL, to); if (rpc_stat == RPC_PROGVERSMISMATCH) { clnt_geterr (client, &rpcerr); minvers = rpcerr.re_vers.low; maxvers = rpcerr.re_vers.high; } else if (rpc_stat == RPC_SUCCESS) { /* * It also supports version MAX_VERS. * Looks like we have a wise guy. * OK, we give them information on all * 4 billion versions they support... */ minvers = 0; maxvers = MAX_VERS; } else { (void) pstatus (client, prognum, MAX_VERS); exit (1); } } else { (void) pstatus (client, prognum, MIN_VERS); exit (1); } clnt_destroy (client); (void) close (sock); sock = RPC_ANYSOCK; /* Re-initialize it for later */ for (vers = minvers; vers <= maxvers; vers++) { addr.sin_port = htons (portnum); if ((client = clnttcp_create (&addr, prognum, vers, &sock, 0, 0)) == NULL) { clnt_pcreateerror ("rpcinfo"); printf (_("program %lu version %lu is not available\n"), prognum, vers); exit (1); } to.tv_usec = 0; to.tv_sec = 10; rpc_stat = clnt_call (client, 0, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL, to); if (pstatus (client, prognum, vers) < 0) failure = 1; clnt_destroy (client); (void) close (sock); sock = RPC_ANYSOCK; } } else { vers = getvers (argv[2]); addr.sin_port = htons (portnum); if ((client = clnttcp_create (&addr, prognum, vers, &sock, 0, 0)) == NULL) { clnt_pcreateerror ("rpcinfo"); printf (_("program %lu version %lu is not available\n"), prognum, vers); exit (1); } to.tv_usec = 0; to.tv_sec = 10; rpc_stat = clnt_call (client, 0, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL, to); if (pstatus (client, prognum, vers) < 0) failure = 1; } if (failure) exit (1); } /* * This routine should take a pointer to an "rpc_err" structure, rather than * a pointer to a CLIENT structure, but "clnt_perror" takes a pointer to * a CLIENT structure rather than a pointer to an "rpc_err" structure. * As such, we have to keep the CLIENT structure around in order to print * a good error message. */ static int pstatus (client, prognum, vers) register CLIENT *client; u_long prognum; u_long vers; { struct rpc_err rpcerr; clnt_geterr (client, &rpcerr); if (rpcerr.re_status != RPC_SUCCESS) { clnt_perror (client, "rpcinfo"); printf (_("program %lu version %lu is not available\n"), prognum, vers); return -1; } else { printf (_("program %lu version %lu ready and waiting\n"), prognum, vers); return 0; } } static void pmapdump (argc, argv) int argc; char **argv; { struct sockaddr_in server_addr; register struct hostent *hp; struct pmaplist *head = NULL; int socket = RPC_ANYSOCK; struct timeval minutetimeout; register CLIENT *client; struct rpcent *rpc; if (argc > 1) { usage (stderr); exit (1); } if (argc == 1) get_inet_address (&server_addr, argv[0]); else { bzero ((char *) &server_addr, sizeof server_addr); server_addr.sin_family = AF_INET; if ((hp = gethostbyname ("localhost")) != NULL) memcpy ((caddr_t) & server_addr.sin_addr, hp->h_addr, hp->h_length); else server_addr.sin_addr.s_addr = inet_addr ("0.0.0.0"); } minutetimeout.tv_sec = 60; minutetimeout.tv_usec = 0; server_addr.sin_port = htons (PMAPPORT); if ((client = clnttcp_create (&server_addr, PMAPPROG, PMAPVERS, &socket, 50, 500)) == NULL) { clnt_pcreateerror (_("rpcinfo: can't contact portmapper")); exit (1); } if (clnt_call (client, PMAPPROC_DUMP, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_pmaplist, (caddr_t) &head, minutetimeout) != RPC_SUCCESS) { fputs (_("rpcinfo: can't contact portmapper"), stderr); fputs (": ", stderr); clnt_perror (client, "rpcinfo"); exit (1); } if (head == NULL) { fputs (_("No remote programs registered.\n"), stdout); } else { fputs (_(" program vers proto port\n"), stdout); for (; head != NULL; head = head->pml_next) { printf ("%10ld%5ld", head->pml_map.pm_prog, head->pml_map.pm_vers); if (head->pml_map.pm_prot == IPPROTO_UDP) printf ("%6s", "udp"); else if (head->pml_map.pm_prot == IPPROTO_TCP) printf ("%6s", "tcp"); else printf ("%6ld", head->pml_map.pm_prot); printf ("%7ld", head->pml_map.pm_port); rpc = getrpcbynumber (head->pml_map.pm_prog); if (rpc) printf (" %s\n", rpc->r_name); else printf ("\n"); } } } /* * reply_proc collects replies from the broadcast. * to get a unique list of responses the output of rpcinfo should * be piped through sort(1) and then uniq(1). */ /*ARGSUSED */ static bool_t reply_proc (res, who) void *res; /* Nothing comes back */ struct sockaddr_in *who; /* Who sent us the reply */ { register struct hostent *hp; hp = gethostbyaddr ((char *) &who->sin_addr, sizeof who->sin_addr, AF_INET); printf ("%s %s\n", inet_ntoa (who->sin_addr), (hp == NULL) ? _("(unknown)") : hp->h_name); return FALSE; } static void brdcst (argc, argv) int argc; char **argv; { enum clnt_stat rpc_stat; u_long prognum, vers; if (argc != 2) { usage (stderr); exit (1); } prognum = getprognum (argv[0]); vers = getvers (argv[1]); rpc_stat = clnt_broadcast (prognum, vers, NULLPROC, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL, (resultproc_t) reply_proc); if ((rpc_stat != RPC_SUCCESS) && (rpc_stat != RPC_TIMEDOUT)) { fprintf (stderr, _("rpcinfo: broadcast failed: %s\n"), clnt_sperrno (rpc_stat)); exit (1); } exit (0); } static void deletereg (argc, argv) int argc; char **argv; { u_long prog_num, version_num; if (argc != 2) { usage (stderr); exit (1); } if (getuid ()) { /* This command allowed only to root */ fputs (_("Sorry. You are not root\n"), stderr); exit (1); } prog_num = getprognum (argv[0]); version_num = getvers (argv[1]); if ((pmap_unset (prog_num, version_num)) == 0) { fprintf (stderr, _("rpcinfo: Could not delete registration for prog %s version %s\n"), argv[0], argv[1]); exit (1); } } static void usage (FILE *stream) { fputs (_("Usage: rpcinfo [ -n portnum ] -u host prognum [ versnum ]\n"), stream); fputs (_(" rpcinfo [ -n portnum ] -t host prognum [ versnum ]\n"), stream); fputs (_(" rpcinfo -p [ host ]\n"), stream); fputs (_(" rpcinfo -b prognum versnum\n"), stream); fputs (_(" rpcinfo -d prognum versnum\n"), stream); fputc ('\n', stream); fprintf (stream, _("\ For bug reporting instructions, please see:\n\ %s.\n"), REPORT_BUGS_TO); } static void print_version (void) { printf ("rpcinfo %s%s\n", PKGVERSION, VERSION); } static u_long getprognum (arg) char *arg; { register struct rpcent *rpc; register u_long prognum; if (isalpha (*arg)) { rpc = getrpcbyname (arg); if (rpc == NULL) { fprintf (stderr, _("rpcinfo: %s is unknown service\n"), arg); exit (1); } prognum = rpc->r_number; } else { prognum = (u_long) atoi (arg); } return prognum; } static u_long getvers (arg) char *arg; { register u_long vers; vers = (int) atoi (arg); return vers; } static void get_inet_address (addr, host) struct sockaddr_in *addr; char *host; { register struct hostent *hp; bzero ((char *) addr, sizeof *addr); addr->sin_addr.s_addr = (u_long) inet_addr (host); if (addr->sin_addr.s_addr == INADDR_NONE || addr->sin_addr.s_addr == INADDR_ANY) { if ((hp = gethostbyname (host)) == NULL) { fprintf (stderr, _("rpcinfo: %s is unknown host\n"), host); exit (1); } memmove ((char *) &addr->sin_addr, hp->h_addr, hp->h_length); } addr->sin_family = AF_INET; }