]> git.saurik.com Git - apple/libinfo.git/blob - rpc.subproj/clnt_tcp.c
Libinfo-517.30.1.tar.gz
[apple/libinfo.git] / rpc.subproj / clnt_tcp.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.1 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24 /*
25 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
26 * unrestricted use provided that this legend is included on all tape
27 * media and as a part of the software program in whole or part. Users
28 * may copy or modify Sun RPC without charge, but are not authorized
29 * to license or distribute it to anyone else except as part of a product or
30 * program developed by the user.
31 *
32 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
33 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
34 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
35 *
36 * Sun RPC is provided with no support and without any obligation on the
37 * part of Sun Microsystems, Inc. to assist in its use, correction,
38 * modification or enhancement.
39 *
40 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
41 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
42 * OR ANY PART THEREOF.
43 *
44 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
45 * or profits or other special, indirect and consequential damages, even if
46 * Sun has been advised of the possibility of such damages.
47 *
48 * Sun Microsystems, Inc.
49 * 2550 Garcia Avenue
50 * Mountain View, California 94043
51 */
52
53 #if defined(LIBC_SCCS) && !defined(lint)
54 /*static char *sccsid = "from: @(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";*/
55 /*static char *sccsid = "from: @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC";*/
56 static char *rcsid = "$Id: clnt_tcp.c,v 1.4 2002/03/15 22:07:48 majka Exp $";
57 #endif
58
59 /*
60 * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
61 *
62 * Copyright (C) 1984, Sun Microsystems, Inc.
63 *
64 * TCP based RPC supports 'batched calls'.
65 * A sequence of calls may be batched-up in a send buffer. The rpc call
66 * returns immediately to the client even though the call was not necessarily
67 * sent. The batching occurs if the results' xdr routine is NULL (0) AND
68 * the rpc timeout value is zero (see clnt.h, rpc).
69 *
70 * Clients should NOT casually batch calls that in fact return results; that is,
71 * the server side should be aware that a call is batched and not produce any
72 * return message. Batched calls that produce many result messages can
73 * deadlock (netlock) the client and the server....
74 *
75 * Now go hang yourself.
76 */
77
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81 #include <unistd.h>
82 #include <rpc/rpc.h>
83 #include <sys/socket.h>
84 #include <sys/fcntl.h>
85 #include <netdb.h>
86 #include <errno.h>
87 #include <rpc/pmap_clnt.h>
88
89 #define MCALL_MSG_SIZE 24
90
91 extern int errno;
92
93 extern int bindresvport();
94 extern bool_t xdr_opaque_auth();
95
96 extern u_short pmap_getport_timeout(struct sockaddr_in *address, uint32_t program, uint32_t version, uint32_t protocol, struct timeval *timeout, struct timeval *totaltimeout);
97
98 static int readtcp();
99 static int writetcp();
100
101 static enum clnt_stat clnttcp_call();
102 static void clnttcp_abort();
103 static void clnttcp_geterr();
104 static bool_t clnttcp_freeres();
105 static bool_t clnttcp_control();
106 static void clnttcp_destroy();
107
108 static struct clnt_ops tcp_ops = {
109 clnttcp_call,
110 clnttcp_abort,
111 clnttcp_geterr,
112 clnttcp_freeres,
113 clnttcp_destroy,
114 clnttcp_control
115 };
116
117 struct ct_data {
118 int ct_sock;
119 bool_t ct_closeit;
120 struct timeval ct_timeout;
121 bool_t ct_timeout_set; /* timeout set by clnt_control? */
122 struct sockaddr_in ct_addr;
123 struct rpc_err ct_error;
124 char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */
125 u_int ct_mpos; /* pos after marshal */
126 XDR ct_xdrs;
127 };
128
129 /*
130 * Create a client handle for a tcp/ip connection.
131 * If *sockp<0, *sockp is set to a newly created TCP socket and it is
132 * connected to raddr. If *sockp non-negative then
133 * raddr is ignored. The rpc/tcp package does buffering
134 * similar to stdio, so the client must pick send and receive buffer sizes,];
135 * 0 => use the default.
136 * If raddr->sin_port is 0, then a binder on the remote machine is
137 * consulted for the right port number.
138 * NB: *sockp is copied into a private area.
139 * NB: It is the clients responsibility to close *sockp.
140 * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this
141 * something more useful.
142 */
143 CLIENT *
144 clnttcp_create_timeout(struct sockaddr_in *raddr, uint32_t prog, uint32_t vers, int *sockp, uint32_t sendsz, uint32_t recvsz, struct timeval *retry_timeout, struct timeval *total_timeout)
145 {
146 CLIENT *h;
147 register struct ct_data *ct = NULL;
148 struct timeval now;
149 struct rpc_msg call_msg;
150 int rfd;
151 u_short port;
152
153 h = (CLIENT *)mem_alloc(sizeof(*h));
154 if (h == NULL)
155 {
156 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
157 rpc_createerr.cf_error.re_errno = errno;
158 goto fooy;
159 }
160
161 ct = (struct ct_data *)mem_alloc(sizeof(*ct));
162 if (ct == NULL)
163 {
164 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
165 rpc_createerr.cf_error.re_errno = errno;
166 goto fooy;
167 }
168
169 /*
170 * If no port number given ask the pmap for one
171 */
172 if (raddr->sin_port == 0)
173 {
174 port = pmap_getport_timeout(raddr, prog, vers, IPPROTO_TCP, retry_timeout, total_timeout);
175 if (port == 0)
176 {
177 mem_free((caddr_t)ct, sizeof(struct ct_data));
178 mem_free((caddr_t)h, sizeof(CLIENT));
179 return NULL;
180 }
181
182 raddr->sin_port = htons(port);
183 }
184
185 /*
186 * If no socket given, open one
187 */
188 if (*sockp < 0)
189 {
190 *sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
191 bindresvport(*sockp, (struct sockaddr_in *)0);
192 if ((*sockp < 0) || (connect(*sockp, (struct sockaddr *)raddr, sizeof(*raddr)) < 0))
193 {
194 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
195 rpc_createerr.cf_error.re_errno = errno;
196 close(*sockp);
197 goto fooy;
198 }
199
200 ct->ct_closeit = TRUE;
201 }
202 else
203 {
204 ct->ct_closeit = FALSE;
205 }
206
207 /*
208 * Set up private data struct
209 */
210 ct->ct_sock = *sockp;
211 ct->ct_timeout.tv_sec = 60;
212 ct->ct_timeout.tv_usec = 0;
213 ct->ct_timeout_set = FALSE;
214 if (total_timeout != NULL)
215 {
216 ct->ct_timeout = *total_timeout;
217 ct->ct_timeout_set = TRUE;
218 }
219 ct->ct_addr = *raddr;
220
221 /*
222 * Initialize call message
223 */
224 rfd = open("/dev/random", O_RDONLY, 0);
225 if ((rfd < 0) || (read(rfd, &call_msg.rm_xid, sizeof(call_msg.rm_xid)) != sizeof(call_msg.rm_xid)))
226 {
227 gettimeofday(&now, (struct timezone *)0);
228 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
229 }
230
231 if (rfd > 0) close(rfd);
232
233 call_msg.rm_direction = CALL;
234 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
235 call_msg.rm_call.cb_prog = prog;
236 call_msg.rm_call.cb_vers = vers;
237
238 /*
239 * pre-serialize the staic part of the call msg and stash it away
240 */
241 xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE, XDR_ENCODE);
242 if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg))
243 {
244 if (ct->ct_closeit) close(*sockp);
245 goto fooy;
246 }
247
248 ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
249 XDR_DESTROY(&(ct->ct_xdrs));
250
251 /*
252 * Create a client handle which uses xdrrec for serialization
253 * and authnone for authentication.
254 */
255 xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz, (caddr_t)ct, readtcp, writetcp);
256 h->cl_ops = &tcp_ops;
257 h->cl_private = (caddr_t)ct;
258 h->cl_auth = authnone_create();
259 return h;
260
261 fooy:
262 mem_free((caddr_t)ct, sizeof(struct ct_data));
263 mem_free((caddr_t)h, sizeof(CLIENT));
264 return NULL;
265 }
266
267 CLIENT *
268 clnttcp_create(raddr, prog, vers, sockp, sendsz, recvsz)
269 #ifdef __LP64__
270 struct sockaddr_in *raddr;
271 uint32_t prog;
272 uint32_t vers;
273 int *sockp;
274 uint32_t sendsz;
275 uint32_t recvsz;
276 #else
277 struct sockaddr_in *raddr;
278 u_long prog;
279 u_long vers;
280 register int *sockp;
281 u_int sendsz;
282 u_int recvsz;
283 #endif
284 {
285 return clnttcp_create_timeout(raddr, (uint32_t)prog, (uint32_t)vers, sockp, (uint32_t)sendsz, (uint32_t)recvsz, NULL, NULL);
286 }
287
288 static enum clnt_stat
289 clnttcp_call(h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
290 register CLIENT *h;
291 #ifdef __LP64__
292 uint32_t proc;
293 #else
294 u_long proc;
295 #endif
296 xdrproc_t xdr_args;
297 caddr_t args_ptr;
298 xdrproc_t xdr_results;
299 caddr_t results_ptr;
300 struct timeval timeout;
301 {
302 register struct ct_data *ct = (struct ct_data *) h->cl_private;
303 register XDR *xdrs = &(ct->ct_xdrs);
304 struct rpc_msg reply_msg;
305 #ifdef __LP64__
306 uint32_t x_id;
307 uint32_t *msg_x_id = (uint32_t *)(ct->ct_mcall); /* yuk */
308 #else
309 u_long x_id;
310 u_long *msg_x_id = (u_long *)(ct->ct_mcall); /* yuk */
311 #endif
312 register bool_t shipnow;
313 int refreshes = 2;
314
315 if (!ct->ct_timeout_set) {
316 ct->ct_timeout = timeout;
317 }
318
319 shipnow =
320 (xdr_results == (xdrproc_t)NULLPROC || (timeout.tv_sec == 0
321 && timeout.tv_usec == 0)) ? FALSE : TRUE;
322
323 call_again:
324 xdrs->x_op = XDR_ENCODE;
325 ct->ct_error.re_status = RPC_SUCCESS;
326 x_id = ntohl(--(*msg_x_id));
327 #ifdef __LP64__
328 if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
329 (! XDR_PUTLONG(xdrs, (int *)&proc)) ||
330 (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
331 (! (*xdr_args)(xdrs, args_ptr, 0)))
332 #else
333 if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
334 (! XDR_PUTLONG(xdrs, (long *)&proc)) ||
335 (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
336 (! (*xdr_args)(xdrs, args_ptr, 0)))
337 #endif
338 {
339 if (ct->ct_error.re_status == RPC_SUCCESS)
340 ct->ct_error.re_status = RPC_CANTENCODEARGS;
341 (void)xdrrec_endofrecord(xdrs, TRUE);
342 return (ct->ct_error.re_status);
343 }
344 if (! xdrrec_endofrecord(xdrs, shipnow))
345 return (ct->ct_error.re_status = RPC_CANTSEND);
346 if (! shipnow)
347 return (RPC_SUCCESS);
348 /*
349 * Hack to provide rpc-based message passing
350 */
351 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
352 return(ct->ct_error.re_status = RPC_TIMEDOUT);
353 }
354
355
356 /*
357 * Keep receiving until we get a valid transaction id
358 */
359 xdrs->x_op = XDR_DECODE;
360 while (TRUE) {
361 reply_msg.acpted_rply.ar_verf = _null_auth;
362 reply_msg.acpted_rply.ar_results.where = NULL;
363 reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
364 if (! xdrrec_skiprecord(xdrs))
365 return (ct->ct_error.re_status);
366 /* now decode and validate the response header */
367 if (! xdr_replymsg(xdrs, &reply_msg)) {
368 if (ct->ct_error.re_status == RPC_SUCCESS)
369 continue;
370 return (ct->ct_error.re_status);
371 }
372 if (reply_msg.rm_xid == x_id)
373 break;
374 }
375
376 /*
377 * process header
378 */
379 _seterr_reply(&reply_msg, &(ct->ct_error));
380 if (ct->ct_error.re_status == RPC_SUCCESS) {
381 if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
382 ct->ct_error.re_status = RPC_AUTHERROR;
383 ct->ct_error.re_why = AUTH_INVALIDRESP;
384 } else if (! (*xdr_results)(xdrs, results_ptr, 0)) {
385 if (ct->ct_error.re_status == RPC_SUCCESS)
386 ct->ct_error.re_status = RPC_CANTDECODERES;
387 }
388 /* free verifier ... */
389 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
390 xdrs->x_op = XDR_FREE;
391 (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
392 }
393 } /* end successful completion */
394 else {
395 /* maybe our credentials need to be refreshed ... */
396 if (refreshes-- && AUTH_REFRESH(h->cl_auth))
397 goto call_again;
398 } /* end of unsuccessful completion */
399 return (ct->ct_error.re_status);
400 }
401
402 static void
403 clnttcp_geterr(h, errp)
404 CLIENT *h;
405 struct rpc_err *errp;
406 {
407 register struct ct_data *ct =
408 (struct ct_data *) h->cl_private;
409
410 *errp = ct->ct_error;
411 }
412
413 static bool_t
414 clnttcp_freeres(cl, xdr_res, res_ptr)
415 CLIENT *cl;
416 xdrproc_t xdr_res;
417 caddr_t res_ptr;
418 {
419 register struct ct_data *ct = (struct ct_data *)cl->cl_private;
420 register XDR *xdrs = &(ct->ct_xdrs);
421
422 xdrs->x_op = XDR_FREE;
423 return ((*xdr_res)(xdrs, res_ptr, 0));
424 }
425
426 static void
427 clnttcp_abort()
428 {
429 }
430
431 static bool_t
432 clnttcp_control(cl, request, info)
433 CLIENT *cl;
434 int request;
435 char *info;
436 {
437 register struct ct_data *ct = (struct ct_data *)cl->cl_private;
438
439 switch (request) {
440 case CLSET_TIMEOUT:
441 ct->ct_timeout = *(struct timeval *)info;
442 ct->ct_timeout_set = TRUE;
443 break;
444 case CLGET_TIMEOUT:
445 *(struct timeval *)info = ct->ct_timeout;
446 break;
447 case CLGET_SERVER_ADDR:
448 *(struct sockaddr_in *)info = ct->ct_addr;
449 break;
450 default:
451 return (FALSE);
452 }
453 return (TRUE);
454 }
455
456
457 static void
458 clnttcp_destroy(h)
459 CLIENT *h;
460 {
461 register struct ct_data *ct =
462 (struct ct_data *) h->cl_private;
463
464 if (ct->ct_closeit) {
465 (void)close(ct->ct_sock);
466 }
467 XDR_DESTROY(&(ct->ct_xdrs));
468 mem_free((caddr_t)ct, sizeof(struct ct_data));
469 mem_free((caddr_t)h, sizeof(CLIENT));
470 }
471
472 /*
473 * Interface between xdr serializer and tcp connection.
474 * Behaves like the system calls, read & write, but keeps some error state
475 * around for the rpc level.
476 */
477 static int
478 readtcp(ct, buf, len)
479 register struct ct_data *ct;
480 caddr_t buf;
481 register int len;
482 {
483 fd_set mask;
484 fd_set readfds;
485
486 if (len == 0)
487 return (0);
488 FD_ZERO(&mask);
489 FD_SET(ct->ct_sock, &mask);
490 while (TRUE) {
491 readfds = mask;
492 switch (select(ct->ct_sock+1, &readfds, NULL, NULL, &(ct->ct_timeout))) {
493 case 0:
494 ct->ct_error.re_status = RPC_TIMEDOUT;
495 return (-1);
496
497 case -1:
498 if (errno == EINTR)
499 continue;
500 ct->ct_error.re_status = RPC_CANTRECV;
501 ct->ct_error.re_errno = errno;
502 return (-1);
503 }
504 break;
505 }
506 switch (len = read(ct->ct_sock, buf, len)) {
507
508 case 0:
509 /* premature eof */
510 ct->ct_error.re_errno = ECONNRESET;
511 ct->ct_error.re_status = RPC_CANTRECV;
512 len = -1; /* it's really an error */
513 break;
514
515 case -1:
516 ct->ct_error.re_errno = errno;
517 ct->ct_error.re_status = RPC_CANTRECV;
518 break;
519 }
520 return (len);
521 }
522
523 static int
524 writetcp(ct, buf, len)
525 struct ct_data *ct;
526 caddr_t buf;
527 int len;
528 {
529 register int i, cnt;
530
531 for (cnt = len; cnt > 0; cnt -= i, buf += i) {
532 if ((i = write(ct->ct_sock, buf, cnt)) == -1) {
533 ct->ct_error.re_errno = errno;
534 ct->ct_error.re_status = RPC_CANTSEND;
535 return (-1);
536 }
537 }
538 return (len);
539 }