blob: 16c03542c0787d52e4114a31bb197eae13491c56 [file] [log] [blame]
/*
* 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 <getopt.h>
#include <string.h>
#include <unistd.h>
#include <rpc/rpc.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
#include <signal.h>
#include <ctype.h>
#include <locale.h>
#include <libintl.h>
#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);
fputs (_("\
For bug reporting instructions, please see:\n\
<http://www.gnu.org/software/libc/bugs.html>.\n"), stream);
}
static void
print_version (void)
{
printf ("rpcinfo (GNU %s) %s\n", PACKAGE, 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;
}