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 *
clnttcp_create(struct sockaddr_in * raddr,rpcprog_t prog,rpcvers_t vers,SOCKET * sockp,u_int sendsz,u_int recvsz)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
clnttcp_call(CLIENT * h,rpcproc_t proc,xdrproc_t xdr_args,void * args_ptr,xdrproc_t xdr_results,void * results_ptr,struct timeval timeout)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
clnttcp_geterr(CLIENT * h,struct rpc_err * errp)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
clnttcp_freeres(CLIENT * cl,xdrproc_t xdr_res,void * res_ptr)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
clnttcp_abort(CLIENT * cl)379 clnttcp_abort(CLIENT *cl)
380 {
381 }
382
383 static bool_t
clnttcp_control(CLIENT * cl,int request,void * info)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
clnttcp_destroy(CLIENT * h)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
readtcp(char * ctptr,caddr_t buf,int len)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
writetcp(char * ctptr,caddr_t buf,int len)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