/* $NetBSD: clnt_generic.c,v 1.33 2014/05/28 14:45:19 christos Exp $ */ /* * Copyright (c) 2010, Oracle America, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the "Oracle America, Inc." nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1986-1991 by Sun Microsystems Inc. */ /* #ident "@(#)clnt_generic.c 1.20 94/05/03 SMI" */ #include #if defined(LIBC_SCCS) && !defined(lint) #if 0 static char sccsid[] = "@(#)clnt_generic.c 1.32 89/03/16 Copyr 1988 Sun Micro"; #else __RCSID("$NetBSD: clnt_generic.c,v 1.33 2014/05/28 14:45:19 christos Exp $"); #endif #endif #include "namespace.h" #include "reentrant.h" #include #include #include #include #include #include #include #include #include #include #include #include "svc_fdset.h" #include "rpc_internal.h" #ifdef __weak_alias __weak_alias(clnt_create_vers,_clnt_create_vers) __weak_alias(clnt_create,_clnt_create) __weak_alias(clnt_tp_create,_clnt_tp_create) __weak_alias(clnt_tli_create,_clnt_tli_create) #endif /* * 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( 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; _DIAGASSERT(hostname != NULL); _DIAGASSERT(vers_out != NULL); /* XXX: nettype appears to support being NULL */ 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, NULL, (xdrproc_t) xdr_void, 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, NULL, (xdrproc_t) xdr_void, 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); } /* * 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( const char * hostname, /* server name */ rpcprog_t prog, /* program number */ rpcvers_t vers, /* version number */ const char * nettype) /* net type */ { struct netconfig *nconf; CLIENT *clnt = NULL; void *handle; enum clnt_stat save_cf_stat = RPC_SUCCESS; struct rpc_err save_cf_error; _DIAGASSERT(hostname != NULL); /* XXX: nettype appears to support being NULL */ if ((handle = __rpc_setconf(nettype)) == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 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; } __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( 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 */ _DIAGASSERT(hostname != NULL); /* nconf is handled below */ if (nconf == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; 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_netid == NULL) goto out; } if (cl->cl_tp == NULL) { cl->cl_tp = strdup(nconf->nc_device); if (cl->cl_tp == NULL) goto out; } (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); } } free(svcaddr->buf); free(svcaddr); return (cl); out: clnt_destroy(cl); return NULL; } /* * 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( 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; struct __rpc_sockinfo si; /* nconf is handled below */ _DIAGASSERT(svcaddr != NULL); if (fd == RPC_ANYFD) { if (nconf == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; return (NULL); } fd = __rpc_nconf2fd(nconf); if (fd == -1) goto err; madefd = TRUE; servtype = nconf->nc_semantics; if (!__rpc_fd2sockinfo(fd, &si)) goto err; (void)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; (void)__rpc_setnodelay(fd, &si); break; case NC_TPI_CLTS: cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz); break; default: goto err; } if (cl == NULL) goto err1; /* borrow errors from clnt_dg/vc creates */ if (nconf) { cl->cl_netid = strdup(nconf->nc_netid); if (cl->cl_netid == NULL) goto err0; cl->cl_tp = strdup(nconf->nc_device); if (cl->cl_tp == NULL) goto err0; } else { cl->cl_netid = __UNCONST(""); cl->cl_tp = __UNCONST(""); } if (madefd) { (void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL); /* (void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL); */ }; return (cl); err0: clnt_destroy(cl); err: rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; err1: if (madefd) (void) close(fd); return (NULL); } /* * Don't block thse so interactive programs don't get stuck in lalaland. * (easier to do this than making connect(2) non-blocking..) */ int __clnt_sigfillset(sigset_t *ss) { static const int usersig[] = { SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP }; if (sigfillset(ss) == -1) return -1; for (size_t i = 0; i < __arraycount(usersig); i++) if (sigdelset(ss, usersig[i]) == -1) return -1; return 0; }