#define _(x) x /* @(#)rpcinfo.c 2.2 88/08/11 4.0 RPCSRC */ #if !defined(lint) && defined (SCCSID) static char sccsid[] = "@(#)rpcinfo.c 1.22 87/08/12 SMI"; #endif /* * Copyright (C) 1986, Sun Microsystems, Inc. */ /* * rpcinfo: ping a particular rpc program * or dump the portmapper */ /* * Sun RPC is a product of Sun Microsystems, Inc. and is provided for * unrestricted use provided that this legend is included on all tape * media and as a part of the software program in whole or part. Users * may copy or modify Sun RPC without charge, but are not authorized * to license or distribute it to anyone else except as part of a product or * program developed by the user. * * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. * * Sun RPC is provided with no support and without any obligation on the * part of Sun Microsystems, Inc. to assist in its use, correction, * modification or enhancement. * * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC * OR ANY PART THEREOF. * * In no event will Sun Microsystems, Inc. be liable for any lost revenue * or profits or other special, indirect and consequential damages, even if * Sun has been advised of the possibility of such damages. * * Sun Microsystems, Inc. * 2550 Garcia Avenue * Mountain View, California 94043 */ #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> #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 (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; setlocale (LC_ALL, ""); function = NONE; portnum = 0; errflg = 0; while ((c = getopt (argc, argv, "ptubdn:")) != -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 '?': errflg = 1; } } if (errflg || function == NONE) { usage (); return 1; } switch (function) { case PMAPDUMP: if (portnum != 0) { usage (); 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 (); 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 (); 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 (); 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 (); 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) bcopy (hp->h_addr, (caddr_t) & server_addr.sin_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); fflush(stdout); return FALSE; } static void brdcst (argc, argv) int argc; char **argv; { enum clnt_stat rpc_stat; u_long prognum, vers; if (argc != 2) { usage (); 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 (); 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 () { fputs (_("Usage: rpcinfo [ -n portnum ] -u host prognum [ versnum ]\n"), stderr); fputs (_(" rpcinfo [ -n portnum ] -t host prognum [ versnum ]\n"), stderr); fputs (_(" rpcinfo -p [ host ]\n"), stderr); fputs (_(" rpcinfo -b prognum versnum\n"), stderr); fputs (_(" rpcinfo -d prognum versnum\n"), stderr); } 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); } bcopy (hp->h_addr, (char *) &addr->sin_addr, hp->h_length); } addr->sin_family = AF_INET; }