]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/rpc/clnt_generic.c
Merge compiler-rt trunk r321017 to contrib/compiler-rt.
[FreeBSD/FreeBSD.git] / lib / libc / rpc / clnt_generic.c
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)
36 static char *sccsid2 = "from: @(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI";
37 static 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
65 extern bool_t __rpc_is_local_host(const char *);
66 int __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  */
82 CLIENT *
83 clnt_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  */
97 CLIENT *
98 clnt_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
145 error:
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  */
165 CLIENT *
166 clnt_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  */
181 CLIENT *
182 clnt_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  */
267 CLIENT *
268 clnt_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  */
281 CLIENT *
282 clnt_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  */
333 CLIENT *
334 clnt_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
411 err:
412         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
413         rpc_createerr.cf_error.re_errno = errno;
414 err1:   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  */
425 int __rpc_minfd = 3;
426
427 int
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 }