xref: /freebsd/lib/libc/rpc/clnt_generic.c (revision aa0a1e58)
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)
52 static char *sccsid2 = "from: @(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI";
53 static 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 
81 extern bool_t __rpc_is_local_host(const char *);
82 int __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  */
98 CLIENT *
99 clnt_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  */
113 CLIENT *
114 clnt_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 
161 error:
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  */
181 CLIENT *
182 clnt_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  */
197 CLIENT *
198 clnt_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  */
283 CLIENT *
284 clnt_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  */
297 CLIENT *
298 clnt_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  */
349 CLIENT *
350 clnt_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 
427 err:
428 	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
429 	rpc_createerr.cf_error.re_errno = errno;
430 err1:	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  */
441 int __rpc_minfd = 3;
442 
443 int
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