clnt_generic.c revision 261046
1/*	$NetBSD: clnt_generic.c,v 1.18 2000/07/06 03:10:34 christos Exp $	*/
2
3/*-
4 * Copyright (c) 2010, Oracle America, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * - Redistributions of source code must retain the above copyright notice,
10 *   this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright notice,
12 *   this list of conditions and the following disclaimer in the documentation
13 *   and/or other materials provided with the distribution.
14 * - Neither the name of the "Oracle America, Inc." nor the names of its
15 *   contributors may be used to endorse or promote products derived
16 *   from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/* #ident	"@(#)clnt_generic.c	1.40	99/04/21 SMI" */
32
33#if defined(LIBC_SCCS) && !defined(lint)
34static char *sccsid2 = "from: @(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI";
35static char *sccsid = "from: @(#)clnt_generic.c	2.2 88/08/01 4.0 RPCSRC";
36#endif
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: stable/10/lib/libc/rpc/clnt_generic.c 261046 2014-01-22 23:45:27Z mav $");
39
40/*
41 * Copyright (c) 1986-1996,1998 by Sun Microsystems, Inc.
42 * All rights reserved.
43 */
44#include "namespace.h"
45#include "reentrant.h"
46#include <sys/types.h>
47#include <sys/fcntl.h>
48#include <sys/socket.h>
49#include <netinet/in.h>
50#include <netinet/tcp.h>
51#include <stdio.h>
52#include <errno.h>
53#include <netdb.h>
54#include <syslog.h>
55#include <rpc/rpc.h>
56#include <rpc/nettype.h>
57#include <string.h>
58#include <stdlib.h>
59#include <unistd.h>
60#include "un-namespace.h"
61#include "rpc_com.h"
62
63extern bool_t __rpc_is_local_host(const char *);
64int __rpc_raise_fd(int);
65
66#ifndef NETIDLEN
67#define	NETIDLEN 32
68#endif
69
70
71/*
72 * Generic client creation with version checking the value of
73 * vers_out is set to the highest server supported value
74 * vers_low <= vers_out <= vers_high  AND an error results
75 * if this can not be done.
76 *
77 * It calls clnt_create_vers_timed() with a NULL value for the timeout
78 * pointer, which indicates that the default timeout should be used.
79 */
80CLIENT *
81clnt_create_vers(const char *hostname, rpcprog_t prog, rpcvers_t *vers_out,
82	rpcvers_t vers_low, rpcvers_t vers_high, const char *nettype)
83{
84
85	return (clnt_create_vers_timed(hostname, prog, vers_out, vers_low,
86				vers_high, nettype, NULL));
87}
88
89/*
90 * This the routine has the same definition as clnt_create_vers(),
91 * except it takes an additional timeout parameter - a pointer to
92 * a timeval structure.  A NULL value for the pointer indicates
93 * that the default timeout value should be used.
94 */
95CLIENT *
96clnt_create_vers_timed(const char *hostname, rpcprog_t prog,
97    rpcvers_t *vers_out, rpcvers_t vers_low, rpcvers_t vers_high,
98    const char *nettype, const struct timeval *tp)
99{
100	CLIENT *clnt;
101	struct timeval to;
102	enum clnt_stat rpc_stat;
103	struct rpc_err rpcerr;
104
105	clnt = clnt_create_timed(hostname, prog, vers_high, nettype, tp);
106	if (clnt == NULL) {
107		return (NULL);
108	}
109	to.tv_sec = 10;
110	to.tv_usec = 0;
111	rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
112			(char *)NULL, (xdrproc_t)xdr_void, (char *)NULL, to);
113	if (rpc_stat == RPC_SUCCESS) {
114		*vers_out = vers_high;
115		return (clnt);
116	}
117	while (rpc_stat == RPC_PROGVERSMISMATCH && vers_high > vers_low) {
118		unsigned int minvers, maxvers;
119
120		clnt_geterr(clnt, &rpcerr);
121		minvers = rpcerr.re_vers.low;
122		maxvers = rpcerr.re_vers.high;
123		if (maxvers < vers_high)
124			vers_high = maxvers;
125		else
126			vers_high--;
127		if (minvers > vers_low)
128			vers_low = minvers;
129		if (vers_low > vers_high) {
130			goto error;
131		}
132		CLNT_CONTROL(clnt, CLSET_VERS, (char *)&vers_high);
133		rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
134				(char *)NULL, (xdrproc_t)xdr_void,
135				(char *)NULL, to);
136		if (rpc_stat == RPC_SUCCESS) {
137			*vers_out = vers_high;
138			return (clnt);
139		}
140	}
141	clnt_geterr(clnt, &rpcerr);
142
143error:
144	rpc_createerr.cf_stat = rpc_stat;
145	rpc_createerr.cf_error = rpcerr;
146	clnt_destroy(clnt);
147	return (NULL);
148}
149
150/*
151 * Top level client creation routine.
152 * Generic client creation: takes (servers name, program-number, nettype) and
153 * returns client handle. Default options are set, which the user can
154 * change using the rpc equivalent of _ioctl()'s.
155 *
156 * It tries for all the netids in that particular class of netid until
157 * it succeeds.
158 * XXX The error message in the case of failure will be the one
159 * pertaining to the last create error.
160 *
161 * It calls clnt_create_timed() with the default timeout.
162 */
163CLIENT *
164clnt_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,
165    const char *nettype)
166{
167
168	return (clnt_create_timed(hostname, prog, vers, nettype, NULL));
169}
170
171/*
172 * This the routine has the same definition as clnt_create(),
173 * except it takes an additional timeout parameter - a pointer to
174 * a timeval structure.  A NULL value for the pointer indicates
175 * that the default timeout value should be used.
176 *
177 * This function calls clnt_tp_create_timed().
178 */
179CLIENT *
180clnt_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
181    const char *netclass, const struct timeval *tp)
182{
183	struct netconfig *nconf;
184	CLIENT *clnt = NULL;
185	void *handle;
186	enum clnt_stat	save_cf_stat = RPC_SUCCESS;
187	struct rpc_err	save_cf_error;
188	char nettype_array[NETIDLEN];
189	char *nettype = &nettype_array[0];
190
191	if (netclass == NULL)
192		nettype = NULL;
193	else {
194		size_t len = strlen(netclass);
195		if (len >= sizeof (nettype_array)) {
196			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
197			return (NULL);
198		}
199		strcpy(nettype, netclass);
200	}
201
202	if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
203		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
204		return (NULL);
205	}
206	rpc_createerr.cf_stat = RPC_SUCCESS;
207	while (clnt == NULL) {
208		if ((nconf = __rpc_getconf(handle)) == NULL) {
209			if (rpc_createerr.cf_stat == RPC_SUCCESS)
210				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
211			break;
212		}
213#ifdef CLNT_DEBUG
214		printf("trying netid %s\n", nconf->nc_netid);
215#endif
216		clnt = clnt_tp_create_timed(hostname, prog, vers, nconf, tp);
217		if (clnt)
218			break;
219		else
220			/*
221			 *	Since we didn't get a name-to-address
222			 *	translation failure here, we remember
223			 *	this particular error.  The object of
224			 *	this is to enable us to return to the
225			 *	caller a more-specific error than the
226			 *	unhelpful ``Name to address translation
227			 *	failed'' which might well occur if we
228			 *	merely returned the last error (because
229			 *	the local loopbacks are typically the
230			 *	last ones in /etc/netconfig and the most
231			 *	likely to be unable to translate a host
232			 *	name).  We also check for a more
233			 *	meaningful error than ``unknown host
234			 *	name'' for the same reasons.
235			 */
236			if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE &&
237			    rpc_createerr.cf_stat != RPC_UNKNOWNHOST) {
238				save_cf_stat = rpc_createerr.cf_stat;
239				save_cf_error = rpc_createerr.cf_error;
240			}
241	}
242
243	/*
244	 *	Attempt to return an error more specific than ``Name to address
245	 *	translation failed'' or ``unknown host name''
246	 */
247	if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE ||
248				rpc_createerr.cf_stat == RPC_UNKNOWNHOST) &&
249					(save_cf_stat != RPC_SUCCESS)) {
250		rpc_createerr.cf_stat = save_cf_stat;
251		rpc_createerr.cf_error = save_cf_error;
252	}
253	__rpc_endconf(handle);
254	return (clnt);
255}
256
257/*
258 * Generic client creation: takes (servers name, program-number, netconf) and
259 * returns client handle. Default options are set, which the user can
260 * change using the rpc equivalent of _ioctl()'s : clnt_control()
261 * It finds out the server address from rpcbind and calls clnt_tli_create().
262 *
263 * It calls clnt_tp_create_timed() with the default timeout.
264 */
265CLIENT *
266clnt_tp_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,
267    const struct netconfig *nconf)
268{
269
270	return (clnt_tp_create_timed(hostname, prog, vers, nconf, NULL));
271}
272
273/*
274 * This has the same definition as clnt_tp_create(), except it
275 * takes an additional parameter - a pointer to a timeval structure.
276 * A NULL value for the timeout pointer indicates that the default
277 * value for the timeout should be used.
278 */
279CLIENT *
280clnt_tp_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
281    const struct netconfig *nconf, const struct timeval *tp)
282{
283	struct netbuf *svcaddr;			/* servers address */
284	CLIENT *cl = NULL;			/* client handle */
285
286	if (nconf == NULL) {
287		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
288		return (NULL);
289	}
290
291	/*
292	 * Get the address of the server
293	 */
294	if ((svcaddr = __rpcb_findaddr_timed(prog, vers,
295			(struct netconfig *)nconf, (char *)hostname,
296			&cl, (struct timeval *)tp)) == NULL) {
297		/* appropriate error number is set by rpcbind libraries */
298		return (NULL);
299	}
300	if (cl == NULL) {
301		cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
302					prog, vers, 0, 0);
303	} else {
304		/* Reuse the CLIENT handle and change the appropriate fields */
305		if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) {
306			if (cl->cl_netid == NULL)
307				cl->cl_netid = strdup(nconf->nc_netid);
308			if (cl->cl_tp == NULL)
309				cl->cl_tp = strdup(nconf->nc_device);
310			(void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog);
311			(void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers);
312		} else {
313			CLNT_DESTROY(cl);
314			cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
315					prog, vers, 0, 0);
316		}
317	}
318	free(svcaddr->buf);
319	free(svcaddr);
320	return (cl);
321}
322
323/*
324 * Generic client creation:  returns client handle.
325 * Default options are set, which the user can
326 * change using the rpc equivalent of _ioctl()'s : clnt_control().
327 * If fd is RPC_ANYFD, it will be opened using nconf.
328 * It will be bound if not so.
329 * If sizes are 0; appropriate defaults will be chosen.
330 */
331CLIENT *
332clnt_tli_create(int fd, const struct netconfig *nconf,
333	struct netbuf *svcaddr, rpcprog_t prog, rpcvers_t vers,
334	uint sendsz, uint recvsz)
335{
336	CLIENT *cl;			/* client handle */
337	bool_t madefd = FALSE;		/* whether fd opened here */
338	long servtype;
339	int one = 1;
340	struct __rpc_sockinfo si;
341	extern int __rpc_minfd;
342
343	if (fd == RPC_ANYFD) {
344		if (nconf == NULL) {
345			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
346			return (NULL);
347		}
348
349		fd = __rpc_nconf2fd(nconf);
350
351		if (fd == -1)
352			goto err;
353		if (fd < __rpc_minfd)
354			fd = __rpc_raise_fd(fd);
355		madefd = TRUE;
356		servtype = nconf->nc_semantics;
357		if (!__rpc_fd2sockinfo(fd, &si))
358			goto err;
359		bindresvport(fd, NULL);
360	} else {
361		if (!__rpc_fd2sockinfo(fd, &si))
362			goto err;
363		servtype = __rpc_socktype2seman(si.si_socktype);
364		if (servtype == -1) {
365			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
366			return (NULL);
367		}
368	}
369
370	if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) {
371		rpc_createerr.cf_stat = RPC_UNKNOWNHOST;	/* XXX */
372		goto err1;
373	}
374
375	switch (servtype) {
376	case NC_TPI_COTS:
377		cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
378		break;
379	case NC_TPI_COTS_ORD:
380		if (nconf && ((strcmp(nconf->nc_protofmly, "inet") == 0))) {
381			_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one,
382			    sizeof (one));
383		}
384		cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
385		break;
386	case NC_TPI_CLTS:
387		cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz);
388		break;
389	default:
390		goto err;
391	}
392
393	if (cl == NULL)
394		goto err1; /* borrow errors from clnt_dg/vc creates */
395	if (nconf) {
396		cl->cl_netid = strdup(nconf->nc_netid);
397		cl->cl_tp = strdup(nconf->nc_device);
398	} else {
399		cl->cl_netid = "";
400		cl->cl_tp = "";
401	}
402	if (madefd) {
403		(void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
404/*		(void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL);  */
405	};
406
407	return (cl);
408
409err:
410	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
411	rpc_createerr.cf_error.re_errno = errno;
412err1:	if (madefd)
413		(void)_close(fd);
414	return (NULL);
415}
416
417/*
418 *  To avoid conflicts with the "magic" file descriptors (0, 1, and 2),
419 *  we try to not use them.  The __rpc_raise_fd() routine will dup
420 *  a descriptor to a higher value.  If we fail to do it, we continue
421 *  to use the old one (and hope for the best).
422 */
423int __rpc_minfd = 3;
424
425int
426__rpc_raise_fd(int fd)
427{
428	int nfd;
429
430	if (fd >= __rpc_minfd)
431		return (fd);
432
433	if ((nfd = _fcntl(fd, F_DUPFD, __rpc_minfd)) == -1)
434		return (fd);
435
436	if (_fsync(nfd) == -1) {
437		_close(nfd);
438		return (fd);
439	}
440
441	if (_close(fd) == -1) {
442		/* this is okay, we will syslog an error, then use the new fd */
443		(void) syslog(LOG_ERR,
444			"could not close() fd %d; mem & fd leak", fd);
445	}
446
447	return (nfd);
448}
449