summaryrefslogtreecommitdiffstats
path: root/lib/libc/rpc/clnt_generic.c
diff options
context:
space:
mode:
authoralfred <alfred@FreeBSD.org>2001-03-19 12:50:13 +0000
committeralfred <alfred@FreeBSD.org>2001-03-19 12:50:13 +0000
commitf67e4a8fc7fc95c74bd6c09d3453200de47faea5 (patch)
tree98b613188d263fdcef5f2d020e5e8c374db1f5b6 /lib/libc/rpc/clnt_generic.c
parent6f24d923a7fa9d1679753d77cc982ec72c22a197 (diff)
downloadFreeBSD-src-f67e4a8fc7fc95c74bd6c09d3453200de47faea5.zip
FreeBSD-src-f67e4a8fc7fc95c74bd6c09d3453200de47faea5.tar.gz
Bring in a hybrid of SunSoft's transport-independent RPC (TI-RPC) and
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
Diffstat (limited to 'lib/libc/rpc/clnt_generic.c')
-rw-r--r--lib/libc/rpc/clnt_generic.c362
1 files changed, 287 insertions, 75 deletions
diff --git a/lib/libc/rpc/clnt_generic.c b/lib/libc/rpc/clnt_generic.c
index d885f18..696ea54 100644
--- a/lib/libc/rpc/clnt_generic.c
+++ b/lib/libc/rpc/clnt_generic.c
@@ -1,3 +1,5 @@
+/* $NetBSD: clnt_generic.c,v 1.18 2000/07/06 03:10:34 christos Exp $ */
+
/*
* Sun RPC is a product of Sun Microsystems, Inc. and is provided for
* unrestricted use provided that this legend is included on all tape
@@ -27,6 +29,8 @@
* Mountain View, California 94043
*/
+/* #ident "@(#)clnt_generic.c 1.20 94/05/03 SMI" */
+
#if defined(LIBC_SCCS) && !defined(lint)
/*static char *sccsid = "from: @(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI";*/
/*static char *sccsid = "from: @(#)clnt_generic.c 2.2 88/08/01 4.0 RPCSRC";*/
@@ -34,104 +38,312 @@ static char *rcsid = "$FreeBSD$";
#endif
/*
- * Copyright (C) 1987, Sun Microsystems, Inc.
+ * Copyright (c) 1986-1991 by Sun Microsystems Inc.
*/
-#include <rpc/rpc.h>
+#include "reentrant.h"
+#include "namespace.h"
+#include <sys/types.h>
#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <stdio.h>
#include <errno.h>
#include <netdb.h>
+#include <rpc/rpc.h>
+#include <rpc/nettype.h>
#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "un-namespace.h"
+#include "rpc_com.h"
+
+/*
+ * Generic client creation with version checking the value of
+ * vers_out is set to the highest server supported value
+ * vers_low <= vers_out <= vers_high AND an error results
+ * if this can not be done.
+ */
+CLIENT *
+clnt_create_vers(hostname, prog, vers_out, vers_low, vers_high, nettype)
+ const char *hostname;
+ rpcprog_t prog;
+ rpcvers_t *vers_out;
+ rpcvers_t vers_low;
+ rpcvers_t vers_high;
+ const char *nettype;
+{
+ CLIENT *clnt;
+ struct timeval to;
+ enum clnt_stat rpc_stat;
+ struct rpc_err rpcerr;
+
+ clnt = clnt_create(hostname, prog, vers_high, nettype);
+ if (clnt == NULL) {
+ return (NULL);
+ }
+ to.tv_sec = 10;
+ to.tv_usec = 0;
+ rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void,
+ (char *) NULL, (xdrproc_t) xdr_void, (char *) NULL, to);
+ if (rpc_stat == RPC_SUCCESS) {
+ *vers_out = vers_high;
+ return (clnt);
+ }
+ if (rpc_stat == RPC_PROGVERSMISMATCH) {
+ unsigned long minvers, maxvers;
+
+ clnt_geterr(clnt, &rpcerr);
+ minvers = rpcerr.re_vers.low;
+ maxvers = rpcerr.re_vers.high;
+ if (maxvers < vers_high)
+ vers_high = (rpcvers_t)maxvers;
+ if (minvers > vers_low)
+ vers_low = (rpcvers_t)minvers;
+ if (vers_low > vers_high) {
+ goto error;
+ }
+ CLNT_CONTROL(clnt, CLSET_VERS, (char *)(void *)&vers_high);
+ rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void,
+ (char *) NULL, (xdrproc_t) xdr_void,
+ (char *) NULL, to);
+ if (rpc_stat == RPC_SUCCESS) {
+ *vers_out = vers_high;
+ return (clnt);
+ }
+ }
+ clnt_geterr(clnt, &rpcerr);
+
+error:
+ rpc_createerr.cf_stat = rpc_stat;
+ rpc_createerr.cf_error = rpcerr;
+ clnt_destroy(clnt);
+ return (NULL);
+}
/*
- * Generic client creation: takes (hostname, program-number, protocol) and
+ * Top level client creation routine.
+ * Generic client creation: takes (servers name, program-number, nettype) and
* returns client handle. Default options are set, which the user can
* change using the rpc equivalent of _ioctl()'s.
+ *
+ * It tries for all the netids in that particular class of netid until
+ * it succeeds.
+ * XXX The error message in the case of failure will be the one
+ * pertaining to the last create error.
+ *
+ * It calls clnt_tp_create();
*/
CLIENT *
-clnt_create(hostname, prog, vers, proto)
- char *hostname;
- u_long prog;
- u_long vers;
- char *proto;
+clnt_create(hostname, prog, vers, nettype)
+ const char *hostname; /* server name */
+ rpcprog_t prog; /* program number */
+ rpcvers_t vers; /* version number */
+ const char *nettype; /* net type */
{
- struct hostent *h;
- struct protoent *p;
- struct sockaddr_in sin;
- struct sockaddr_un sun;
- int sock;
- static struct timeval tv;
- CLIENT *client;
-
- if (!strcmp(proto, "unix")) {
- bzero((char *)&sun, sizeof(sun));
- sun.sun_family = AF_UNIX;
- strcpy(sun.sun_path, hostname);
- sun.sun_len = sizeof(sun.sun_len) + sizeof(sun.sun_family) +
- strlen(sun.sun_path) + 1;
- sock = RPC_ANYSOCK;
- client = clntunix_create(&sun, prog, vers, &sock, 0, 0);
- if (client == NULL)
- return(NULL);
- tv.tv_sec = 25;
- tv.tv_usec = 0;
- clnt_control(client, CLSET_TIMEOUT, &tv);
- return(client);
- }
+ struct netconfig *nconf;
+ CLIENT *clnt = NULL;
+ void *handle;
+ enum clnt_stat save_cf_stat = RPC_SUCCESS;
+ struct rpc_err save_cf_error;
- h = gethostbyname(hostname);
- if (h == NULL) {
- rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
+
+ if ((handle = __rpc_setconf(nettype)) == NULL) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
return (NULL);
}
- if (h->h_addrtype != AF_INET) {
- /*
- * Only support INET for now
- */
- rpc_createerr.cf_stat = RPC_SYSTEMERROR;
- rpc_createerr.cf_error.re_errno = EAFNOSUPPORT;
- return (NULL);
+ rpc_createerr.cf_stat = RPC_SUCCESS;
+ while (clnt == NULL) {
+ if ((nconf = __rpc_getconf(handle)) == NULL) {
+ if (rpc_createerr.cf_stat == RPC_SUCCESS)
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+ break;
+ }
+#ifdef CLNT_DEBUG
+ printf("trying netid %s\n", nconf->nc_netid);
+#endif
+ clnt = clnt_tp_create(hostname, prog, vers, nconf);
+ if (clnt)
+ break;
+ else
+ /*
+ * Since we didn't get a name-to-address
+ * translation failure here, we remember
+ * this particular error. The object of
+ * this is to enable us to return to the
+ * caller a more-specific error than the
+ * unhelpful ``Name to address translation
+ * failed'' which might well occur if we
+ * merely returned the last error (because
+ * the local loopbacks are typically the
+ * last ones in /etc/netconfig and the most
+ * likely to be unable to translate a host
+ * name).
+ */
+ if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE) {
+ save_cf_stat = rpc_createerr.cf_stat;
+ save_cf_error = rpc_createerr.cf_error;
+ }
+ }
+
+ /*
+ * Attempt to return an error more specific than ``Name to address
+ * translation failed''
+ */
+ if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE) &&
+ (save_cf_stat != RPC_SUCCESS)) {
+ rpc_createerr.cf_stat = save_cf_stat;
+ rpc_createerr.cf_error = save_cf_error;
}
- memset(&sin, 0, sizeof(sin));
- sin.sin_len = sizeof(struct sockaddr_in);
- sin.sin_family = h->h_addrtype;
- sin.sin_port = 0;
- memcpy((char*)&sin.sin_addr, h->h_addr, h->h_length);
- p = getprotobyname(proto);
- if (p == NULL) {
+ __rpc_endconf(handle);
+ return (clnt);
+}
+
+/*
+ * Generic client creation: takes (servers name, program-number, netconf) and
+ * returns client handle. Default options are set, which the user can
+ * change using the rpc equivalent of _ioctl()'s : clnt_control()
+ * It finds out the server address from rpcbind and calls clnt_tli_create()
+ */
+CLIENT *
+clnt_tp_create(hostname, prog, vers, nconf)
+ const char *hostname; /* server name */
+ rpcprog_t prog; /* program number */
+ rpcvers_t vers; /* version number */
+ const struct netconfig *nconf; /* net config struct */
+{
+ struct netbuf *svcaddr; /* servers address */
+ CLIENT *cl = NULL; /* client handle */
+
+ if (nconf == NULL) {
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
- rpc_createerr.cf_error.re_errno = EPFNOSUPPORT;
return (NULL);
}
- sock = RPC_ANYSOCK;
- switch (p->p_proto) {
- case IPPROTO_UDP:
- tv.tv_sec = 5;
- tv.tv_usec = 0;
- client = clntudp_create(&sin, prog, vers, tv, &sock);
- if (client == NULL) {
- return (NULL);
+
+ /*
+ * Get the address of the server
+ */
+ if ((svcaddr = __rpcb_findaddr(prog, vers, nconf, hostname,
+ &cl)) == NULL) {
+ /* appropriate error number is set by rpcbind libraries */
+ return (NULL);
+ }
+ if (cl == NULL) {
+ cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
+ prog, vers, 0, 0);
+ } else {
+ /* Reuse the CLIENT handle and change the appropriate fields */
+ if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) {
+ if (cl->cl_netid == NULL)
+ cl->cl_netid = strdup(nconf->nc_netid);
+ if (cl->cl_tp == NULL)
+ cl->cl_tp = strdup(nconf->nc_device);
+ (void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog);
+ (void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers);
+ } else {
+ CLNT_DESTROY(cl);
+ cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
+ prog, vers, 0, 0);
}
-#if 0 /* XXX do we need this? */
- tv.tv_sec = 25;
- tv.tv_usec = 0;
- clnt_control(client, CLSET_TIMEOUT, &tv);
-#endif
- break;
- case IPPROTO_TCP:
- client = clnttcp_create(&sin, prog, vers, &sock, 0, 0);
- if (client == NULL) {
+ }
+ free(svcaddr->buf);
+ free(svcaddr);
+ return (cl);
+}
+
+/*
+ * Generic client creation: returns client handle.
+ * Default options are set, which the user can
+ * change using the rpc equivalent of _ioctl()'s : clnt_control().
+ * If fd is RPC_ANYFD, it will be opened using nconf.
+ * It will be bound if not so.
+ * If sizes are 0; appropriate defaults will be chosen.
+ */
+CLIENT *
+clnt_tli_create(fd, nconf, svcaddr, prog, vers, sendsz, recvsz)
+ int fd; /* fd */
+ const struct netconfig *nconf; /* netconfig structure */
+ const struct netbuf *svcaddr; /* servers address */
+ rpcprog_t prog; /* program number */
+ rpcvers_t vers; /* version number */
+ u_int sendsz; /* send size */
+ u_int recvsz; /* recv size */
+{
+ CLIENT *cl; /* client handle */
+ bool_t madefd = FALSE; /* whether fd opened here */
+ long servtype;
+ int one = 1;
+ struct __rpc_sockinfo si;
+
+ if (fd == RPC_ANYFD) {
+ if (nconf == NULL) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
return (NULL);
}
-#if 0 /* XXX do we need this? */
- tv.tv_sec = 25;
- tv.tv_usec = 0;
- clnt_control(client, CLSET_TIMEOUT, &tv);
-#endif
+
+ fd = __rpc_nconf2fd(nconf);
+
+ if (fd == -1)
+ goto err;
+
+ madefd = TRUE;
+ servtype = nconf->nc_semantics;
+ if (!__rpc_fd2sockinfo(fd, &si))
+ goto err;
+
+ bindresvport(fd, NULL);
+ } else {
+ if (!__rpc_fd2sockinfo(fd, &si))
+ goto err;
+ servtype = __rpc_socktype2seman(si.si_socktype);
+ if (servtype == -1) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+ return NULL;
+ }
+
+ }
+
+ if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNHOST; /* XXX */
+ goto err1;
+ }
+
+ switch (servtype) {
+ case NC_TPI_COTS_ORD:
+ cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
+ if (!nconf || !cl)
+ break;
+ /* XXX fvdl - is this useful? */
+ if (strncmp(nconf->nc_protofmly, "inet", 4) == 0)
+ _setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one,
+ sizeof (one));
+ break;
+ case NC_TPI_CLTS:
+ cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz);
break;
default:
- rpc_createerr.cf_stat = RPC_SYSTEMERROR;
- rpc_createerr.cf_error.re_errno = EPFNOSUPPORT;
- return (NULL);
+ goto err;
}
- return (client);
+
+ if (cl == NULL)
+ goto err1; /* borrow errors from clnt_dg/vc creates */
+ if (nconf) {
+ cl->cl_netid = strdup(nconf->nc_netid);
+ cl->cl_tp = strdup(nconf->nc_device);
+ } else {
+ cl->cl_netid = "";
+ cl->cl_tp = "";
+ }
+ if (madefd) {
+ (void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
+/* (void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, (char *) NULL); */
+ };
+
+ return (cl);
+
+err:
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+err1: if (madefd)
+ (void)_close(fd);
+ return (NULL);
}
OpenPOWER on IntegriCloud