f67e4a8fc7
associated changes that had to happen to make this possible as well as bugs fixed along the way. Bring in required TLI library routines to support this. Since we don't support TLI we've essentially copied what NetBSD has done, adding a thin layer to emulate direct the TLI calls into BSD socket calls. This is mostly from Sun's tirpc release that was made in 1994, however some fixes were backported from the 1999 release (supposedly only made available after this porting effort was underway). The submitter has agreed to continue on and bring us up to the 1999 release. Several key features are introduced with this update: Client calls are thread safe. (1999 code has server side thread safe) Updated, a more modern interface. Many userland updates were done to bring the code up to par with the recent RPC API. There is an update to the pthreads library, a function pthread_main_np() was added to emulate a function of Sun's threads library. While we're at it, bring in NetBSD's lockd, it's been far too long of a wait. New rpcbind(8) replaces portmap(8) (supporting communication over an authenticated Unix-domain socket, and by default only allowing set and unset requests over that channel). It's much more secure than the old portmapper. Umount(8), mountd(8), mount_nfs(8), nfsd(8) have also been upgraded to support TI-RPC and to support IPV6. Umount(8) is also fixed to unmount pathnames longer than 80 chars, which are currently truncated by the Kernel statfs structure. Submitted by: Martin Blapp <mb@imp.ch> Manpage review: ru Secure RPC implemented by: wpaul
1716 lines
43 KiB
C
1716 lines
43 KiB
C
/* $NetBSD: rpcinfo.c,v 1.15 2000/10/04 20:09:05 mjl Exp $ */
|
|
/* $FreeBSD$ */
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/* #ident "@(#)rpcinfo.c 1.18 93/07/05 SMI" */
|
|
|
|
#if 0
|
|
#ifndef lint
|
|
static char sccsid[] = "@(#)rpcinfo.c 1.16 89/04/05 Copyr 1986 Sun Micro";
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
* rpcinfo: ping a particular rpc program
|
|
* or dump the the registered programs on the remote machine.
|
|
*/
|
|
|
|
/*
|
|
* We are for now defining PORTMAP here. It doesnt even compile
|
|
* unless it is defined.
|
|
*/
|
|
#ifndef PORTMAP
|
|
#define PORTMAP
|
|
#endif
|
|
|
|
/*
|
|
* If PORTMAP is defined, rpcinfo will talk to both portmapper and
|
|
* rpcbind programs; else it talks only to rpcbind. In the latter case
|
|
* all the portmapper specific options such as -u, -t, -p become void.
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <rpc/rpc.h>
|
|
#include <stdio.h>
|
|
#include <rpc/rpcb_prot.h>
|
|
#include <rpc/rpcent.h>
|
|
#include <rpc/nettype.h>
|
|
#include <rpc/rpc_com.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <err.h>
|
|
#include <ctype.h>
|
|
|
|
#ifdef PORTMAP /* Support for version 2 portmapper */
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
#include <rpc/pmap_prot.h>
|
|
#include <rpc/pmap_clnt.h>
|
|
#endif
|
|
|
|
#define MAXHOSTLEN 256
|
|
#define MIN_VERS ((u_long) 0)
|
|
#define MAX_VERS ((u_long) 4294967295UL)
|
|
#define UNKNOWN "unknown"
|
|
|
|
/*
|
|
* 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 BROADCAST 4 /* ping broadcast service */
|
|
#define DELETES 5 /* delete registration for the service */
|
|
#define ADDRPING 6 /* pings at the given address */
|
|
#define PROGPING 7 /* pings a program on a given host */
|
|
#define RPCBDUMP 8 /* dump rpcbind registrations */
|
|
#define RPCBDUMP_SHORT 9 /* dump rpcbind registrations - short version */
|
|
#define RPCBADDRLIST 10 /* dump addr list about one prog */
|
|
#define RPCBGETSTAT 11 /* Get statistics */
|
|
|
|
struct netidlist {
|
|
char *netid;
|
|
struct netidlist *next;
|
|
};
|
|
|
|
struct verslist {
|
|
int vers;
|
|
struct verslist *next;
|
|
};
|
|
|
|
struct rpcbdump_short {
|
|
u_long prog;
|
|
struct verslist *vlist;
|
|
struct netidlist *nlist;
|
|
struct rpcbdump_short *next;
|
|
char *owner;
|
|
};
|
|
|
|
|
|
|
|
#ifdef PORTMAP
|
|
static void ip_ping(u_short, char *, int, char **);
|
|
static CLIENT *clnt_com_create(struct sockaddr_in *, u_long, u_long, int *,
|
|
char *);
|
|
static void pmapdump(int, char **);
|
|
static void get_inet_address(struct sockaddr_in *, char *);
|
|
#endif
|
|
|
|
static bool_t reply_proc(void *, struct netbuf *, struct netconfig *);
|
|
static void brdcst(int, char **);
|
|
static void addrping(char *, char *, int, char **);
|
|
static void progping(char *, int, char **);
|
|
static CLIENT *clnt_addr_create(char *, struct netconfig *, u_long, u_long);
|
|
static CLIENT *clnt_rpcbind_create(char *, int, struct netbuf **);
|
|
static CLIENT *getclnthandle(char *, struct netconfig *, u_long,
|
|
struct netbuf **);
|
|
static CLIENT *local_rpcb(u_long, u_long);
|
|
static int pstatus(CLIENT *, u_long, u_long);
|
|
static void rpcbdump(int, char *, int, char **);
|
|
static void rpcbgetstat(int, char **);
|
|
static void rpcbaddrlist(char *, int, char **);
|
|
static void deletereg(char *, int, char **);
|
|
static void print_rmtcallstat(int, rpcb_stat *);
|
|
static void print_getaddrstat(int, rpcb_stat *);
|
|
static void usage(void);
|
|
static u_long getprognum(char *);
|
|
static u_long getvers(char *);
|
|
static char *spaces(int);
|
|
static bool_t add_version(struct rpcbdump_short *, u_long);
|
|
static bool_t add_netid(struct rpcbdump_short *, char *);
|
|
|
|
int main(int argc, char **argv);
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
register int c;
|
|
int errflg;
|
|
int function;
|
|
char *netid = NULL;
|
|
char *address = NULL;
|
|
#ifdef PORTMAP
|
|
char *strptr;
|
|
u_short portnum = 0;
|
|
#endif
|
|
|
|
function = NONE;
|
|
errflg = 0;
|
|
#ifdef PORTMAP
|
|
while ((c = getopt(argc, argv, "a:bdlmn:pstT:u")) != -1) {
|
|
#else
|
|
while ((c = getopt(argc, argv, "a:bdlmn:sT:")) != -1) {
|
|
#endif
|
|
switch (c) {
|
|
#ifdef PORTMAP
|
|
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 'n':
|
|
portnum = (u_short) strtol(optarg, &strptr, 10);
|
|
if (strptr == optarg || *strptr != '\0') {
|
|
fprintf(stderr,
|
|
"rpcinfo: %s is illegal port number\n",
|
|
optarg);
|
|
exit(1);
|
|
}
|
|
break;
|
|
#endif
|
|
case 'a':
|
|
address = optarg;
|
|
if (function != NONE)
|
|
errflg = 1;
|
|
else
|
|
function = ADDRPING;
|
|
break;
|
|
case 'b':
|
|
if (function != NONE)
|
|
errflg = 1;
|
|
else
|
|
function = BROADCAST;
|
|
break;
|
|
|
|
case 'd':
|
|
if (function != NONE)
|
|
errflg = 1;
|
|
else
|
|
function = DELETES;
|
|
break;
|
|
|
|
case 'l':
|
|
if (function != NONE)
|
|
errflg = 1;
|
|
else
|
|
function = RPCBADDRLIST;
|
|
break;
|
|
|
|
case 'm':
|
|
if (function != NONE)
|
|
errflg = 1;
|
|
else
|
|
function = RPCBGETSTAT;
|
|
break;
|
|
|
|
case 's':
|
|
if (function != NONE)
|
|
errflg = 1;
|
|
else
|
|
function = RPCBDUMP_SHORT;
|
|
break;
|
|
|
|
case 'T':
|
|
netid = optarg;
|
|
break;
|
|
case '?':
|
|
errflg = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (errflg || ((function == ADDRPING) && !netid)) {
|
|
usage();
|
|
return (1);
|
|
}
|
|
|
|
if (function == NONE) {
|
|
if (argc - optind > 1)
|
|
function = PROGPING;
|
|
else
|
|
function = RPCBDUMP;
|
|
}
|
|
|
|
switch (function) {
|
|
#ifdef PORTMAP
|
|
case PMAPDUMP:
|
|
if (portnum != 0) {
|
|
usage();
|
|
return (1);
|
|
}
|
|
pmapdump(argc - optind, argv + optind);
|
|
break;
|
|
|
|
case UDPPING:
|
|
ip_ping(portnum, "udp", argc - optind, argv + optind);
|
|
break;
|
|
|
|
case TCPPING:
|
|
ip_ping(portnum, "tcp", argc - optind, argv + optind);
|
|
break;
|
|
#endif
|
|
case BROADCAST:
|
|
brdcst(argc - optind, argv + optind);
|
|
break;
|
|
case DELETES:
|
|
deletereg(netid, argc - optind, argv + optind);
|
|
break;
|
|
case ADDRPING:
|
|
addrping(address, netid, argc - optind, argv + optind);
|
|
break;
|
|
case PROGPING:
|
|
progping(netid, argc - optind, argv + optind);
|
|
break;
|
|
case RPCBDUMP:
|
|
case RPCBDUMP_SHORT:
|
|
rpcbdump(function, netid, argc - optind, argv + optind);
|
|
break;
|
|
case RPCBGETSTAT:
|
|
rpcbgetstat(argc - optind, argv + optind);
|
|
break;
|
|
case RPCBADDRLIST:
|
|
rpcbaddrlist(netid, argc - optind, argv + optind);
|
|
break;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static CLIENT *
|
|
local_rpcb(u_long prog, u_long vers)
|
|
{
|
|
struct netbuf nbuf;
|
|
struct sockaddr_un sun;
|
|
int sock;
|
|
|
|
memset(&sun, 0, sizeof sun);
|
|
sock = socket(AF_LOCAL, SOCK_STREAM, 0);
|
|
if (sock < 0)
|
|
return NULL;
|
|
|
|
sun.sun_family = AF_LOCAL;
|
|
strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
|
|
nbuf.len = sun.sun_len = SUN_LEN(&sun);
|
|
nbuf.maxlen = sizeof (struct sockaddr_un);
|
|
nbuf.buf = &sun;
|
|
|
|
return clnt_vc_create(sock, &nbuf, prog, vers, 0, 0);
|
|
}
|
|
|
|
#ifdef PORTMAP
|
|
static CLIENT *
|
|
clnt_com_create(struct sockaddr_in *addr, u_long prog, u_long vers,
|
|
int *fdp, char *trans)
|
|
{
|
|
CLIENT *clnt;
|
|
|
|
if (strcmp(trans, "tcp") == 0) {
|
|
clnt = clnttcp_create(addr, prog, vers, fdp, 0, 0);
|
|
} else {
|
|
struct timeval to;
|
|
|
|
to.tv_sec = 5;
|
|
to.tv_usec = 0;
|
|
clnt = clntudp_create(addr, prog, vers, to, fdp);
|
|
}
|
|
if (clnt == (CLIENT *)NULL) {
|
|
clnt_pcreateerror("rpcinfo");
|
|
if (vers == MIN_VERS)
|
|
printf("program %lu is not available\n", prog);
|
|
else
|
|
printf("program %lu version %lu is not available\n",
|
|
prog, vers);
|
|
exit(1);
|
|
}
|
|
return (clnt);
|
|
}
|
|
|
|
/*
|
|
* If portnum is 0, then go and get the address from portmapper, which happens
|
|
* transparently through clnt*_create(); If version number is not given, it
|
|
* tries to find out the version number by making a call to version 0 and if
|
|
* that fails, it obtains the high order and the low order version number. If
|
|
* version 0 calls succeeds, it tries for MAXVERS call and repeats the same.
|
|
*/
|
|
static void
|
|
ip_ping(u_short portnum, char *trans, int argc, char **argv)
|
|
{
|
|
CLIENT *client;
|
|
int fd = RPC_ANYFD;
|
|
struct timeval to;
|
|
struct sockaddr_in addr;
|
|
enum clnt_stat rpc_stat;
|
|
u_long prognum, vers, minvers, maxvers;
|
|
struct rpc_err rpcerr;
|
|
int failure = 0;
|
|
|
|
if (argc < 2 || argc > 3) {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
to.tv_sec = 10;
|
|
to.tv_usec = 0;
|
|
prognum = getprognum(argv[1]);
|
|
get_inet_address(&addr, argv[0]);
|
|
if (argc == 2) { /* Version number not known */
|
|
/*
|
|
* A call to version 0 should fail with a program/version
|
|
* mismatch, and give us the range of versions supported.
|
|
*/
|
|
vers = MIN_VERS;
|
|
} else {
|
|
vers = getvers(argv[2]);
|
|
}
|
|
addr.sin_port = htons(portnum);
|
|
client = clnt_com_create(&addr, prognum, vers, &fd, trans);
|
|
rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void,
|
|
(char *)NULL, (xdrproc_t) xdr_void, (char *)NULL,
|
|
to);
|
|
if (argc != 2) {
|
|
/* Version number was known */
|
|
if (pstatus(client, prognum, vers) < 0)
|
|
exit(1);
|
|
(void) CLNT_DESTROY(client);
|
|
return;
|
|
}
|
|
/* Version number not known */
|
|
(void) CLNT_CONTROL(client, CLSET_FD_NCLOSE, (char *)NULL);
|
|
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.
|
|
*/
|
|
(void) CLNT_DESTROY(client);
|
|
addr.sin_port = htons(portnum);
|
|
client = clnt_com_create(&addr, prognum, MAX_VERS, &fd, trans);
|
|
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) {
|
|
/*
|
|
* 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);
|
|
}
|
|
(void) CLNT_DESTROY(client);
|
|
for (vers = minvers; vers <= maxvers; vers++) {
|
|
addr.sin_port = htons(portnum);
|
|
client = clnt_com_create(&addr, prognum, vers, &fd, trans);
|
|
rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void,
|
|
(char *)NULL, (xdrproc_t) xdr_void,
|
|
(char *)NULL, to);
|
|
if (pstatus(client, prognum, vers) < 0)
|
|
failure = 1;
|
|
(void) CLNT_DESTROY(client);
|
|
}
|
|
if (failure)
|
|
exit(1);
|
|
(void) close(fd);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Dump all the portmapper registerations
|
|
*/
|
|
static void
|
|
pmapdump(int argc, char **argv)
|
|
{
|
|
struct sockaddr_in server_addr;
|
|
struct pmaplist *head = NULL;
|
|
int socket = RPC_ANYSOCK;
|
|
struct timeval minutetimeout;
|
|
register CLIENT *client;
|
|
struct rpcent *rpc;
|
|
enum clnt_stat clnt_st;
|
|
struct rpc_err err;
|
|
char *host;
|
|
|
|
if (argc > 1) {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
if (argc == 1) {
|
|
host = argv[0];
|
|
get_inet_address(&server_addr, host);
|
|
server_addr.sin_port = htons(PMAPPORT);
|
|
client = clnttcp_create(&server_addr, PMAPPROG, PMAPVERS,
|
|
&socket, 50, 500);
|
|
} else
|
|
client = local_rpcb(PMAPPROG, PMAPVERS);
|
|
|
|
if (client == NULL) {
|
|
if (rpc_createerr.cf_stat == RPC_TLIERROR) {
|
|
/*
|
|
* "Misc. TLI error" is not too helpful. Most likely
|
|
* the connection to the remote server timed out, so
|
|
* this error is at least less perplexing.
|
|
*/
|
|
rpc_createerr.cf_stat = RPC_PMAPFAILURE;
|
|
rpc_createerr.cf_error.re_status = RPC_FAILED;
|
|
}
|
|
clnt_pcreateerror("rpcinfo: can't contact portmapper");
|
|
exit(1);
|
|
}
|
|
|
|
minutetimeout.tv_sec = 60;
|
|
minutetimeout.tv_usec = 0;
|
|
|
|
clnt_st = CLNT_CALL(client, PMAPPROC_DUMP, (xdrproc_t) xdr_void,
|
|
NULL, (xdrproc_t) xdr_pmaplist_ptr, (char *)&head,
|
|
minutetimeout);
|
|
if (clnt_st != RPC_SUCCESS) {
|
|
if ((clnt_st == RPC_PROGVERSMISMATCH) ||
|
|
(clnt_st == RPC_PROGUNAVAIL)) {
|
|
CLNT_GETERR(client, &err);
|
|
if (err.re_vers.low > PMAPVERS)
|
|
fprintf(stderr,
|
|
"%s does not support portmapper. Try rpcinfo %s instead\n",
|
|
host, host);
|
|
exit(1);
|
|
}
|
|
clnt_perror(client, "rpcinfo: can't contact portmapper");
|
|
exit(1);
|
|
}
|
|
if (head == NULL) {
|
|
printf("No remote programs registered.\n");
|
|
} else {
|
|
printf(" program vers proto port service\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 if (head->pml_map.pm_prot == IPPROTO_ST)
|
|
printf("%6s", "unix");
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_inet_address(struct sockaddr_in *addr, char *host)
|
|
{
|
|
struct netconfig *nconf;
|
|
struct addrinfo hints, *res;
|
|
int error;
|
|
|
|
(void) memset((char *)addr, 0, sizeof (*addr));
|
|
addr->sin_addr.s_addr = inet_addr(host);
|
|
if (addr->sin_addr.s_addr == -1 || addr->sin_addr.s_addr == 0) {
|
|
if ((nconf = __rpc_getconfip("udp")) == NULL &&
|
|
(nconf = __rpc_getconfip("tcp")) == NULL) {
|
|
fprintf(stderr,
|
|
"rpcinfo: couldn't find a suitable transport\n");
|
|
exit(1);
|
|
} else {
|
|
memset(&hints, 0, sizeof hints);
|
|
hints.ai_family = AF_INET;
|
|
if ((error = getaddrinfo(host, "rpcbind", &hints, &res))
|
|
!= 0) {
|
|
fprintf(stderr, "rpcinfo: %s: %s\n",
|
|
host, gai_strerror(error));
|
|
exit(1);
|
|
} else {
|
|
memcpy(addr, res->ai_addr, res->ai_addrlen);
|
|
freeaddrinfo(res);
|
|
}
|
|
(void) freenetconfigent(nconf);
|
|
}
|
|
} else {
|
|
addr->sin_family = AF_INET;
|
|
}
|
|
}
|
|
#endif /* PORTMAP */
|
|
|
|
/*
|
|
* 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(void *res, struct netbuf *who, struct netconfig *nconf)
|
|
/* void *res; Nothing comes back */
|
|
/* struct netbuf *who; Who sent us the reply */
|
|
/* struct netconfig *nconf; On which transport the reply came */
|
|
{
|
|
char *uaddr;
|
|
char hostbuf[NI_MAXHOST];
|
|
char *hostname;
|
|
struct sockaddr *sa = (struct sockaddr *)who->buf;
|
|
|
|
if (getnameinfo(sa, sa->sa_len, hostbuf, NI_MAXHOST, NULL, 0, 0)) {
|
|
hostname = UNKNOWN;
|
|
} else {
|
|
hostname = hostbuf;
|
|
}
|
|
if (!(uaddr = taddr2uaddr(nconf, who))) {
|
|
uaddr = UNKNOWN;
|
|
}
|
|
printf("%s\t%s\n", uaddr, hostname);
|
|
if (strcmp(uaddr, UNKNOWN))
|
|
free((char *)uaddr);
|
|
return (FALSE);
|
|
}
|
|
|
|
static void
|
|
brdcst(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 = rpc_broadcast(prognum, vers, NULLPROC,
|
|
(xdrproc_t) xdr_void, (char *)NULL, (xdrproc_t) xdr_void,
|
|
(char *)NULL, (resultproc_t) reply_proc, NULL);
|
|
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 bool_t
|
|
add_version(struct rpcbdump_short *rs, u_long vers)
|
|
{
|
|
struct verslist *vl;
|
|
|
|
for (vl = rs->vlist; vl; vl = vl->next)
|
|
if (vl->vers == vers)
|
|
break;
|
|
if (vl)
|
|
return (TRUE);
|
|
vl = (struct verslist *)malloc(sizeof (struct verslist));
|
|
if (vl == NULL)
|
|
return (FALSE);
|
|
vl->vers = vers;
|
|
vl->next = rs->vlist;
|
|
rs->vlist = vl;
|
|
return (TRUE);
|
|
}
|
|
|
|
static bool_t
|
|
add_netid(struct rpcbdump_short *rs, char *netid)
|
|
{
|
|
struct netidlist *nl;
|
|
|
|
for (nl = rs->nlist; nl; nl = nl->next)
|
|
if (strcmp(nl->netid, netid) == 0)
|
|
break;
|
|
if (nl)
|
|
return (TRUE);
|
|
nl = (struct netidlist *)malloc(sizeof (struct netidlist));
|
|
if (nl == NULL)
|
|
return (FALSE);
|
|
nl->netid = netid;
|
|
nl->next = rs->nlist;
|
|
rs->nlist = nl;
|
|
return (TRUE);
|
|
}
|
|
|
|
static void
|
|
rpcbdump(int dumptype, char *netid, int argc, char **argv)
|
|
{
|
|
rpcblist_ptr head = NULL;
|
|
struct timeval minutetimeout;
|
|
register CLIENT *client;
|
|
struct rpcent *rpc;
|
|
char *host;
|
|
struct netidlist *nl;
|
|
struct verslist *vl;
|
|
struct rpcbdump_short *rs, *rs_tail;
|
|
char buf[256];
|
|
enum clnt_stat clnt_st;
|
|
struct rpc_err err;
|
|
struct rpcbdump_short *rs_head = NULL;
|
|
|
|
if (argc > 1) {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
if (argc == 1) {
|
|
host = argv[0];
|
|
if (netid == NULL) {
|
|
client = clnt_rpcbind_create(host, RPCBVERS, NULL);
|
|
} else {
|
|
struct netconfig *nconf;
|
|
|
|
nconf = getnetconfigent(netid);
|
|
if (nconf == NULL) {
|
|
nc_perror("rpcinfo: invalid transport");
|
|
exit(1);
|
|
}
|
|
client = getclnthandle(host, nconf, RPCBVERS, NULL);
|
|
if (nconf)
|
|
(void) freenetconfigent(nconf);
|
|
}
|
|
} else
|
|
client = local_rpcb(PMAPPROG, RPCBVERS);
|
|
|
|
if (client == (CLIENT *)NULL) {
|
|
clnt_pcreateerror("rpcinfo: can't contact rpcbind");
|
|
exit(1);
|
|
}
|
|
|
|
minutetimeout.tv_sec = 60;
|
|
minutetimeout.tv_usec = 0;
|
|
clnt_st = CLNT_CALL(client, RPCBPROC_DUMP, (xdrproc_t) xdr_void,
|
|
NULL, (xdrproc_t) xdr_rpcblist_ptr, (char *) &head,
|
|
minutetimeout);
|
|
if (clnt_st != RPC_SUCCESS) {
|
|
if ((clnt_st == RPC_PROGVERSMISMATCH) ||
|
|
(clnt_st == RPC_PROGUNAVAIL)) {
|
|
int vers;
|
|
|
|
CLNT_GETERR(client, &err);
|
|
if (err.re_vers.low == RPCBVERS4) {
|
|
vers = RPCBVERS4;
|
|
clnt_control(client, CLSET_VERS, (char *)&vers);
|
|
clnt_st = CLNT_CALL(client, RPCBPROC_DUMP,
|
|
(xdrproc_t) xdr_void, NULL,
|
|
(xdrproc_t) xdr_rpcblist_ptr, (char *) &head,
|
|
minutetimeout);
|
|
if (clnt_st != RPC_SUCCESS)
|
|
goto failed;
|
|
} else {
|
|
if (err.re_vers.high == PMAPVERS) {
|
|
int high, low;
|
|
struct pmaplist *pmaphead = NULL;
|
|
rpcblist_ptr list, prev;
|
|
|
|
vers = PMAPVERS;
|
|
clnt_control(client, CLSET_VERS, (char *)&vers);
|
|
clnt_st = CLNT_CALL(client, PMAPPROC_DUMP,
|
|
(xdrproc_t) xdr_void, NULL,
|
|
(xdrproc_t) xdr_pmaplist_ptr,
|
|
(char *)&pmaphead, minutetimeout);
|
|
if (clnt_st != RPC_SUCCESS)
|
|
goto failed;
|
|
/*
|
|
* convert to rpcblist_ptr format
|
|
*/
|
|
for (head = NULL; pmaphead != NULL;
|
|
pmaphead = pmaphead->pml_next) {
|
|
list = (rpcblist *)malloc(sizeof (rpcblist));
|
|
if (list == NULL)
|
|
goto error;
|
|
if (head == NULL)
|
|
head = list;
|
|
else
|
|
prev->rpcb_next = (rpcblist_ptr) list;
|
|
|
|
list->rpcb_next = NULL;
|
|
list->rpcb_map.r_prog = pmaphead->pml_map.pm_prog;
|
|
list->rpcb_map.r_vers = pmaphead->pml_map.pm_vers;
|
|
if (pmaphead->pml_map.pm_prot == IPPROTO_UDP)
|
|
list->rpcb_map.r_netid = "udp";
|
|
else if (pmaphead->pml_map.pm_prot == IPPROTO_TCP)
|
|
list->rpcb_map.r_netid = "tcp";
|
|
else {
|
|
#define MAXLONG_AS_STRING "2147483648"
|
|
list->rpcb_map.r_netid =
|
|
malloc(strlen(MAXLONG_AS_STRING) + 1);
|
|
if (list->rpcb_map.r_netid == NULL)
|
|
goto error;
|
|
sprintf(list->rpcb_map.r_netid, "%6ld",
|
|
pmaphead->pml_map.pm_prot);
|
|
}
|
|
list->rpcb_map.r_owner = UNKNOWN;
|
|
low = pmaphead->pml_map.pm_port & 0xff;
|
|
high = (pmaphead->pml_map.pm_port >> 8) & 0xff;
|
|
list->rpcb_map.r_addr = strdup("0.0.0.0.XXX.XXX");
|
|
sprintf(&list->rpcb_map.r_addr[8], "%d.%d",
|
|
high, low);
|
|
prev = list;
|
|
}
|
|
}
|
|
}
|
|
} else { /* any other error */
|
|
failed:
|
|
clnt_perror(client, "rpcinfo: can't contact rpcbind: ");
|
|
exit(1);
|
|
}
|
|
}
|
|
if (head == NULL) {
|
|
printf("No remote programs registered.\n");
|
|
} else if (dumptype == RPCBDUMP) {
|
|
printf(
|
|
" program version netid address service owner\n");
|
|
for (; head != NULL; head = head->rpcb_next) {
|
|
printf("%10u%5u ",
|
|
head->rpcb_map.r_prog, head->rpcb_map.r_vers);
|
|
printf("%-9s ", head->rpcb_map.r_netid);
|
|
printf("%-22s", head->rpcb_map.r_addr);
|
|
rpc = getrpcbynumber(head->rpcb_map.r_prog);
|
|
if (rpc)
|
|
printf(" %-10s", rpc->r_name);
|
|
else
|
|
printf(" %-10s", "-");
|
|
printf(" %s\n", head->rpcb_map.r_owner);
|
|
}
|
|
} else if (dumptype == RPCBDUMP_SHORT) {
|
|
for (; head != NULL; head = head->rpcb_next) {
|
|
for (rs = rs_head; rs; rs = rs->next)
|
|
if (head->rpcb_map.r_prog == rs->prog)
|
|
break;
|
|
if (rs == NULL) {
|
|
rs = (struct rpcbdump_short *)
|
|
malloc(sizeof (struct rpcbdump_short));
|
|
if (rs == NULL)
|
|
goto error;
|
|
rs->next = NULL;
|
|
if (rs_head == NULL) {
|
|
rs_head = rs;
|
|
rs_tail = rs;
|
|
} else {
|
|
rs_tail->next = rs;
|
|
rs_tail = rs;
|
|
}
|
|
rs->prog = head->rpcb_map.r_prog;
|
|
rs->owner = head->rpcb_map.r_owner;
|
|
rs->nlist = NULL;
|
|
rs->vlist = NULL;
|
|
}
|
|
if (add_version(rs, head->rpcb_map.r_vers) == FALSE)
|
|
goto error;
|
|
if (add_netid(rs, head->rpcb_map.r_netid) == FALSE)
|
|
goto error;
|
|
}
|
|
printf(
|
|
" program version(s) netid(s) service owner\n");
|
|
for (rs = rs_head; rs; rs = rs->next) {
|
|
char *p = buf;
|
|
|
|
printf("%10ld ", rs->prog);
|
|
for (vl = rs->vlist; vl; vl = vl->next) {
|
|
sprintf(p, "%d", vl->vers);
|
|
p = p + strlen(p);
|
|
if (vl->next)
|
|
sprintf(p++, ",");
|
|
}
|
|
printf("%-10s", buf);
|
|
buf[0] = NULL;
|
|
for (nl = rs->nlist; nl; nl = nl->next) {
|
|
strcat(buf, nl->netid);
|
|
if (nl->next)
|
|
strcat(buf, ",");
|
|
}
|
|
printf("%-32s", buf);
|
|
rpc = getrpcbynumber(rs->prog);
|
|
if (rpc)
|
|
printf(" %-11s", rpc->r_name);
|
|
else
|
|
printf(" %-11s", "-");
|
|
printf(" %s\n", rs->owner);
|
|
}
|
|
}
|
|
clnt_destroy(client);
|
|
return;
|
|
error: fprintf(stderr, "rpcinfo: no memory\n");
|
|
return;
|
|
}
|
|
|
|
static char nullstring[] = "\000";
|
|
|
|
static void
|
|
rpcbaddrlist(char *netid, int argc, char **argv)
|
|
{
|
|
rpcb_entry_list_ptr head = NULL;
|
|
struct timeval minutetimeout;
|
|
register CLIENT *client;
|
|
struct rpcent *rpc;
|
|
char *host;
|
|
RPCB parms;
|
|
struct netbuf *targaddr;
|
|
|
|
if (argc != 3) {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
host = argv[0];
|
|
if (netid == NULL) {
|
|
client = clnt_rpcbind_create(host, RPCBVERS4, &targaddr);
|
|
} else {
|
|
struct netconfig *nconf;
|
|
|
|
nconf = getnetconfigent(netid);
|
|
if (nconf == NULL) {
|
|
nc_perror("rpcinfo: invalid transport");
|
|
exit(1);
|
|
}
|
|
client = getclnthandle(host, nconf, RPCBVERS4, &targaddr);
|
|
if (nconf)
|
|
(void) freenetconfigent(nconf);
|
|
}
|
|
if (client == (CLIENT *)NULL) {
|
|
clnt_pcreateerror("rpcinfo: can't contact rpcbind");
|
|
exit(1);
|
|
}
|
|
minutetimeout.tv_sec = 60;
|
|
minutetimeout.tv_usec = 0;
|
|
|
|
parms.r_prog = getprognum(argv[1]);
|
|
parms.r_vers = getvers(argv[2]);
|
|
parms.r_netid = client->cl_netid;
|
|
if (targaddr == NULL) {
|
|
parms.r_addr = nullstring; /* for XDRing */
|
|
} else {
|
|
/*
|
|
* We also send the remote system the address we
|
|
* used to contact it in case it can help it
|
|
* connect back with us
|
|
*/
|
|
struct netconfig *nconf;
|
|
|
|
nconf = getnetconfigent(client->cl_netid);
|
|
if (nconf != NULL) {
|
|
parms.r_addr = taddr2uaddr(nconf, targaddr);
|
|
if (parms.r_addr == NULL)
|
|
parms.r_addr = nullstring;
|
|
freenetconfigent(nconf);
|
|
} else {
|
|
parms.r_addr = nullstring; /* for XDRing */
|
|
}
|
|
free(targaddr->buf);
|
|
free(targaddr);
|
|
}
|
|
parms.r_owner = nullstring;
|
|
|
|
if (CLNT_CALL(client, RPCBPROC_GETADDRLIST, (xdrproc_t) xdr_rpcb,
|
|
(char *) &parms, (xdrproc_t) xdr_rpcb_entry_list_ptr,
|
|
(char *) &head, minutetimeout) != RPC_SUCCESS) {
|
|
clnt_perror(client, "rpcinfo: can't contact rpcbind: ");
|
|
exit(1);
|
|
}
|
|
if (head == NULL) {
|
|
printf("No remote programs registered.\n");
|
|
} else {
|
|
printf(
|
|
" program vers tp_family/name/class address\t\t service\n");
|
|
for (; head != NULL; head = head->rpcb_entry_next) {
|
|
rpcb_entry *re;
|
|
char buf[128];
|
|
|
|
re = &head->rpcb_entry_map;
|
|
printf("%10u%3u ",
|
|
parms.r_prog, parms.r_vers);
|
|
sprintf(buf, "%s/%s/%s ",
|
|
re->r_nc_protofmly, re->r_nc_proto,
|
|
re->r_nc_semantics == NC_TPI_CLTS ? "clts" :
|
|
re->r_nc_semantics == NC_TPI_COTS ? "cots" :
|
|
"cots_ord");
|
|
printf("%-24s", buf);
|
|
printf("%-24s", re->r_maddr);
|
|
rpc = getrpcbynumber(parms.r_prog);
|
|
if (rpc)
|
|
printf(" %-13s", rpc->r_name);
|
|
else
|
|
printf(" %-13s", "-");
|
|
printf("\n");
|
|
}
|
|
}
|
|
clnt_destroy(client);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* monitor rpcbind
|
|
*/
|
|
static void
|
|
rpcbgetstat(int argc, char **argv)
|
|
{
|
|
rpcb_stat_byvers inf;
|
|
struct timeval minutetimeout;
|
|
register CLIENT *client;
|
|
char *host;
|
|
int i, j;
|
|
rpcbs_addrlist *pa;
|
|
rpcbs_rmtcalllist *pr;
|
|
int cnt, flen;
|
|
#define MAXFIELD 64
|
|
char fieldbuf[MAXFIELD];
|
|
#define MAXLINE 256
|
|
char linebuf[MAXLINE];
|
|
char *cp, *lp;
|
|
char *pmaphdr[] = {
|
|
"NULL", "SET", "UNSET", "GETPORT",
|
|
"DUMP", "CALLIT"
|
|
};
|
|
char *rpcb3hdr[] = {
|
|
"NULL", "SET", "UNSET", "GETADDR", "DUMP", "CALLIT", "TIME",
|
|
"U2T", "T2U"
|
|
};
|
|
char *rpcb4hdr[] = {
|
|
"NULL", "SET", "UNSET", "GETADDR", "DUMP", "CALLIT", "TIME",
|
|
"U2T", "T2U", "VERADDR", "INDRECT", "GETLIST", "GETSTAT"
|
|
};
|
|
|
|
#define TABSTOP 8
|
|
|
|
if (argc >= 1) {
|
|
host = argv[0];
|
|
client = clnt_rpcbind_create(host, RPCBVERS4, NULL);
|
|
} else
|
|
client = local_rpcb(PMAPPROG, RPCBVERS4);
|
|
if (client == (CLIENT *)NULL) {
|
|
clnt_pcreateerror("rpcinfo: can't contact rpcbind");
|
|
exit(1);
|
|
}
|
|
minutetimeout.tv_sec = 60;
|
|
minutetimeout.tv_usec = 0;
|
|
memset((char *)&inf, 0, sizeof (rpcb_stat_byvers));
|
|
if (CLNT_CALL(client, RPCBPROC_GETSTAT, (xdrproc_t) xdr_void, NULL,
|
|
(xdrproc_t) xdr_rpcb_stat_byvers, (char *)&inf, minutetimeout)
|
|
!= RPC_SUCCESS) {
|
|
clnt_perror(client, "rpcinfo: can't contact rpcbind: ");
|
|
exit(1);
|
|
}
|
|
printf("PORTMAP (version 2) statistics\n");
|
|
lp = linebuf;
|
|
for (i = 0; i <= rpcb_highproc_2; i++) {
|
|
fieldbuf[0] = '\0';
|
|
switch (i) {
|
|
case PMAPPROC_SET:
|
|
sprintf(fieldbuf, "%d/", inf[RPCBVERS_2_STAT].setinfo);
|
|
break;
|
|
case PMAPPROC_UNSET:
|
|
sprintf(fieldbuf, "%d/",
|
|
inf[RPCBVERS_2_STAT].unsetinfo);
|
|
break;
|
|
case PMAPPROC_GETPORT:
|
|
cnt = 0;
|
|
for (pa = inf[RPCBVERS_2_STAT].addrinfo; pa;
|
|
pa = pa->next)
|
|
cnt += pa->success;
|
|
sprintf(fieldbuf, "%d/", cnt);
|
|
break;
|
|
case PMAPPROC_CALLIT:
|
|
cnt = 0;
|
|
for (pr = inf[RPCBVERS_2_STAT].rmtinfo; pr;
|
|
pr = pr->next)
|
|
cnt += pr->success;
|
|
sprintf(fieldbuf, "%d/", cnt);
|
|
break;
|
|
default: break; /* For the remaining ones */
|
|
}
|
|
cp = &fieldbuf[0] + strlen(fieldbuf);
|
|
sprintf(cp, "%d", inf[RPCBVERS_2_STAT].info[i]);
|
|
flen = strlen(fieldbuf);
|
|
printf("%s%s", pmaphdr[i],
|
|
spaces((TABSTOP * (1 + flen / TABSTOP))
|
|
- strlen(pmaphdr[i])));
|
|
sprintf(lp, "%s%s", fieldbuf,
|
|
spaces(cnt = ((TABSTOP * (1 + flen / TABSTOP))
|
|
- flen)));
|
|
lp += (flen + cnt);
|
|
}
|
|
printf("\n%s\n\n", linebuf);
|
|
|
|
if (inf[RPCBVERS_2_STAT].info[PMAPPROC_CALLIT]) {
|
|
printf("PMAP_RMTCALL call statistics\n");
|
|
print_rmtcallstat(RPCBVERS_2_STAT, &inf[RPCBVERS_2_STAT]);
|
|
printf("\n");
|
|
}
|
|
|
|
if (inf[RPCBVERS_2_STAT].info[PMAPPROC_GETPORT]) {
|
|
printf("PMAP_GETPORT call statistics\n");
|
|
print_getaddrstat(RPCBVERS_2_STAT, &inf[RPCBVERS_2_STAT]);
|
|
printf("\n");
|
|
}
|
|
|
|
printf("RPCBIND (version 3) statistics\n");
|
|
lp = linebuf;
|
|
for (i = 0; i <= rpcb_highproc_3; i++) {
|
|
fieldbuf[0] = '\0';
|
|
switch (i) {
|
|
case RPCBPROC_SET:
|
|
sprintf(fieldbuf, "%d/", inf[RPCBVERS_3_STAT].setinfo);
|
|
break;
|
|
case RPCBPROC_UNSET:
|
|
sprintf(fieldbuf, "%d/",
|
|
inf[RPCBVERS_3_STAT].unsetinfo);
|
|
break;
|
|
case RPCBPROC_GETADDR:
|
|
cnt = 0;
|
|
for (pa = inf[RPCBVERS_3_STAT].addrinfo; pa;
|
|
pa = pa->next)
|
|
cnt += pa->success;
|
|
sprintf(fieldbuf, "%d/", cnt);
|
|
break;
|
|
case RPCBPROC_CALLIT:
|
|
cnt = 0;
|
|
for (pr = inf[RPCBVERS_3_STAT].rmtinfo; pr;
|
|
pr = pr->next)
|
|
cnt += pr->success;
|
|
sprintf(fieldbuf, "%d/", cnt);
|
|
break;
|
|
default: break; /* For the remaining ones */
|
|
}
|
|
cp = &fieldbuf[0] + strlen(fieldbuf);
|
|
sprintf(cp, "%d", inf[RPCBVERS_3_STAT].info[i]);
|
|
flen = strlen(fieldbuf);
|
|
printf("%s%s", rpcb3hdr[i],
|
|
spaces((TABSTOP * (1 + flen / TABSTOP))
|
|
- strlen(rpcb3hdr[i])));
|
|
sprintf(lp, "%s%s", fieldbuf,
|
|
spaces(cnt = ((TABSTOP * (1 + flen / TABSTOP))
|
|
- flen)));
|
|
lp += (flen + cnt);
|
|
}
|
|
printf("\n%s\n\n", linebuf);
|
|
|
|
if (inf[RPCBVERS_3_STAT].info[RPCBPROC_CALLIT]) {
|
|
printf("RPCB_RMTCALL (version 3) call statistics\n");
|
|
print_rmtcallstat(RPCBVERS_3_STAT, &inf[RPCBVERS_3_STAT]);
|
|
printf("\n");
|
|
}
|
|
|
|
if (inf[RPCBVERS_3_STAT].info[RPCBPROC_GETADDR]) {
|
|
printf("RPCB_GETADDR (version 3) call statistics\n");
|
|
print_getaddrstat(RPCBVERS_3_STAT, &inf[RPCBVERS_3_STAT]);
|
|
printf("\n");
|
|
}
|
|
|
|
printf("RPCBIND (version 4) statistics\n");
|
|
|
|
for (j = 0; j <= 9; j += 9) { /* Just two iterations for printing */
|
|
lp = linebuf;
|
|
for (i = j; i <= MAX(8, rpcb_highproc_4 - 9 + j); i++) {
|
|
fieldbuf[0] = '\0';
|
|
switch (i) {
|
|
case RPCBPROC_SET:
|
|
sprintf(fieldbuf, "%d/",
|
|
inf[RPCBVERS_4_STAT].setinfo);
|
|
break;
|
|
case RPCBPROC_UNSET:
|
|
sprintf(fieldbuf, "%d/",
|
|
inf[RPCBVERS_4_STAT].unsetinfo);
|
|
break;
|
|
case RPCBPROC_GETADDR:
|
|
cnt = 0;
|
|
for (pa = inf[RPCBVERS_4_STAT].addrinfo; pa;
|
|
pa = pa->next)
|
|
cnt += pa->success;
|
|
sprintf(fieldbuf, "%d/", cnt);
|
|
break;
|
|
case RPCBPROC_CALLIT:
|
|
cnt = 0;
|
|
for (pr = inf[RPCBVERS_4_STAT].rmtinfo; pr;
|
|
pr = pr->next)
|
|
cnt += pr->success;
|
|
sprintf(fieldbuf, "%d/", cnt);
|
|
break;
|
|
default: break; /* For the remaining ones */
|
|
}
|
|
cp = &fieldbuf[0] + strlen(fieldbuf);
|
|
/*
|
|
* XXX: We also add RPCBPROC_GETADDRLIST queries to
|
|
* RPCB_GETADDR because rpcbind includes the
|
|
* RPCB_GETADDRLIST successes in RPCB_GETADDR.
|
|
*/
|
|
if (i != RPCBPROC_GETADDR)
|
|
sprintf(cp, "%d", inf[RPCBVERS_4_STAT].info[i]);
|
|
else
|
|
sprintf(cp, "%d", inf[RPCBVERS_4_STAT].info[i] +
|
|
inf[RPCBVERS_4_STAT].info[RPCBPROC_GETADDRLIST]);
|
|
flen = strlen(fieldbuf);
|
|
printf("%s%s", rpcb4hdr[i],
|
|
spaces((TABSTOP * (1 + flen / TABSTOP))
|
|
- strlen(rpcb4hdr[i])));
|
|
sprintf(lp, "%s%s", fieldbuf,
|
|
spaces(cnt = ((TABSTOP * (1 + flen / TABSTOP))
|
|
- flen)));
|
|
lp += (flen + cnt);
|
|
}
|
|
printf("\n%s\n", linebuf);
|
|
}
|
|
|
|
if (inf[RPCBVERS_4_STAT].info[RPCBPROC_CALLIT] ||
|
|
inf[RPCBVERS_4_STAT].info[RPCBPROC_INDIRECT]) {
|
|
printf("\n");
|
|
printf("RPCB_RMTCALL (version 4) call statistics\n");
|
|
print_rmtcallstat(RPCBVERS_4_STAT, &inf[RPCBVERS_4_STAT]);
|
|
}
|
|
|
|
if (inf[RPCBVERS_4_STAT].info[RPCBPROC_GETADDR]) {
|
|
printf("\n");
|
|
printf("RPCB_GETADDR (version 4) call statistics\n");
|
|
print_getaddrstat(RPCBVERS_4_STAT, &inf[RPCBVERS_4_STAT]);
|
|
}
|
|
clnt_destroy(client);
|
|
}
|
|
|
|
/*
|
|
* Delete registeration for this (prog, vers, netid)
|
|
*/
|
|
static void
|
|
deletereg(char *netid, int argc, char **argv)
|
|
{
|
|
struct netconfig *nconf = NULL;
|
|
|
|
if (argc != 2) {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
if (netid) {
|
|
nconf = getnetconfigent(netid);
|
|
if (nconf == NULL) {
|
|
fprintf(stderr, "rpcinfo: netid %s not supported\n",
|
|
netid);
|
|
exit(1);
|
|
}
|
|
}
|
|
if ((rpcb_unset(getprognum(argv[0]), getvers(argv[1]), nconf)) == 0) {
|
|
fprintf(stderr,
|
|
"rpcinfo: Could not delete registration for prog %s version %s\n",
|
|
argv[0], argv[1]);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create and return a handle for the given nconf.
|
|
* Exit if cannot create handle.
|
|
*/
|
|
static CLIENT *
|
|
clnt_addr_create(char *address, struct netconfig *nconf,
|
|
u_long prog, u_long vers)
|
|
{
|
|
CLIENT *client;
|
|
static struct netbuf *nbuf;
|
|
static int fd = RPC_ANYFD;
|
|
|
|
if (fd == RPC_ANYFD) {
|
|
if ((fd = __rpc_nconf2fd(nconf)) == -1) {
|
|
rpc_createerr.cf_stat = RPC_TLIERROR;
|
|
clnt_pcreateerror("rpcinfo");
|
|
exit(1);
|
|
}
|
|
/* Convert the uaddr to taddr */
|
|
nbuf = uaddr2taddr(nconf, address);
|
|
if (nbuf == NULL) {
|
|
errx(1, "rpcinfo: no address for client handle");
|
|
exit(1);
|
|
}
|
|
}
|
|
client = clnt_tli_create(fd, nconf, nbuf, prog, vers, 0, 0);
|
|
if (client == (CLIENT *)NULL) {
|
|
clnt_pcreateerror("rpcinfo");
|
|
exit(1);
|
|
}
|
|
return (client);
|
|
}
|
|
|
|
/*
|
|
* If the version number is given, ping that (prog, vers); else try to find
|
|
* the version numbers supported for that prog and ping all the versions.
|
|
* Remote rpcbind is not contacted for this service. The requests are
|
|
* sent directly to the services themselves.
|
|
*/
|
|
static void
|
|
addrping(char *address, char *netid, int argc, char **argv)
|
|
{
|
|
CLIENT *client;
|
|
struct timeval to;
|
|
enum clnt_stat rpc_stat;
|
|
u_long prognum, versnum, minvers, maxvers;
|
|
struct rpc_err rpcerr;
|
|
int failure = 0;
|
|
struct netconfig *nconf;
|
|
int fd;
|
|
|
|
if (argc < 1 || argc > 2 || (netid == NULL)) {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
nconf = getnetconfigent(netid);
|
|
if (nconf == (struct netconfig *)NULL) {
|
|
fprintf(stderr, "rpcinfo: Could not find %s\n", netid);
|
|
exit(1);
|
|
}
|
|
to.tv_sec = 10;
|
|
to.tv_usec = 0;
|
|
prognum = getprognum(argv[0]);
|
|
if (argc == 1) { /* Version number not known */
|
|
/*
|
|
* A call to version 0 should fail with a program/version
|
|
* mismatch, and give us the range of versions supported.
|
|
*/
|
|
versnum = MIN_VERS;
|
|
} else {
|
|
versnum = getvers(argv[1]);
|
|
}
|
|
client = clnt_addr_create(address, nconf, prognum, versnum);
|
|
rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void,
|
|
(char *)NULL, (xdrproc_t) xdr_void,
|
|
(char *)NULL, to);
|
|
if (argc == 2) {
|
|
/* Version number was known */
|
|
if (pstatus(client, prognum, versnum) < 0)
|
|
failure = 1;
|
|
(void) CLNT_DESTROY(client);
|
|
if (failure)
|
|
exit(1);
|
|
return;
|
|
}
|
|
/* Version number not known */
|
|
(void) CLNT_CONTROL(client, CLSET_FD_NCLOSE, (char *)NULL);
|
|
(void) CLNT_CONTROL(client, CLGET_FD, (char *)&fd);
|
|
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.
|
|
*/
|
|
(void) CLNT_DESTROY(client);
|
|
client = clnt_addr_create(address, nconf, prognum, MAX_VERS);
|
|
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) {
|
|
/*
|
|
* 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);
|
|
}
|
|
(void) CLNT_DESTROY(client);
|
|
for (versnum = minvers; versnum <= maxvers; versnum++) {
|
|
client = clnt_addr_create(address, nconf, prognum, versnum);
|
|
rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void,
|
|
(char *)NULL, (xdrproc_t) xdr_void,
|
|
(char *)NULL, to);
|
|
if (pstatus(client, prognum, versnum) < 0)
|
|
failure = 1;
|
|
(void) CLNT_DESTROY(client);
|
|
}
|
|
(void) close(fd);
|
|
if (failure)
|
|
exit(1);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If the version number is given, ping that (prog, vers); else try to find
|
|
* the version numbers supported for that prog and ping all the versions.
|
|
* Remote rpcbind is *contacted* for this service. The requests are
|
|
* then sent directly to the services themselves.
|
|
*/
|
|
static void
|
|
progping(char *netid, int argc, char **argv)
|
|
{
|
|
CLIENT *client;
|
|
struct timeval to;
|
|
enum clnt_stat rpc_stat;
|
|
u_long prognum, versnum, minvers, maxvers;
|
|
struct rpc_err rpcerr;
|
|
int failure = 0;
|
|
struct netconfig *nconf;
|
|
|
|
if (argc < 2 || argc > 3 || (netid == NULL)) {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
prognum = getprognum(argv[1]);
|
|
if (argc == 2) { /* Version number not known */
|
|
/*
|
|
* A call to version 0 should fail with a program/version
|
|
* mismatch, and give us the range of versions supported.
|
|
*/
|
|
versnum = MIN_VERS;
|
|
} else {
|
|
versnum = getvers(argv[2]);
|
|
}
|
|
if (netid) {
|
|
nconf = getnetconfigent(netid);
|
|
if (nconf == (struct netconfig *)NULL) {
|
|
fprintf(stderr, "rpcinfo: Could not find %s\n", netid);
|
|
exit(1);
|
|
}
|
|
client = clnt_tp_create(argv[0], prognum, versnum, nconf);
|
|
} else {
|
|
client = clnt_create(argv[0], prognum, versnum, "NETPATH");
|
|
}
|
|
if (client == (CLIENT *)NULL) {
|
|
clnt_pcreateerror("rpcinfo");
|
|
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 (argc == 3) {
|
|
/* Version number was known */
|
|
if (pstatus(client, prognum, versnum) < 0)
|
|
failure = 1;
|
|
(void) CLNT_DESTROY(client);
|
|
if (failure)
|
|
exit(1);
|
|
return;
|
|
}
|
|
/* Version number not known */
|
|
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.
|
|
*/
|
|
versnum = MAX_VERS;
|
|
(void) CLNT_CONTROL(client, CLSET_VERS, (char *)&versnum);
|
|
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) {
|
|
/*
|
|
* 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);
|
|
}
|
|
for (versnum = minvers; versnum <= maxvers; versnum++) {
|
|
(void) CLNT_CONTROL(client, CLSET_VERS, (char *)&versnum);
|
|
rpc_stat = CLNT_CALL(client, NULLPROC, (xdrproc_t) xdr_void,
|
|
(char *)NULL, (xdrproc_t) xdr_void,
|
|
(char *)NULL, to);
|
|
if (pstatus(client, prognum, versnum) < 0)
|
|
failure = 1;
|
|
}
|
|
(void) CLNT_DESTROY(client);
|
|
if (failure)
|
|
exit(1);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
usage()
|
|
{
|
|
fprintf(stderr, "Usage: rpcinfo [-m | -s] [host]\n");
|
|
#ifdef PORTMAP
|
|
fprintf(stderr, " rpcinfo -p [host]\n");
|
|
#endif
|
|
fprintf(stderr, " rpcinfo -T netid host prognum [versnum]\n");
|
|
fprintf(stderr, " rpcinfo -l host prognum versnum\n");
|
|
#ifdef PORTMAP
|
|
fprintf(stderr,
|
|
" rpcinfo [-n portnum] -u | -t host prognum [versnum]\n");
|
|
#endif
|
|
fprintf(stderr,
|
|
" rpcinfo -a serv_address -T netid prognum [version]\n");
|
|
fprintf(stderr, " rpcinfo -b prognum versnum\n");
|
|
fprintf(stderr, " rpcinfo -d [-T netid] prognum versnum\n");
|
|
}
|
|
|
|
static u_long
|
|
getprognum (char *arg)
|
|
{
|
|
char *strptr;
|
|
register struct rpcent *rpc;
|
|
register u_long prognum;
|
|
char *tptr = arg;
|
|
|
|
while (*tptr && isdigit(*tptr++));
|
|
if (*tptr || isalpha(*(tptr - 1))) {
|
|
rpc = getrpcbyname(arg);
|
|
if (rpc == NULL) {
|
|
fprintf(stderr, "rpcinfo: %s is unknown service\n",
|
|
arg);
|
|
exit(1);
|
|
}
|
|
prognum = rpc->r_number;
|
|
} else {
|
|
prognum = strtol(arg, &strptr, 10);
|
|
if (strptr == arg || *strptr != '\0') {
|
|
fprintf(stderr,
|
|
"rpcinfo: %s is illegal program number\n", arg);
|
|
exit(1);
|
|
}
|
|
}
|
|
return (prognum);
|
|
}
|
|
|
|
static u_long
|
|
getvers(char *arg)
|
|
{
|
|
char *strptr;
|
|
register u_long vers;
|
|
|
|
vers = (int) strtol(arg, &strptr, 10);
|
|
if (strptr == arg || *strptr != '\0') {
|
|
fprintf(stderr, "rpcinfo: %s is illegal version number\n",
|
|
arg);
|
|
exit(1);
|
|
}
|
|
return (vers);
|
|
}
|
|
|
|
/*
|
|
* 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(register CLIENT *client, u_long prog, 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",
|
|
prog, vers);
|
|
return (-1);
|
|
} else {
|
|
printf("program %lu version %lu ready and waiting\n",
|
|
prog, vers);
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
static CLIENT *
|
|
clnt_rpcbind_create(char *host, int rpcbversnum, struct netbuf **targaddr)
|
|
{
|
|
static char *tlist[3] = {
|
|
"circuit_n", "circuit_v", "datagram_v"
|
|
};
|
|
int i;
|
|
struct netconfig *nconf;
|
|
CLIENT *clnt = NULL;
|
|
void *handle;
|
|
|
|
rpc_createerr.cf_stat = RPC_SUCCESS;
|
|
for (i = 0; i < 3; i++) {
|
|
if ((handle = __rpc_setconf(tlist[i])) == NULL)
|
|
continue;
|
|
while (clnt == (CLIENT *)NULL) {
|
|
if ((nconf = __rpc_getconf(handle)) == NULL) {
|
|
if (rpc_createerr.cf_stat == RPC_SUCCESS)
|
|
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
|
|
break;
|
|
}
|
|
clnt = getclnthandle(host, nconf, rpcbversnum,
|
|
targaddr);
|
|
}
|
|
if (clnt)
|
|
break;
|
|
__rpc_endconf(handle);
|
|
}
|
|
return (clnt);
|
|
}
|
|
|
|
static CLIENT*
|
|
getclnthandle(char *host, struct netconfig *nconf,
|
|
u_long rpcbversnum, struct netbuf **targaddr)
|
|
{
|
|
struct netbuf addr;
|
|
struct addrinfo hints, *res;
|
|
CLIENT *client = NULL;
|
|
|
|
/* Get the address of the rpcbind */
|
|
memset(&hints, 0, sizeof hints);
|
|
if (getaddrinfo(host, "rpcbind", &hints, &res) != 0) {
|
|
rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
|
|
return (NULL);
|
|
}
|
|
addr.len = addr.maxlen = res->ai_addrlen;
|
|
addr.buf = res->ai_addr;
|
|
client = clnt_tli_create(RPC_ANYFD, nconf, &addr, RPCBPROG,
|
|
rpcbversnum, 0, 0);
|
|
if (client) {
|
|
if (targaddr != NULL) {
|
|
*targaddr =
|
|
(struct netbuf *)malloc(sizeof (struct netbuf));
|
|
if (*targaddr != NULL) {
|
|
(*targaddr)->maxlen = addr.maxlen;
|
|
(*targaddr)->len = addr.len;
|
|
(*targaddr)->buf = (char *)malloc(addr.len);
|
|
if ((*targaddr)->buf != NULL) {
|
|
memcpy((*targaddr)->buf, addr.buf,
|
|
addr.len);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (rpc_createerr.cf_stat == RPC_TLIERROR) {
|
|
/*
|
|
* Assume that the other system is dead; this is a
|
|
* better error to display to the user.
|
|
*/
|
|
rpc_createerr.cf_stat = RPC_RPCBFAILURE;
|
|
rpc_createerr.cf_error.re_status = RPC_FAILED;
|
|
}
|
|
}
|
|
freeaddrinfo(res);
|
|
return (client);
|
|
}
|
|
|
|
static void
|
|
print_rmtcallstat(int rtype, rpcb_stat *infp)
|
|
{
|
|
register rpcbs_rmtcalllist_ptr pr;
|
|
struct rpcent *rpc;
|
|
|
|
if (rtype == RPCBVERS_4_STAT)
|
|
printf(
|
|
"prog\t\tvers\tproc\tnetid\tindirect success failure\n");
|
|
else
|
|
printf("prog\t\tvers\tproc\tnetid\tsuccess\tfailure\n");
|
|
for (pr = infp->rmtinfo; pr; pr = pr->next) {
|
|
rpc = getrpcbynumber(pr->prog);
|
|
if (rpc)
|
|
printf("%-16s", rpc->r_name);
|
|
else
|
|
printf("%-16d", pr->prog);
|
|
printf("%d\t%d\t%s\t",
|
|
pr->vers, pr->proc, pr->netid);
|
|
if (rtype == RPCBVERS_4_STAT)
|
|
printf("%d\t ", pr->indirect);
|
|
printf("%d\t%d\n", pr->success, pr->failure);
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_getaddrstat(int rtype, rpcb_stat *infp)
|
|
{
|
|
rpcbs_addrlist_ptr al;
|
|
register struct rpcent *rpc;
|
|
|
|
printf("prog\t\tvers\tnetid\t success\tfailure\n");
|
|
for (al = infp->addrinfo; al; al = al->next) {
|
|
rpc = getrpcbynumber(al->prog);
|
|
if (rpc)
|
|
printf("%-16s", rpc->r_name);
|
|
else
|
|
printf("%-16d", al->prog);
|
|
printf("%d\t%s\t %-12d\t%d\n",
|
|
al->vers, al->netid,
|
|
al->success, al->failure);
|
|
}
|
|
}
|
|
|
|
static char *
|
|
spaces(int howmany)
|
|
{
|
|
static char space_array[] = /* 64 spaces */
|
|
" ";
|
|
|
|
if (howmany <= 0 || howmany > sizeof (space_array)) {
|
|
return ("");
|
|
}
|
|
return (&space_array[sizeof (space_array) - howmany - 1]);
|
|
}
|