Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libc/rpc/clnt_generic.c
39530 views
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
/*
34
* Copyright (c) 1986-1996,1998 by Sun Microsystems, Inc.
35
* All rights reserved.
36
*/
37
#include "namespace.h"
38
#include "reentrant.h"
39
#include <sys/types.h>
40
#include <sys/fcntl.h>
41
#include <sys/socket.h>
42
#include <netinet/in.h>
43
#include <netinet/tcp.h>
44
#include <stdio.h>
45
#include <errno.h>
46
#include <netdb.h>
47
#include <syslog.h>
48
#include <rpc/rpc.h>
49
#include <rpc/nettype.h>
50
#include <string.h>
51
#include <stdlib.h>
52
#include <unistd.h>
53
#include "un-namespace.h"
54
#include "rpc_com.h"
55
56
extern bool_t __rpc_is_local_host(const char *);
57
int __rpc_raise_fd(int);
58
59
#ifndef NETIDLEN
60
#define NETIDLEN 32
61
#endif
62
63
64
/*
65
* Generic client creation with version checking the value of
66
* vers_out is set to the highest server supported value
67
* vers_low <= vers_out <= vers_high AND an error results
68
* if this can not be done.
69
*
70
* It calls clnt_create_vers_timed() with a NULL value for the timeout
71
* pointer, which indicates that the default timeout should be used.
72
*/
73
CLIENT *
74
clnt_create_vers(const char *hostname, rpcprog_t prog, rpcvers_t *vers_out,
75
rpcvers_t vers_low, rpcvers_t vers_high, const char *nettype)
76
{
77
78
return (clnt_create_vers_timed(hostname, prog, vers_out, vers_low,
79
vers_high, nettype, NULL));
80
}
81
82
/*
83
* This the routine has the same definition as clnt_create_vers(),
84
* except it takes an additional timeout parameter - a pointer to
85
* a timeval structure. A NULL value for the pointer indicates
86
* that the default timeout value should be used.
87
*/
88
CLIENT *
89
clnt_create_vers_timed(const char *hostname, rpcprog_t prog,
90
rpcvers_t *vers_out, rpcvers_t vers_low, rpcvers_t vers_high,
91
const char *nettype, const struct timeval *tp)
92
{
93
CLIENT *clnt;
94
struct timeval to;
95
enum clnt_stat rpc_stat;
96
struct rpc_err rpcerr;
97
98
clnt = clnt_create_timed(hostname, prog, vers_high, nettype, tp);
99
if (clnt == NULL) {
100
return (NULL);
101
}
102
to.tv_sec = 10;
103
to.tv_usec = 0;
104
rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
105
(char *)NULL, (xdrproc_t)xdr_void, (char *)NULL, to);
106
if (rpc_stat == RPC_SUCCESS) {
107
*vers_out = vers_high;
108
return (clnt);
109
}
110
while (rpc_stat == RPC_PROGVERSMISMATCH && vers_high > vers_low) {
111
unsigned int minvers, maxvers;
112
113
clnt_geterr(clnt, &rpcerr);
114
minvers = rpcerr.re_vers.low;
115
maxvers = rpcerr.re_vers.high;
116
if (maxvers < vers_high)
117
vers_high = maxvers;
118
else
119
vers_high--;
120
if (minvers > vers_low)
121
vers_low = minvers;
122
if (vers_low > vers_high) {
123
goto error;
124
}
125
CLNT_CONTROL(clnt, CLSET_VERS, (char *)&vers_high);
126
rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
127
(char *)NULL, (xdrproc_t)xdr_void,
128
(char *)NULL, to);
129
if (rpc_stat == RPC_SUCCESS) {
130
*vers_out = vers_high;
131
return (clnt);
132
}
133
}
134
clnt_geterr(clnt, &rpcerr);
135
136
error:
137
rpc_createerr.cf_stat = rpc_stat;
138
rpc_createerr.cf_error = rpcerr;
139
clnt_destroy(clnt);
140
return (NULL);
141
}
142
143
/*
144
* Top level client creation routine.
145
* Generic client creation: takes (servers name, program-number, nettype) and
146
* returns client handle. Default options are set, which the user can
147
* change using the rpc equivalent of _ioctl()'s.
148
*
149
* It tries for all the netids in that particular class of netid until
150
* it succeeds.
151
* XXX The error message in the case of failure will be the one
152
* pertaining to the last create error.
153
*
154
* It calls clnt_create_timed() with the default timeout.
155
*/
156
CLIENT *
157
clnt_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,
158
const char *nettype)
159
{
160
161
return (clnt_create_timed(hostname, prog, vers, nettype, NULL));
162
}
163
164
/*
165
* This the routine has the same definition as clnt_create(),
166
* except it takes an additional timeout parameter - a pointer to
167
* a timeval structure. A NULL value for the pointer indicates
168
* that the default timeout value should be used.
169
*
170
* This function calls clnt_tp_create_timed().
171
*/
172
CLIENT *
173
clnt_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
174
const char *netclass, const struct timeval *tp)
175
{
176
struct netconfig *nconf;
177
CLIENT *clnt = NULL;
178
void *handle;
179
enum clnt_stat save_cf_stat = RPC_SUCCESS;
180
struct rpc_err save_cf_error;
181
char nettype_array[NETIDLEN];
182
char *nettype = &nettype_array[0];
183
184
if (netclass == NULL)
185
nettype = NULL;
186
else {
187
size_t len = strlen(netclass);
188
if (len >= sizeof (nettype_array)) {
189
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
190
return (NULL);
191
}
192
strcpy(nettype, netclass);
193
}
194
195
if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
196
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
197
return (NULL);
198
}
199
rpc_createerr.cf_stat = RPC_SUCCESS;
200
while (clnt == NULL) {
201
if ((nconf = __rpc_getconf(handle)) == NULL) {
202
if (rpc_createerr.cf_stat == RPC_SUCCESS)
203
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
204
break;
205
}
206
#ifdef CLNT_DEBUG
207
printf("trying netid %s\n", nconf->nc_netid);
208
#endif
209
clnt = clnt_tp_create_timed(hostname, prog, vers, nconf, tp);
210
if (clnt)
211
break;
212
else
213
/*
214
* Since we didn't get a name-to-address
215
* translation failure here, we remember
216
* this particular error. The object of
217
* this is to enable us to return to the
218
* caller a more-specific error than the
219
* unhelpful ``Name to address translation
220
* failed'' which might well occur if we
221
* merely returned the last error (because
222
* the local loopbacks are typically the
223
* last ones in /etc/netconfig and the most
224
* likely to be unable to translate a host
225
* name). We also check for a more
226
* meaningful error than ``unknown host
227
* name'' for the same reasons.
228
*/
229
if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE &&
230
rpc_createerr.cf_stat != RPC_UNKNOWNHOST) {
231
save_cf_stat = rpc_createerr.cf_stat;
232
save_cf_error = rpc_createerr.cf_error;
233
}
234
}
235
236
/*
237
* Attempt to return an error more specific than ``Name to address
238
* translation failed'' or ``unknown host name''
239
*/
240
if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE ||
241
rpc_createerr.cf_stat == RPC_UNKNOWNHOST) &&
242
(save_cf_stat != RPC_SUCCESS)) {
243
rpc_createerr.cf_stat = save_cf_stat;
244
rpc_createerr.cf_error = save_cf_error;
245
}
246
__rpc_endconf(handle);
247
return (clnt);
248
}
249
250
/*
251
* Generic client creation: takes (servers name, program-number, netconf) and
252
* returns client handle. Default options are set, which the user can
253
* change using the rpc equivalent of _ioctl()'s : clnt_control()
254
* It finds out the server address from rpcbind and calls clnt_tli_create().
255
*
256
* It calls clnt_tp_create_timed() with the default timeout.
257
*/
258
CLIENT *
259
clnt_tp_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,
260
const struct netconfig *nconf)
261
{
262
263
return (clnt_tp_create_timed(hostname, prog, vers, nconf, NULL));
264
}
265
266
/*
267
* This has the same definition as clnt_tp_create(), except it
268
* takes an additional parameter - a pointer to a timeval structure.
269
* A NULL value for the timeout pointer indicates that the default
270
* value for the timeout should be used.
271
*/
272
CLIENT *
273
clnt_tp_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
274
const struct netconfig *nconf, const struct timeval *tp)
275
{
276
struct netbuf *svcaddr; /* servers address */
277
CLIENT *cl = NULL; /* client handle */
278
279
if (nconf == NULL) {
280
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
281
return (NULL);
282
}
283
284
/*
285
* Get the address of the server
286
*/
287
if ((svcaddr = __rpcb_findaddr_timed(prog, vers,
288
(struct netconfig *)nconf, (char *)hostname,
289
&cl, (struct timeval *)tp)) == NULL) {
290
/* appropriate error number is set by rpcbind libraries */
291
return (NULL);
292
}
293
if (cl == NULL) {
294
cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
295
prog, vers, 0, 0);
296
} else {
297
/* Reuse the CLIENT handle and change the appropriate fields */
298
if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) {
299
if (cl->cl_netid == NULL)
300
cl->cl_netid = strdup(nconf->nc_netid);
301
if (cl->cl_tp == NULL)
302
cl->cl_tp = strdup(nconf->nc_device);
303
(void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog);
304
(void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers);
305
} else {
306
CLNT_DESTROY(cl);
307
cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
308
prog, vers, 0, 0);
309
}
310
}
311
free(svcaddr->buf);
312
free(svcaddr);
313
return (cl);
314
}
315
316
/*
317
* Generic client creation: returns client handle.
318
* Default options are set, which the user can
319
* change using the rpc equivalent of _ioctl()'s : clnt_control().
320
* If fd is RPC_ANYFD, it will be opened using nconf.
321
* It will be bound if not so.
322
* If sizes are 0; appropriate defaults will be chosen.
323
*/
324
CLIENT *
325
clnt_tli_create(int fd, const struct netconfig *nconf,
326
struct netbuf *svcaddr, rpcprog_t prog, rpcvers_t vers,
327
uint sendsz, uint recvsz)
328
{
329
CLIENT *cl; /* client handle */
330
bool_t madefd = FALSE; /* whether fd opened here */
331
long servtype;
332
int one = 1;
333
struct __rpc_sockinfo si;
334
extern int __rpc_minfd;
335
336
if (fd == RPC_ANYFD) {
337
if (nconf == NULL) {
338
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
339
return (NULL);
340
}
341
342
fd = __rpc_nconf2fd(nconf);
343
344
if (fd == -1)
345
goto err;
346
if (fd < __rpc_minfd)
347
fd = __rpc_raise_fd(fd);
348
madefd = TRUE;
349
servtype = nconf->nc_semantics;
350
if (!__rpc_fd2sockinfo(fd, &si))
351
goto err;
352
bindresvport(fd, NULL);
353
} else {
354
if (!__rpc_fd2sockinfo(fd, &si))
355
goto err;
356
servtype = __rpc_socktype2seman(si.si_socktype);
357
if (servtype == -1) {
358
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
359
return (NULL);
360
}
361
}
362
363
if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) {
364
rpc_createerr.cf_stat = RPC_UNKNOWNHOST; /* XXX */
365
goto err1;
366
}
367
368
switch (servtype) {
369
case NC_TPI_COTS:
370
cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
371
break;
372
case NC_TPI_COTS_ORD:
373
if (nconf && ((strcmp(nconf->nc_protofmly, "inet") == 0))) {
374
_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one,
375
sizeof (one));
376
}
377
cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
378
break;
379
case NC_TPI_CLTS:
380
cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz);
381
break;
382
default:
383
goto err;
384
}
385
386
if (cl == NULL)
387
goto err1; /* borrow errors from clnt_dg/vc creates */
388
if (nconf) {
389
cl->cl_netid = strdup(nconf->nc_netid);
390
cl->cl_tp = strdup(nconf->nc_device);
391
} else {
392
cl->cl_netid = "";
393
cl->cl_tp = "";
394
}
395
if (madefd) {
396
(void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
397
/* (void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL); */
398
}
399
400
return (cl);
401
402
err:
403
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
404
rpc_createerr.cf_error.re_errno = errno;
405
err1: if (madefd)
406
(void)_close(fd);
407
return (NULL);
408
}
409
410
/*
411
* To avoid conflicts with the "magic" file descriptors (0, 1, and 2),
412
* we try to not use them. The __rpc_raise_fd() routine will dup
413
* a descriptor to a higher value. If we fail to do it, we continue
414
* to use the old one (and hope for the best).
415
*/
416
int __rpc_minfd = 3;
417
418
int
419
__rpc_raise_fd(int fd)
420
{
421
int nfd;
422
423
if (fd >= __rpc_minfd)
424
return (fd);
425
426
if ((nfd = _fcntl(fd, F_DUPFD, __rpc_minfd)) == -1)
427
return (fd);
428
429
if (_fsync(nfd) == -1) {
430
_close(nfd);
431
return (fd);
432
}
433
434
if (_close(fd) == -1) {
435
/* this is okay, we will syslog an error, then use the new fd */
436
(void) syslog(LOG_ERR,
437
"could not close() fd %d; mem & fd leak", fd);
438
}
439
440
return (nfd);
441
}
442
443