Libinfo-324.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 __private_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 u_long proc;
292 xdrproc_t xdr_args;
293 caddr_t args_ptr;
294 xdrproc_t xdr_results;
295 caddr_t results_ptr;
296 struct timeval timeout;
297 {
298 register struct ct_data *ct = (struct ct_data *) h->cl_private;
299 register XDR *xdrs = &(ct->ct_xdrs);
300 struct rpc_msg reply_msg;
301 u_long x_id;
302 u_long *msg_x_id = (u_long *)(ct->ct_mcall); /* yuk */
303 register bool_t shipnow;
304 int refreshes = 2;
305
306 if (!ct->ct_timeout_set) {
307 ct->ct_timeout = timeout;
308 }
309
310 shipnow =
311 (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
312 && timeout.tv_usec == 0) ? FALSE : TRUE;
313
314 call_again:
315 xdrs->x_op = XDR_ENCODE;
316 ct->ct_error.re_status = RPC_SUCCESS;
317 x_id = ntohl(--(*msg_x_id));
318 #ifdef __LP64__
319 if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
320 (! XDR_PUTLONG(xdrs, (int *)&proc)) ||
321 (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
322 (! (*xdr_args)(xdrs, args_ptr)))
323 #else
324 if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
325 (! XDR_PUTLONG(xdrs, (long *)&proc)) ||
326 (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
327 (! (*xdr_args)(xdrs, args_ptr)))
328 #endif
329 {
330 if (ct->ct_error.re_status == RPC_SUCCESS)
331 ct->ct_error.re_status = RPC_CANTENCODEARGS;
332 (void)xdrrec_endofrecord(xdrs, TRUE);
333 return (ct->ct_error.re_status);
334 }
335 if (! xdrrec_endofrecord(xdrs, shipnow))
336 return (ct->ct_error.re_status = RPC_CANTSEND);
337 if (! shipnow)
338 return (RPC_SUCCESS);
339 /*
340 * Hack to provide rpc-based message passing
341 */
342 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
343 return(ct->ct_error.re_status = RPC_TIMEDOUT);
344 }
345
346
347 /*
348 * Keep receiving until we get a valid transaction id
349 */
350 xdrs->x_op = XDR_DECODE;
351 while (TRUE) {
352 reply_msg.acpted_rply.ar_verf = _null_auth;
353 reply_msg.acpted_rply.ar_results.where = NULL;
354 reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
355 if (! xdrrec_skiprecord(xdrs))
356 return (ct->ct_error.re_status);
357 /* now decode and validate the response header */
358 if (! xdr_replymsg(xdrs, &reply_msg)) {
359 if (ct->ct_error.re_status == RPC_SUCCESS)
360 continue;
361 return (ct->ct_error.re_status);
362 }
363 if (reply_msg.rm_xid == x_id)
364 break;
365 }
366
367 /*
368 * process header
369 */
370 _seterr_reply(&reply_msg, &(ct->ct_error));
371 if (ct->ct_error.re_status == RPC_SUCCESS) {
372 if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
373 ct->ct_error.re_status = RPC_AUTHERROR;
374 ct->ct_error.re_why = AUTH_INVALIDRESP;
375 } else if (! (*xdr_results)(xdrs, results_ptr)) {
376 if (ct->ct_error.re_status == RPC_SUCCESS)
377 ct->ct_error.re_status = RPC_CANTDECODERES;
378 }
379 /* free verifier ... */
380 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
381 xdrs->x_op = XDR_FREE;
382 (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
383 }
384 } /* end successful completion */
385 else {
386 /* maybe our credentials need to be refreshed ... */
387 if (refreshes-- && AUTH_REFRESH(h->cl_auth))
388 goto call_again;
389 } /* end of unsuccessful completion */
390 return (ct->ct_error.re_status);
391 }
392
393 static void
394 clnttcp_geterr(h, errp)
395 CLIENT *h;
396 struct rpc_err *errp;
397 {
398 register struct ct_data *ct =
399 (struct ct_data *) h->cl_private;
400
401 *errp = ct->ct_error;
402 }
403
404 static bool_t
405 clnttcp_freeres(cl, xdr_res, res_ptr)
406 CLIENT *cl;
407 xdrproc_t xdr_res;
408 caddr_t res_ptr;
409 {
410 register struct ct_data *ct = (struct ct_data *)cl->cl_private;
411 register XDR *xdrs = &(ct->ct_xdrs);
412
413 xdrs->x_op = XDR_FREE;
414 return ((*xdr_res)(xdrs, res_ptr));
415 }
416
417 static void
418 clnttcp_abort()
419 {
420 }
421
422 static bool_t
423 clnttcp_control(cl, request, info)
424 CLIENT *cl;
425 int request;
426 char *info;
427 {
428 register struct ct_data *ct = (struct ct_data *)cl->cl_private;
429
430 switch (request) {
431 case CLSET_TIMEOUT:
432 ct->ct_timeout = *(struct timeval *)info;
433 ct->ct_timeout_set = TRUE;
434 break;
435 case CLGET_TIMEOUT:
436 *(struct timeval *)info = ct->ct_timeout;
437 break;
438 case CLGET_SERVER_ADDR:
439 *(struct sockaddr_in *)info = ct->ct_addr;
440 break;
441 default:
442 return (FALSE);
443 }
444 return (TRUE);
445 }
446
447
448 static void
449 clnttcp_destroy(h)
450 CLIENT *h;
451 {
452 register struct ct_data *ct =
453 (struct ct_data *) h->cl_private;
454
455 if (ct->ct_closeit) {
456 (void)close(ct->ct_sock);
457 }
458 XDR_DESTROY(&(ct->ct_xdrs));
459 mem_free((caddr_t)ct, sizeof(struct ct_data));
460 mem_free((caddr_t)h, sizeof(CLIENT));
461 }
462
463 /*
464 * Interface between xdr serializer and tcp connection.
465 * Behaves like the system calls, read & write, but keeps some error state
466 * around for the rpc level.
467 */
468 static int
469 readtcp(ct, buf, len)
470 register struct ct_data *ct;
471 caddr_t buf;
472 register int len;
473 {
474 fd_set mask;
475 fd_set readfds;
476
477 if (len == 0)
478 return (0);
479 FD_ZERO(&mask);
480 FD_SET(ct->ct_sock, &mask);
481 while (TRUE) {
482 readfds = mask;
483 switch (select(ct->ct_sock+1, &readfds, NULL, NULL, &(ct->ct_timeout))) {
484 case 0:
485 ct->ct_error.re_status = RPC_TIMEDOUT;
486 return (-1);
487
488 case -1:
489 if (errno == EINTR)
490 continue;
491 ct->ct_error.re_status = RPC_CANTRECV;
492 ct->ct_error.re_errno = errno;
493 return (-1);
494 }
495 break;
496 }
497 switch (len = read(ct->ct_sock, buf, len)) {
498
499 case 0:
500 /* premature eof */
501 ct->ct_error.re_errno = ECONNRESET;
502 ct->ct_error.re_status = RPC_CANTRECV;
503 len = -1; /* it's really an error */
504 break;
505
506 case -1:
507 ct->ct_error.re_errno = errno;
508 ct->ct_error.re_status = RPC_CANTRECV;
509 break;
510 }
511 return (len);
512 }
513
514 static int
515 writetcp(ct, buf, len)
516 struct ct_data *ct;
517 caddr_t buf;
518 int len;
519 {
520 register int i, cnt;
521
522 for (cnt = len; cnt > 0; cnt -= i, buf += i) {
523 if ((i = write(ct->ct_sock, buf, cnt)) == -1) {
524 ct->ct_error.re_errno = errno;
525 ct->ct_error.re_status = RPC_CANTSEND;
526 return (-1);
527 }
528 }
529 return (len);
530 }