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