Bring in a transport-independent RPC (TI-RPC).
[dragonfly.git] / lib / libc / rpc / clnt_generic.c
blob3b8dc20aa2e910d567b082896e81a10b5e2f3e03
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
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.
12 * The Original Code is Copyright 1998 by Sun Microsystems, Inc
14 * The Initial Developer of the Original Code is: Sun
15 * Microsystems, Inc.
17 * All Rights Reserved.
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.
26 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
27 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
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.
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.
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.
42 * Sun Microsystems, Inc.
43 * 2550 Garcia Avenue
44 * Mountain View, California 94043
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 * $DragonFly: src/lib/libc/rpc/clnt_generic.c,v 1.4 2005/01/31 22:29:38 dillon Exp $
55 * Copyright (c) 1986-1996,1998 by Sun Microsystems, Inc.
56 * All rights reserved.
58 #include "namespace.h"
59 #include "reentrant.h"
60 #include <sys/types.h>
61 #include <sys/fcntl.h>
62 #include <sys/socket.h>
63 #include <netinet/in.h>
64 #include <netinet/tcp.h>
65 #include <stdio.h>
66 #include <errno.h>
67 #include <netdb.h>
68 #include <syslog.h>
69 #include <rpc/rpc.h>
70 #include <rpc/nettype.h>
71 #include <string.h>
72 #include <stdlib.h>
73 #include <unistd.h>
74 #include "un-namespace.h"
75 #include "rpc_com.h"
77 extern bool_t __rpc_is_local_host(const char *);
78 int __rpc_raise_fd(int);
80 #ifndef NETIDLEN
81 #define NETIDLEN 32
82 #endif
86 * Generic client creation with version checking the value of
87 * vers_out is set to the highest server supported value
88 * vers_low <= vers_out <= vers_high AND an error results
89 * if this can not be done.
91 * It calls clnt_create_vers_timed() with a NULL value for the timeout
92 * pointer, which indicates that the default timeout should be used.
94 CLIENT *
95 clnt_create_vers(const char *hostname, rpcprog_t prog, rpcvers_t *vers_out,
96 rpcvers_t vers_low, rpcvers_t vers_high, const char *nettype)
99 return (clnt_create_vers_timed(hostname, prog, vers_out, vers_low,
100 vers_high, nettype, NULL));
104 * This the routine has the same definition as clnt_create_vers(),
105 * except it takes an additional timeout parameter - a pointer to
106 * a timeval structure. A NULL value for the pointer indicates
107 * that the default timeout value should be used.
109 CLIENT *
110 clnt_create_vers_timed(const char *hostname, rpcprog_t prog,
111 rpcvers_t *vers_out, rpcvers_t vers_low, rpcvers_t vers_high,
112 const char *nettype, const struct timeval *tp)
114 CLIENT *clnt;
115 struct timeval to;
116 enum clnt_stat rpc_stat;
117 struct rpc_err rpcerr;
119 clnt = clnt_create_timed(hostname, prog, vers_high, nettype, tp);
120 if (clnt == NULL) {
121 return (NULL);
123 to.tv_sec = 10;
124 to.tv_usec = 0;
125 rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
126 (char *)NULL, (xdrproc_t)xdr_void, (char *)NULL, to);
127 if (rpc_stat == RPC_SUCCESS) {
128 *vers_out = vers_high;
129 return (clnt);
131 while (rpc_stat == RPC_PROGVERSMISMATCH && vers_high > vers_low) {
132 unsigned int minvers, maxvers;
134 clnt_geterr(clnt, &rpcerr);
135 minvers = rpcerr.re_vers.low;
136 maxvers = rpcerr.re_vers.high;
137 if (maxvers < vers_high)
138 vers_high = maxvers;
139 else
140 vers_high--;
141 if (minvers > vers_low)
142 vers_low = minvers;
143 if (vers_low > vers_high) {
144 goto error;
146 CLNT_CONTROL(clnt, CLSET_VERS, (char *)&vers_high);
147 rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
148 (char *)NULL, (xdrproc_t)xdr_void,
149 (char *)NULL, to);
150 if (rpc_stat == RPC_SUCCESS) {
151 *vers_out = vers_high;
152 return (clnt);
155 clnt_geterr(clnt, &rpcerr);
157 error:
158 rpc_createerr.cf_stat = rpc_stat;
159 rpc_createerr.cf_error = rpcerr;
160 clnt_destroy(clnt);
161 return (NULL);
165 * Top level client creation routine.
166 * Generic client creation: takes (servers name, program-number, nettype) and
167 * returns client handle. Default options are set, which the user can
168 * change using the rpc equivalent of _ioctl()'s.
170 * It tries for all the netids in that particular class of netid until
171 * it succeeds.
172 * XXX The error message in the case of failure will be the one
173 * pertaining to the last create error.
175 * It calls clnt_create_timed() with the default timeout.
177 CLIENT *
178 clnt_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,
179 const char *nettype)
182 return (clnt_create_timed(hostname, prog, vers, nettype, NULL));
186 * This the routine has the same definition as clnt_create(),
187 * except it takes an additional timeout parameter - a pointer to
188 * a timeval structure. A NULL value for the pointer indicates
189 * that the default timeout value should be used.
191 * This function calls clnt_tp_create_timed().
193 CLIENT *
194 clnt_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
195 const char *netclass, const struct timeval *tp)
197 struct netconfig *nconf;
198 CLIENT *clnt = NULL;
199 void *handle;
200 enum clnt_stat save_cf_stat = RPC_SUCCESS;
201 struct rpc_err save_cf_error;
202 char nettype_array[NETIDLEN];
203 char *nettype = &nettype_array[0];
205 if (netclass == NULL)
206 nettype = NULL;
207 else {
208 size_t len = strlen(netclass);
209 if (len >= sizeof (nettype_array)) {
210 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
211 return (NULL);
213 strcpy(nettype, netclass);
216 if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
217 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
218 return (NULL);
220 rpc_createerr.cf_stat = RPC_SUCCESS;
221 while (clnt == NULL) {
222 if ((nconf = __rpc_getconf(handle)) == NULL) {
223 if (rpc_createerr.cf_stat == RPC_SUCCESS)
224 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
225 break;
227 #ifdef CLNT_DEBUG
228 printf("trying netid %s\n", nconf->nc_netid);
229 #endif
230 clnt = clnt_tp_create_timed(hostname, prog, vers, nconf, tp);
231 if (clnt)
232 break;
233 else
235 * Since we didn't get a name-to-address
236 * translation failure here, we remember
237 * this particular error. The object of
238 * this is to enable us to return to the
239 * caller a more-specific error than the
240 * unhelpful ``Name to address translation
241 * failed'' which might well occur if we
242 * merely returned the last error (because
243 * the local loopbacks are typically the
244 * last ones in /etc/netconfig and the most
245 * likely to be unable to translate a host
246 * name). We also check for a more
247 * meaningful error than ``unknown host
248 * name'' for the same reasons.
250 if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE &&
251 rpc_createerr.cf_stat != RPC_UNKNOWNHOST) {
252 save_cf_stat = rpc_createerr.cf_stat;
253 save_cf_error = rpc_createerr.cf_error;
258 * Attempt to return an error more specific than ``Name to address
259 * translation failed'' or ``unknown host name''
261 if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE ||
262 rpc_createerr.cf_stat == RPC_UNKNOWNHOST) &&
263 (save_cf_stat != RPC_SUCCESS)) {
264 rpc_createerr.cf_stat = save_cf_stat;
265 rpc_createerr.cf_error = save_cf_error;
267 __rpc_endconf(handle);
268 return (clnt);
272 * Generic client creation: takes (servers name, program-number, netconf) and
273 * returns client handle. Default options are set, which the user can
274 * change using the rpc equivalent of _ioctl()'s : clnt_control()
275 * It finds out the server address from rpcbind and calls clnt_tli_create().
277 * It calls clnt_tp_create_timed() with the default timeout.
279 CLIENT *
280 clnt_tp_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,
281 const struct netconfig *nconf)
284 return (clnt_tp_create_timed(hostname, prog, vers, nconf, NULL));
288 * This has the same definition as clnt_tp_create(), except it
289 * takes an additional parameter - a pointer to a timeval structure.
290 * A NULL value for the timeout pointer indicates that the default
291 * value for the timeout should be used.
293 CLIENT *
294 clnt_tp_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
295 const struct netconfig *nconf, const struct timeval *tp)
297 struct netbuf *svcaddr; /* servers address */
298 CLIENT *cl = NULL; /* client handle */
300 if (nconf == NULL) {
301 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
302 return (NULL);
306 * Get the address of the server
308 if ((svcaddr = __rpcb_findaddr_timed(prog, vers,
309 (struct netconfig *)nconf, (char *)hostname,
310 &cl, (struct timeval *)tp)) == NULL) {
311 /* appropriate error number is set by rpcbind libraries */
312 return (NULL);
314 if (cl == NULL) {
315 cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
316 prog, vers, 0, 0);
317 } else {
318 /* Reuse the CLIENT handle and change the appropriate fields */
319 if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) {
320 if (cl->cl_netid == NULL)
321 cl->cl_netid = strdup(nconf->nc_netid);
322 if (cl->cl_tp == NULL)
323 cl->cl_tp = strdup(nconf->nc_device);
324 CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog);
325 CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers);
326 } else {
327 CLNT_DESTROY(cl);
328 cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
329 prog, vers, 0, 0);
332 free(svcaddr->buf);
333 free(svcaddr);
334 return (cl);
338 * Generic client creation: returns client handle.
339 * Default options are set, which the user can
340 * change using the rpc equivalent of _ioctl()'s : clnt_control().
341 * If fd is RPC_ANYFD, it will be opened using nconf.
342 * It will be bound if not so.
343 * If sizes are 0; appropriate defaults will be chosen.
345 CLIENT *
346 clnt_tli_create(int fd, const struct netconfig *nconf,
347 struct netbuf *svcaddr, rpcprog_t prog, rpcvers_t vers,
348 uint sendsz, uint recvsz)
350 CLIENT *cl; /* client handle */
351 bool_t madefd = FALSE; /* whether fd opened here */
352 long servtype;
353 int one = 1;
354 struct __rpc_sockinfo si;
355 extern int __rpc_minfd;
357 if (fd == RPC_ANYFD) {
358 if (nconf == NULL) {
359 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
360 return (NULL);
363 fd = __rpc_nconf2fd(nconf);
365 if (fd == -1)
366 goto err;
367 if (fd < __rpc_minfd)
368 fd = __rpc_raise_fd(fd);
369 madefd = TRUE;
370 servtype = nconf->nc_semantics;
371 if (!__rpc_fd2sockinfo(fd, &si))
372 goto err;
373 bindresvport(fd, NULL);
374 } else {
375 if (!__rpc_fd2sockinfo(fd, &si))
376 goto err;
377 servtype = __rpc_socktype2seman(si.si_socktype);
378 if (servtype == -1) {
379 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
380 return (NULL);
384 if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) {
385 rpc_createerr.cf_stat = RPC_UNKNOWNHOST; /* XXX */
386 goto err1;
389 switch (servtype) {
390 case NC_TPI_COTS:
391 cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
392 break;
393 case NC_TPI_COTS_ORD:
394 if (nconf && ((strcmp(nconf->nc_protofmly, "inet") == 0))) {
395 _setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one,
396 sizeof (one));
398 cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
399 break;
400 case NC_TPI_CLTS:
401 cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz);
402 break;
403 default:
404 goto err;
407 if (cl == NULL)
408 goto err1; /* borrow errors from clnt_dg/vc creates */
409 if (nconf) {
410 cl->cl_netid = strdup(nconf->nc_netid);
411 cl->cl_tp = strdup(nconf->nc_device);
412 } else {
413 cl->cl_netid = "";
414 cl->cl_tp = "";
416 if (madefd) {
417 CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
418 /* CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL); */
421 return (cl);
423 err:
424 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
425 rpc_createerr.cf_error.re_errno = errno;
426 err1: if (madefd)
427 _close(fd);
428 return (NULL);
432 * To avoid conflicts with the "magic" file descriptors (0, 1, and 2),
433 * we try to not use them. The __rpc_raise_fd() routine will dup
434 * a descriptor to a higher value. If we fail to do it, we continue
435 * to use the old one (and hope for the best).
437 int __rpc_minfd = 3;
440 __rpc_raise_fd(int fd)
442 int nfd;
444 if (fd >= __rpc_minfd)
445 return (fd);
447 if ((nfd = _fcntl(fd, F_DUPFD, __rpc_minfd)) == -1)
448 return (fd);
450 if (_fsync(nfd) == -1) {
451 _close(nfd);
452 return (fd);
455 if (_close(fd) == -1) {
456 /* this is okay, we will syslog an error, then use the new fd */
457 syslog(LOG_ERR, "could not close() fd %d; mem & fd leak", fd);
460 return (nfd);