Home | History | Annotate | Line # | Download | only in rpc
clnt_generic.c revision 1.32
      1 /*	$NetBSD: clnt_generic.c,v 1.32 2014/05/28 14:41:47 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2010, Oracle America, Inc.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions are
      8  * met:
      9  *
     10  *     * Redistributions of source code must retain the above copyright
     11  *       notice, this list of conditions and the following disclaimer.
     12  *     * Redistributions in binary form must reproduce the above
     13  *       copyright notice, this list of conditions and the following
     14  *       disclaimer in the documentation and/or other materials
     15  *       provided with the distribution.
     16  *     * Neither the name of the "Oracle America, Inc." nor the names of its
     17  *       contributors may be used to endorse or promote products derived
     18  *       from this software without specific prior written permission.
     19  *
     20  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     23  *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     24  *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
     25  *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     27  *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     28  *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     29  *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     30  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     32  */
     33 /*
     34  * Copyright (c) 1986-1991 by Sun Microsystems Inc.
     35  */
     36 
     37 /* #ident	"@(#)clnt_generic.c	1.20	94/05/03 SMI" */
     38 
     39 #include <sys/cdefs.h>
     40 #if defined(LIBC_SCCS) && !defined(lint)
     41 #if 0
     42 static char sccsid[] = "@(#)clnt_generic.c 1.32 89/03/16 Copyr 1988 Sun Micro";
     43 #else
     44 __RCSID("$NetBSD: clnt_generic.c,v 1.32 2014/05/28 14:41:47 christos Exp $");
     45 #endif
     46 #endif
     47 
     48 #include "namespace.h"
     49 #include "reentrant.h"
     50 #include <sys/types.h>
     51 #include <sys/socket.h>
     52 #include <netinet/in.h>
     53 #include <assert.h>
     54 #include <stdio.h>
     55 #include <errno.h>
     56 #include <rpc/rpc.h>
     57 #include <rpc/nettype.h>
     58 #include <string.h>
     59 #include <stdlib.h>
     60 #include <unistd.h>
     61 
     62 #include "svc_fdset.h"
     63 #include "rpc_internal.h"
     64 
     65 #ifdef __weak_alias
     66 __weak_alias(clnt_create_vers,_clnt_create_vers)
     67 __weak_alias(clnt_create,_clnt_create)
     68 __weak_alias(clnt_tp_create,_clnt_tp_create)
     69 __weak_alias(clnt_tli_create,_clnt_tli_create)
     70 #endif
     71 
     72 /*
     73  * Generic client creation with version checking the value of
     74  * vers_out is set to the highest server supported value
     75  * vers_low <= vers_out <= vers_high  AND an error results
     76  * if this can not be done.
     77  */
     78 CLIENT *
     79 clnt_create_vers(
     80 	const char *	hostname,
     81 	rpcprog_t	prog,
     82 	rpcvers_t *	vers_out,
     83 	rpcvers_t	vers_low,
     84 	rpcvers_t	vers_high,
     85 	const char *	nettype)
     86 {
     87 	CLIENT *clnt;
     88 	struct timeval to;
     89 	enum clnt_stat rpc_stat;
     90 	struct rpc_err rpcerr;
     91 
     92 	_DIAGASSERT(hostname != NULL);
     93 	_DIAGASSERT(vers_out != NULL);
     94 	/* XXX: nettype appears to support being NULL */
     95 
     96 	clnt = clnt_create(hostname, prog, vers_high, nettype);
     97 	if (clnt == NULL) {
     98 		return (NULL);
     99 	}
    100 	to.tv_sec = 10;
    101 	to.tv_usec = 0;
    102 	rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void,
    103 	    NULL, (xdrproc_t) xdr_void, NULL, to);
    104 	if (rpc_stat == RPC_SUCCESS) {
    105 		*vers_out = vers_high;
    106 		return (clnt);
    107 	}
    108 	if (rpc_stat == RPC_PROGVERSMISMATCH) {
    109 		unsigned long minvers, maxvers;
    110 
    111 		clnt_geterr(clnt, &rpcerr);
    112 		minvers = rpcerr.re_vers.low;
    113 		maxvers = rpcerr.re_vers.high;
    114 		if (maxvers < vers_high)
    115 			vers_high = (rpcvers_t)maxvers;
    116 		if (minvers > vers_low)
    117 			vers_low = (rpcvers_t)minvers;
    118 		if (vers_low > vers_high) {
    119 			goto error;
    120 		}
    121 		CLNT_CONTROL(clnt, CLSET_VERS, (char *)(void *)&vers_high);
    122 		rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void,
    123 		    NULL, (xdrproc_t) xdr_void, NULL, to);
    124 		if (rpc_stat == RPC_SUCCESS) {
    125 			*vers_out = vers_high;
    126 			return (clnt);
    127 		}
    128 	}
    129 	clnt_geterr(clnt, &rpcerr);
    130 
    131 error:
    132 	rpc_createerr.cf_stat = rpc_stat;
    133 	rpc_createerr.cf_error = rpcerr;
    134 	clnt_destroy(clnt);
    135 	return (NULL);
    136 }
    137 
    138 /*
    139  * Top level client creation routine.
    140  * Generic client creation: takes (servers name, program-number, nettype) and
    141  * returns client handle. Default options are set, which the user can
    142  * change using the rpc equivalent of ioctl()'s.
    143  *
    144  * It tries for all the netids in that particular class of netid until
    145  * it succeeds.
    146  * XXX The error message in the case of failure will be the one
    147  * pertaining to the last create error.
    148  *
    149  * It calls clnt_tp_create();
    150  */
    151 CLIENT *
    152 clnt_create(
    153 	const char *	hostname,			/* server name */
    154 	rpcprog_t	prog,				/* program number */
    155 	rpcvers_t	vers,				/* version number */
    156 	const char *	nettype)			/* net type */
    157 {
    158 	struct netconfig *nconf;
    159 	CLIENT *clnt = NULL;
    160 	void *handle;
    161 	enum clnt_stat	save_cf_stat = RPC_SUCCESS;
    162 	struct rpc_err	save_cf_error;
    163 
    164 	_DIAGASSERT(hostname != NULL);
    165 	/* XXX: nettype appears to support being NULL */
    166 
    167 	if ((handle = __rpc_setconf(nettype)) == NULL) {
    168 		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
    169 		return (NULL);
    170 	}
    171 	rpc_createerr.cf_stat = RPC_SUCCESS;
    172 	while (clnt == NULL) {
    173 		if ((nconf = __rpc_getconf(handle)) == NULL) {
    174 			if (rpc_createerr.cf_stat == RPC_SUCCESS)
    175 				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
    176 			break;
    177 		}
    178 #ifdef CLNT_DEBUG
    179 		printf("trying netid %s\n", nconf->nc_netid);
    180 #endif
    181 		clnt = clnt_tp_create(hostname, prog, vers, nconf);
    182 		if (clnt)
    183 			break;
    184 		else
    185 			/*
    186 			 *	Since we didn't get a name-to-address
    187 			 *	translation failure here, we remember
    188 			 *	this particular error.  The object of
    189 			 *	this is to enable us to return to the
    190 			 *	caller a more-specific error than the
    191 			 *	unhelpful ``Name to address translation
    192 			 *	failed'' which might well occur if we
    193 			 *	merely returned the last error (because
    194 			 *	the local loopbacks are typically the
    195 			 *	last ones in /etc/netconfig and the most
    196 			 *	likely to be unable to translate a host
    197 			 *	name).
    198 			 */
    199 			if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE) {
    200 				save_cf_stat = rpc_createerr.cf_stat;
    201 				save_cf_error = rpc_createerr.cf_error;
    202 			}
    203 	}
    204 
    205 	/*
    206 	 *	Attempt to return an error more specific than ``Name to address
    207 	 *	translation failed''
    208 	 */
    209 	if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE) &&
    210 		(save_cf_stat != RPC_SUCCESS)) {
    211 		rpc_createerr.cf_stat = save_cf_stat;
    212 		rpc_createerr.cf_error = save_cf_error;
    213 	}
    214 	__rpc_endconf(handle);
    215 	return (clnt);
    216 }
    217 
    218 /*
    219  * Generic client creation: takes (servers name, program-number, netconf) and
    220  * returns client handle. Default options are set, which the user can
    221  * change using the rpc equivalent of ioctl()'s : clnt_control()
    222  * It finds out the server address from rpcbind and calls clnt_tli_create()
    223  */
    224 CLIENT *
    225 clnt_tp_create(
    226 	const char *		hostname,	/* server name */
    227 	rpcprog_t		prog,		/* program number */
    228 	rpcvers_t		vers,		/* version number */
    229 	const struct netconfig *nconf)		/* net config struct */
    230 {
    231 	struct netbuf *svcaddr;			/* servers address */
    232 	CLIENT *cl = NULL;			/* client handle */
    233 
    234 	_DIAGASSERT(hostname != NULL);
    235 	/* nconf is handled below */
    236 
    237 	if (nconf == NULL) {
    238 		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
    239 		return (NULL);
    240 	}
    241 
    242 	/*
    243 	 * Get the address of the server
    244 	 */
    245 	if ((svcaddr = __rpcb_findaddr(prog, vers, nconf, hostname,
    246 		&cl)) == NULL) {
    247 		/* appropriate error number is set by rpcbind libraries */
    248 		return (NULL);
    249 	}
    250 	if (cl == NULL) {
    251 		cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
    252 					prog, vers, 0, 0);
    253 	} else {
    254 		/* Reuse the CLIENT handle and change the appropriate fields */
    255 		if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) {
    256 			if (cl->cl_netid == NULL) {
    257 				cl->cl_netid = strdup(nconf->nc_netid);
    258 				if (cl->cl_netid == NULL)
    259 					goto out;
    260 			}
    261 			if (cl->cl_tp == NULL) {
    262 				cl->cl_tp = strdup(nconf->nc_device);
    263 				if (cl->cl_tp == NULL)
    264 					goto out;
    265 			}
    266 			(void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog);
    267 			(void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers);
    268 		} else {
    269 			CLNT_DESTROY(cl);
    270 			cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
    271 					prog, vers, 0, 0);
    272 		}
    273 	}
    274 	free(svcaddr->buf);
    275 	free(svcaddr);
    276 	return (cl);
    277 out:
    278 	clnt_destroy(cl);
    279 	return NULL;
    280 }
    281 
    282 /*
    283  * Generic client creation:  returns client handle.
    284  * Default options are set, which the user can
    285  * change using the rpc equivalent of ioctl()'s : clnt_control().
    286  * If fd is RPC_ANYFD, it will be opened using nconf.
    287  * It will be bound if not so.
    288  * If sizes are 0; appropriate defaults will be chosen.
    289  */
    290 CLIENT *
    291 clnt_tli_create(
    292 	int fd,				/* fd */
    293 	const struct netconfig *nconf,	/* netconfig structure */
    294 	const struct netbuf *svcaddr,	/* servers address */
    295 	rpcprog_t prog,			/* program number */
    296 	rpcvers_t vers,			/* version number */
    297 	u_int sendsz,			/* send size */
    298 	u_int recvsz)			/* recv size */
    299 {
    300 	CLIENT *cl;			/* client handle */
    301 	bool_t madefd = FALSE;		/* whether fd opened here */
    302 	long servtype;
    303 	struct __rpc_sockinfo si;
    304 
    305 	/* nconf is handled below */
    306 	_DIAGASSERT(svcaddr != NULL);
    307 
    308 	if (fd == RPC_ANYFD) {
    309 		if (nconf == NULL) {
    310 			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
    311 			return (NULL);
    312 		}
    313 
    314 		fd = __rpc_nconf2fd(nconf);
    315 
    316 		if (fd == -1)
    317 			goto err;
    318 
    319 		madefd = TRUE;
    320 		servtype = nconf->nc_semantics;
    321 		if (!__rpc_fd2sockinfo(fd, &si))
    322 			goto err;
    323 
    324 		(void)bindresvport(fd, NULL);
    325 	} else {
    326 		if (!__rpc_fd2sockinfo(fd, &si))
    327 			goto err;
    328 		servtype = __rpc_socktype2seman(si.si_socktype);
    329 		if (servtype == -1) {
    330 			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
    331 			return NULL;
    332 		}
    333 
    334 	}
    335 
    336 	if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) {
    337 		rpc_createerr.cf_stat = RPC_UNKNOWNHOST;	/* XXX */
    338 		goto err1;
    339 	}
    340 
    341 	switch (servtype) {
    342 	case NC_TPI_COTS_ORD:
    343 		cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
    344 		if (!nconf || !cl)
    345 			break;
    346 		__rpc_setnodelay(fd, &si);
    347 		break;
    348 	case NC_TPI_CLTS:
    349 		cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz);
    350 		break;
    351 	default:
    352 		goto err;
    353 	}
    354 
    355 	if (cl == NULL)
    356 		goto err1; /* borrow errors from clnt_dg/vc creates */
    357 	if (nconf) {
    358 		cl->cl_netid = strdup(nconf->nc_netid);
    359 		if (cl->cl_netid == NULL)
    360 			goto err0;
    361 		cl->cl_tp = strdup(nconf->nc_device);
    362 		if (cl->cl_tp == NULL)
    363 			goto err0;
    364 	} else {
    365 		cl->cl_netid = __UNCONST("");
    366 		cl->cl_tp = __UNCONST("");
    367 	}
    368 	if (madefd) {
    369 		(void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
    370 /*		(void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL);  */
    371 	};
    372 
    373 	return (cl);
    374 
    375 err0:
    376 	clnt_destroy(cl);
    377 err:
    378 	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
    379 	rpc_createerr.cf_error.re_errno = errno;
    380 err1:	if (madefd)
    381 		(void) close(fd);
    382 	return (NULL);
    383 }
    384 
    385 /*
    386  * Don't block thse so interactive programs don't get stuck in lalaland.
    387  * (easier to do this than making connect(2) non-blocking..)
    388  */
    389 int
    390 __clnt_sigfillset(sigset_t *ss) {
    391 	static const int usersig[] = {
    392 	    SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP
    393 	};
    394 	if (sigfillset(ss) == -1)
    395 		return -1;
    396 	for (size_t i = 0; i < __arraycount(usersig); i++)
    397 		if (sigdelset(ss, usersig[i]) == -1)
    398 			return -1;
    399 	return 0;
    400 }
    401