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