Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/lib/rpc/clnt_tcp.c
39536 views
1
/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */
2
/*
3
* Copyright (c) 2010, Oracle America, Inc.
4
*
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions are met:
9
*
10
* * Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
*
13
* * Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in
15
* the documentation and/or other materials provided with the
16
* distribution.
17
*
18
* * Neither the name of the "Oracle America, Inc." nor the names of
19
* its contributors may be used to endorse or promote products
20
* derived from this software without specific prior written permission.
21
*
22
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
23
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
*/
34
#if !defined(lint) && defined(SCCSIDS)
35
static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
36
#endif
37
38
/*
39
* clnt_tcp.c, Implements a TCP/IP based, client side RPC.
40
*
41
* TCP based RPC supports 'batched calls'.
42
* A sequence of calls may be batched-up in a send buffer. The rpc call
43
* return immediately to the client even though the call was not necessarily
44
* sent. The batching occurs if the results' xdr routine is NULL (0) AND
45
* the rpc timeout value is zero (see clnt.h, rpc).
46
*
47
* Clients should NOT casually batch calls that in fact return results; that is,
48
* the server side should be aware that a call is batched and not produce any
49
* return message. Batched calls that produce many result messages can
50
* deadlock (netlock) the client and the server....
51
*
52
* Now go hang yourself.
53
*/
54
55
#include <stdio.h>
56
#include <unistd.h>
57
#include <gssrpc/rpc.h>
58
#include <sys/socket.h>
59
#include <netdb.h>
60
#include <errno.h>
61
#include <string.h>
62
#include <gssrpc/pmap_clnt.h>
63
/* FD_ZERO may need memset declaration (e.g., Solaris 9) */
64
#include <string.h>
65
#include <port-sockets.h>
66
67
#define MCALL_MSG_SIZE 24
68
69
#ifndef GETSOCKNAME_ARG3_TYPE
70
#define GETSOCKNAME_ARG3_TYPE int
71
#endif
72
73
static enum clnt_stat clnttcp_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
74
xdrproc_t, void *, struct timeval);
75
static void clnttcp_abort(CLIENT *);
76
static void clnttcp_geterr(CLIENT *, struct rpc_err *);
77
static bool_t clnttcp_freeres(CLIENT *, xdrproc_t, void *);
78
static bool_t clnttcp_control(CLIENT *, int, void *);
79
static void clnttcp_destroy(CLIENT *);
80
81
static struct clnt_ops tcp_ops = {
82
clnttcp_call,
83
clnttcp_abort,
84
clnttcp_geterr,
85
clnttcp_freeres,
86
clnttcp_destroy,
87
clnttcp_control
88
};
89
90
struct ct_data {
91
int ct_sock;
92
bool_t ct_closeit;
93
struct timeval ct_wait;
94
bool_t ct_waitset; /* wait set by clnt_control? */
95
struct sockaddr_in ct_addr;
96
struct rpc_err ct_error;
97
union {
98
char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */
99
uint32_t ct_mcalli;
100
} ct_u;
101
u_int ct_mpos; /* pos after marshal */
102
XDR ct_xdrs;
103
};
104
105
static int readtcp(char *, caddr_t, int);
106
static int writetcp(char *, caddr_t, int);
107
108
109
/*
110
* Create a client handle for a tcp/ip connection.
111
* If *sockp<0, *sockp is set to a newly created TCP socket and it is
112
* connected to raddr. If *sockp non-negative then
113
* raddr is ignored. The rpc/tcp package does buffering
114
* similar to stdio, so the client must pick send and receive buffer sizes,];
115
* 0 => use the default.
116
* If raddr->sin_port is 0, then a binder on the remote machine is
117
* consulted for the right port number.
118
* NB: *sockp is copied into a private area.
119
* NB: It is the clients responsibility to close *sockp.
120
* NB: The rpch->cl_auth is set null authentication. Caller may wish to set this
121
* something more useful.
122
*/
123
CLIENT *
124
clnttcp_create(
125
struct sockaddr_in *raddr,
126
rpcprog_t prog,
127
rpcvers_t vers,
128
SOCKET *sockp,
129
u_int sendsz,
130
u_int recvsz)
131
{
132
CLIENT *h;
133
struct ct_data *ct = 0;
134
struct timeval now;
135
struct rpc_msg call_msg;
136
137
h = (CLIENT *)mem_alloc(sizeof(*h));
138
if (h == NULL) {
139
(void)fprintf(stderr, "clnttcp_create: out of memory\n");
140
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
141
rpc_createerr.cf_error.re_errno = errno;
142
goto fooy;
143
}
144
ct = (struct ct_data *)mem_alloc(sizeof(*ct));
145
if (ct == NULL) {
146
(void)fprintf(stderr, "clnttcp_create: out of memory\n");
147
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
148
rpc_createerr.cf_error.re_errno = errno;
149
goto fooy;
150
}
151
152
/*
153
* If no port number given ask the pmap for one
154
*/
155
if (raddr != NULL && raddr->sin_port == 0) {
156
u_short port;
157
if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) {
158
mem_free((caddr_t)ct, sizeof(struct ct_data));
159
mem_free((caddr_t)h, sizeof(CLIENT));
160
return ((CLIENT *)NULL);
161
}
162
raddr->sin_port = htons(port);
163
}
164
165
/*
166
* If no socket given, open one
167
*/
168
if (*sockp < 0) {
169
*sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
170
(void)bindresvport_sa(*sockp, NULL);
171
if (*sockp < 0 || raddr == NULL ||
172
connect(*sockp, (struct sockaddr *)raddr,
173
sizeof(*raddr)) < 0) {
174
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
175
rpc_createerr.cf_error.re_errno = errno;
176
(void)closesocket(*sockp);
177
goto fooy;
178
}
179
ct->ct_closeit = TRUE;
180
} else {
181
ct->ct_closeit = FALSE;
182
}
183
184
/*
185
* Set up private data struct
186
*/
187
ct->ct_sock = *sockp;
188
ct->ct_wait.tv_usec = 0;
189
ct->ct_waitset = FALSE;
190
if (raddr == NULL) {
191
/* Get the remote address from the socket, if it's IPv4. */
192
struct sockaddr_in sin;
193
socklen_t len = sizeof(sin);
194
int ret = getpeername(ct->ct_sock, (struct sockaddr *)&sin, &len);
195
if (ret == 0 && len == sizeof(sin) && sin.sin_family == AF_INET)
196
ct->ct_addr = sin;
197
else
198
memset(&ct->ct_addr, 0, sizeof(ct->ct_addr));
199
} else
200
ct->ct_addr = *raddr;
201
202
/*
203
* Initialize call message
204
*/
205
(void)gettimeofday(&now, (struct timezone *)0);
206
call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
207
call_msg.rm_direction = CALL;
208
call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
209
call_msg.rm_call.cb_prog = prog;
210
call_msg.rm_call.cb_vers = vers;
211
212
/*
213
* pre-serialize the staic part of the call msg and stash it away
214
*/
215
xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcall, MCALL_MSG_SIZE,
216
XDR_ENCODE);
217
if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
218
if (ct->ct_closeit)
219
(void)closesocket(*sockp);
220
goto fooy;
221
}
222
ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
223
XDR_DESTROY(&(ct->ct_xdrs));
224
225
/*
226
* Create a client handle which uses xdrrec for serialization
227
* and authnone for authentication.
228
*/
229
xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
230
(caddr_t)ct, readtcp, writetcp);
231
h->cl_ops = &tcp_ops;
232
h->cl_private = (caddr_t) ct;
233
h->cl_auth = authnone_create();
234
return (h);
235
236
fooy:
237
/*
238
* Something goofed, free stuff and barf
239
*/
240
mem_free((caddr_t)ct, sizeof(struct ct_data));
241
mem_free((caddr_t)h, sizeof(CLIENT));
242
return ((CLIENT *)NULL);
243
}
244
245
static enum clnt_stat
246
clnttcp_call(
247
CLIENT *h,
248
rpcproc_t proc,
249
xdrproc_t xdr_args,
250
void * args_ptr,
251
xdrproc_t xdr_results,
252
void * results_ptr,
253
struct timeval timeout)
254
{
255
struct ct_data *ct = h->cl_private;
256
XDR *xdrs = &ct->ct_xdrs;
257
struct rpc_msg reply_msg;
258
uint32_t x_id;
259
uint32_t *msg_x_id = &ct->ct_u.ct_mcalli; /* yuk */
260
bool_t shipnow;
261
int refreshes = 2;
262
long procl = proc;
263
264
if (!ct->ct_waitset) {
265
ct->ct_wait = timeout;
266
}
267
268
shipnow =
269
(xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
270
&& timeout.tv_usec == 0) ? FALSE : TRUE;
271
272
call_again:
273
xdrs->x_op = XDR_ENCODE;
274
ct->ct_error.re_status = RPC_SUCCESS;
275
x_id = ntohl(--(*msg_x_id));
276
if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcall, ct->ct_mpos)) ||
277
(! XDR_PUTLONG(xdrs, &procl)) ||
278
(! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
279
(! AUTH_WRAP(h->cl_auth, xdrs, xdr_args, args_ptr))) {
280
if (ct->ct_error.re_status == RPC_SUCCESS)
281
ct->ct_error.re_status = RPC_CANTENCODEARGS;
282
(void)xdrrec_endofrecord(xdrs, TRUE);
283
return (ct->ct_error.re_status);
284
}
285
if (! xdrrec_endofrecord(xdrs, shipnow))
286
return (ct->ct_error.re_status = RPC_CANTSEND);
287
if (! shipnow)
288
return (RPC_SUCCESS);
289
/*
290
* Hack to provide rpc-based message passing
291
*/
292
if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
293
return(ct->ct_error.re_status = RPC_TIMEDOUT);
294
}
295
296
297
/*
298
* Keep receiving until we get a valid transaction id
299
*/
300
xdrs->x_op = XDR_DECODE;
301
while (TRUE) {
302
reply_msg.acpted_rply.ar_verf = gssrpc__null_auth;
303
reply_msg.acpted_rply.ar_results.where = NULL;
304
reply_msg.acpted_rply.ar_results.proc = xdr_void;
305
if (! xdrrec_skiprecord(xdrs))
306
return (ct->ct_error.re_status);
307
/* now decode and validate the response header */
308
if (! xdr_replymsg(xdrs, &reply_msg)) {
309
/*
310
* Free some stuff allocated by xdr_replymsg()
311
* to avoid leaks, since it may allocate
312
* memory from partially successful decodes.
313
*/
314
enum xdr_op op = xdrs->x_op;
315
xdrs->x_op = XDR_FREE;
316
xdr_replymsg(xdrs, &reply_msg);
317
xdrs->x_op = op;
318
if (ct->ct_error.re_status == RPC_SUCCESS)
319
continue;
320
return (ct->ct_error.re_status);
321
}
322
if (reply_msg.rm_xid == x_id)
323
break;
324
}
325
326
/*
327
* process header
328
*/
329
gssrpc__seterr_reply(&reply_msg, &(ct->ct_error));
330
if (ct->ct_error.re_status == RPC_SUCCESS) {
331
if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
332
ct->ct_error.re_status = RPC_AUTHERROR;
333
ct->ct_error.re_why = AUTH_INVALIDRESP;
334
} else if (! AUTH_UNWRAP(h->cl_auth, xdrs,
335
xdr_results, results_ptr)) {
336
if (ct->ct_error.re_status == RPC_SUCCESS)
337
ct->ct_error.re_status = RPC_CANTDECODERES;
338
}
339
} /* end successful completion */
340
else {
341
/* maybe our credentials need to be refreshed ... */
342
if (refreshes-- && AUTH_REFRESH(h->cl_auth, &reply_msg))
343
goto call_again;
344
} /* end of unsuccessful completion */
345
/* free verifier ... */
346
if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
347
(reply_msg.acpted_rply.ar_verf.oa_base != NULL)) {
348
xdrs->x_op = XDR_FREE;
349
(void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
350
}
351
return (ct->ct_error.re_status);
352
}
353
354
static void
355
clnttcp_geterr(
356
CLIENT *h,
357
struct rpc_err *errp)
358
{
359
struct ct_data *ct = h->cl_private;
360
361
*errp = ct->ct_error;
362
}
363
364
static bool_t
365
clnttcp_freeres(
366
CLIENT *cl,
367
xdrproc_t xdr_res,
368
void * res_ptr)
369
{
370
struct ct_data *ct = cl->cl_private;
371
XDR *xdrs = &ct->ct_xdrs;
372
373
xdrs->x_op = XDR_FREE;
374
return ((*xdr_res)(xdrs, res_ptr));
375
}
376
377
/*ARGSUSED*/
378
static void
379
clnttcp_abort(CLIENT *cl)
380
{
381
}
382
383
static bool_t
384
clnttcp_control(
385
CLIENT *cl,
386
int request,
387
void *info)
388
{
389
struct ct_data *ct = cl->cl_private;
390
GETSOCKNAME_ARG3_TYPE len;
391
392
switch (request) {
393
case CLSET_TIMEOUT:
394
ct->ct_wait = *(struct timeval *)info;
395
ct->ct_waitset = TRUE;
396
break;
397
case CLGET_TIMEOUT:
398
*(struct timeval *)info = ct->ct_wait;
399
break;
400
case CLGET_SERVER_ADDR:
401
*(struct sockaddr_in *)info = ct->ct_addr;
402
break;
403
case CLGET_LOCAL_ADDR:
404
len = sizeof(struct sockaddr);
405
if (getsockname(ct->ct_sock, (struct sockaddr*)info, &len) < 0)
406
return FALSE;
407
else
408
return TRUE;
409
default:
410
return (FALSE);
411
}
412
return (TRUE);
413
}
414
415
416
static void
417
clnttcp_destroy(CLIENT *h)
418
{
419
struct ct_data *ct = h->cl_private;
420
421
if (ct->ct_closeit)
422
(void)closesocket(ct->ct_sock);
423
XDR_DESTROY(&(ct->ct_xdrs));
424
mem_free((caddr_t)ct, sizeof(struct ct_data));
425
mem_free((caddr_t)h, sizeof(CLIENT));
426
}
427
428
/*
429
* Interface between xdr serializer and tcp connection.
430
* Behaves like the system calls, read & write, but keeps some error state
431
* around for the rpc level.
432
*/
433
static int
434
readtcp(
435
char *ctptr,
436
caddr_t buf,
437
int len)
438
{
439
struct ct_data *ct = (void *)ctptr;
440
struct timeval tout;
441
#ifdef FD_SETSIZE
442
fd_set mask;
443
fd_set readfds;
444
445
if (len == 0)
446
return (0);
447
FD_ZERO(&mask);
448
FD_SET(ct->ct_sock, &mask);
449
#else
450
int mask = 1 << (ct->ct_sock);
451
int readfds;
452
453
if (len == 0)
454
return (0);
455
456
#endif /* def FD_SETSIZE */
457
while (TRUE) {
458
readfds = mask;
459
tout = ct->ct_wait;
460
switch (select(gssrpc__rpc_dtablesize(), &readfds, (fd_set*)NULL, (fd_set*)NULL,
461
&tout)) {
462
case 0:
463
ct->ct_error.re_status = RPC_TIMEDOUT;
464
return (-1);
465
466
case -1:
467
if (errno == EINTR)
468
continue;
469
ct->ct_error.re_status = RPC_CANTRECV;
470
ct->ct_error.re_errno = errno;
471
return (-1);
472
}
473
break;
474
}
475
switch (len = read(ct->ct_sock, buf, (size_t) len)) {
476
477
case 0:
478
/* premature eof */
479
ct->ct_error.re_errno = ECONNRESET;
480
ct->ct_error.re_status = RPC_CANTRECV;
481
len = -1; /* it's really an error */
482
break;
483
484
case -1:
485
ct->ct_error.re_errno = errno;
486
ct->ct_error.re_status = RPC_CANTRECV;
487
break;
488
}
489
return (len);
490
}
491
492
static int
493
writetcp(
494
char *ctptr,
495
caddr_t buf,
496
int len)
497
{
498
struct ct_data *ct = (struct ct_data *)(void *)ctptr;
499
int i, cnt;
500
501
for (cnt = len; cnt > 0; cnt -= i, buf += i) {
502
if ((i = write(ct->ct_sock, buf, (size_t) cnt)) == -1) {
503
ct->ct_error.re_errno = errno;
504
ct->ct_error.re_status = RPC_CANTSEND;
505
return (-1);
506
}
507
}
508
return (len);
509
}
510
511