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