xref: /dragonfly/lib/libc/rpc/clnt_generic.c (revision df052c2a9588fe12c7a2df4e61e2bfa3f3e16ce0)
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 *
clnt_create_vers(const char * hostname,rpcprog_t prog,rpcvers_t * vers_out,rpcvers_t vers_low,rpcvers_t vers_high,const char * nettype)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 *
clnt_create_vers_timed(const char * hostname,rpcprog_t prog,rpcvers_t * vers_out,rpcvers_t vers_low,rpcvers_t vers_high,const char * nettype,const struct timeval * tp)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 *
clnt_create(const char * hostname,rpcprog_t prog,rpcvers_t vers,const char * nettype)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 *
clnt_create_timed(const char * hostname,rpcprog_t prog,rpcvers_t vers,const char * netclass,const struct timeval * tp)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 *
clnt_tp_create(const char * hostname,rpcprog_t prog,rpcvers_t vers,const struct netconfig * nconf)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 *
clnt_tp_create_timed(const char * hostname,rpcprog_t prog,rpcvers_t vers,const struct netconfig * nconf,const struct timeval * tp)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 *
clnt_tli_create(int fd,const struct netconfig * nconf,struct netbuf * svcaddr,rpcprog_t prog,rpcvers_t vers,uint sendsz,uint recvsz)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
__rpc_raise_fd(int fd)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