diff --git a/usr.bin/rpcinfo/Makefile b/usr.bin/rpcinfo/Makefile new file mode 100644 index 000000000000..1605846fbea1 --- /dev/null +++ b/usr.bin/rpcinfo/Makefile @@ -0,0 +1,8 @@ +# from: @(#)Makefile 5.2 (Berkeley) 5/11/90 +# $Id: Makefile,v 1.2 1993/11/10 03:46:45 smace Exp $ + +PROG= rpcinfo +MAN8 = rpcinfo.8 + + +.include diff --git a/usr.bin/rpcinfo/rpcinfo.8 b/usr.bin/rpcinfo/rpcinfo.8 new file mode 100644 index 000000000000..c2c556decc81 --- /dev/null +++ b/usr.bin/rpcinfo/rpcinfo.8 @@ -0,0 +1,166 @@ +.\" from: @(#)rpcinfo.8c 2.2 88/08/03 4.0 RPCSRC; from 1.24 88/02/25 SMI +.\" $Id: rpcinfo.8,v 1.1 1993/09/13 23:22:41 jtc Exp $ +.\" +.Dd December 17, 1987 +.Dt RPCINFO 8 +.Os +.Sh NAME +.Nm rpcinfo +.Nd report RPC information +.Sh SYNOPSIS +.Nm rpcinfo +.Fl p +.Op Ar host +.Nm rpcinfo +.Op Fl n Ar portnum +.Fl u Ar host +.Ar program +.Op Ar version +.Nm rpcinfo +.Op Fl n Ar portnum +.Fl t Ar host +.Ar program +.Op Ar version +.Nm rpcinfo +.Fl b +.Ar program version +.Nm rpcinfo +.Fl d +.Ar program version +.Sh DESCRIPTION +.Nm rpcinfo +makes an +.Tn RPC +call to an +.Tn RPC +server and reports what it finds. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl p +Probe the portmapper on +.Ar host , +and print a list of all registered +.Tn RPC +programs. If +.Ar host +is not specified, it defaults to the value returned by +.Xr hostname 1 . +.It Fl u +Make an +.Tn RPC +call to procedure 0 of +.Ar program +on the specified +.Ar host +using +.Tn UDP , +and report whether a response was received. +.It Fl t +Make an +.Tn RPC +call to procedure 0 of +.Ar program +on the specified +.Ar host +using +.Tn TCP , +and report whether a response was received. +.It Fl n +Use +.Ar portnum +as the port number for the +.Fl t +and +.Fl u +options instead of the port number given by the portmapper. +.It Fl b +Make an +.Tn RPC +broadcast to procedure 0 of the specified +.Ar program +and +.Ar version +using +.Tn UDP +and report all hosts that respond. +.It Fl d +Delete registration for the +.Tn RPC +service of the specified +.Ar program +and +.Ar version . +This option can be exercised only by the super-user. +.El +.Pp +The +.Ar program +argument can be either a name or a number. +.Pp +If a +.Ar version +is specified, +.Nm rpcinfo +attempts to call that version of the specified +.Ar program . +Otherwise, +.Nm rpcinfo +attempts to find all the registered version +numbers for the specified +.Ar program +by calling version 0 (which is presumed not +to exist; if it does exist, +.Ar rpcinfo +attempts to obtain this information by calling +an extremely high version +number instead) and attempts to call each registered version. +Note: the version number is required for +.Fl b +and +.Fl d +options. +.Sh EXAMPLES +To show all of the +.Tn RPC +services registered on the local machine use: +.Pp +.Dl example% rpcinfo -p +.Pp +To show all of the +.Tn RPC +services registered on the machine named +.Ar klaxon +use: +.Pp +.Dl example% rpcinfo -p klaxon +.Pp +To show all machines on the local net that are running the Yellow Pages +service use: +.Pp +.Dl example% rpcinfo -b ypserv 'version' | uniq +.Pp +where 'version' is the current Yellow Pages version obtained from the +results of the +.Fl p +switch above. +.Pp +To delete the registration for version 1 of the +.Nm walld +service use: +.Pp +.Dl example% rpcinfo -d walld 1 +.Sh SEE ALSO +.Xr rpc 5 , +.Xr portmap 8 +.Rs +.%T "RPC Programming Guide" +.Re +.Sh BUGS +In releases prior to SunOS 3.0, the Network File System (NFS) did not +register itself with the portmapper; +.Nm rpcinfo +cannot be used to make +.Tn RPC +calls to the +.Tn NFS +server on hosts running such releases. diff --git a/usr.bin/rpcinfo/rpcinfo.c b/usr.bin/rpcinfo/rpcinfo.c new file mode 100644 index 000000000000..5e8ba12b8296 --- /dev/null +++ b/usr.bin/rpcinfo/rpcinfo.c @@ -0,0 +1,666 @@ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpcinfo.c 1.22 87/08/12 SMI";*/ +/*static char sccsid[] = "from: @(#)rpcinfo.c 2.2 88/08/11 4.0 RPCSRC";*/ +static char rcsid[] = "$Id: rpcinfo.c,v 1.1 1993/09/13 23:22:42 jtc Exp $"; +#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 +#include +#include +#include +#include +#include +#include +#include + +#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*/); +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*/); +extern u_long inet_addr(); /* in 4.2BSD, arpa/inet.h called that a in_addr */ +extern char *inet_ntoa(); + +/* + * 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(argc, argv) + int argc; + char **argv; +{ + register int c; + extern char *optarg; + extern int optind; + int errflg; + int function; + u_short portnum; + + function = NONE; + portnum = 0; + errflg = 0; + while ((c = getopt(argc, argv, "ptubdn:")) != EOF) { + 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, xdr_void, (char *)NULL, + 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, xdr_void, + (char *)NULL, 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) { + /* + * 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, xdr_void, + (char *)NULL, xdr_void, (char *)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, xdr_void, (char *)NULL, + xdr_void, (char *)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, xdr_void, (char *)NULL, + 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); + 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, xdr_void, + (char *)NULL, 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) { + /* + * 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, xdr_void, (char *)NULL, + xdr_void, (char *)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, xdr_void, (char *)NULL, + xdr_void, (char *)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, xdr_void, NULL, + xdr_pmaplist, &head, minutetimeout) != RPC_SUCCESS) { + fprintf(stderr, "rpcinfo: can't contact portmapper: "); + clnt_perror(client, "rpcinfo"); + exit(1); + } + if (head == NULL) { + printf("No remote programs registered.\n"); + } else { + printf(" program vers proto port\n"); + 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(); + exit(1); + } + prognum = getprognum(argv[0]); + vers = getvers(argv[1]); + rpc_stat = clnt_broadcast(prognum, vers, NULLPROC, xdr_void, + (char *)NULL, xdr_void, (char *)NULL, 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 */ + fprintf(stderr, "Sorry. You are not root\n") ; + 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() +{ + fprintf(stderr, "Usage: rpcinfo [ -n portnum ] -u host prognum [ versnum ]\n"); + fprintf(stderr, " rpcinfo [ -n portnum ] -t host prognum [ versnum ]\n"); + fprintf(stderr, " rpcinfo -p [ host ]\n"); + fprintf(stderr, " rpcinfo -b prognum versnum\n"); + fprintf(stderr, " rpcinfo -d prognum versnum\n") ; +} + +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 == -1 || addr->sin_addr.s_addr == 0) { + 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; +}