Libinfo-129.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 * return 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 static int readtcp();
97 static int writetcp();
98
99 static enum clnt_stat clnttcp_call();
100 static void clnttcp_abort();
101 static void clnttcp_geterr();
102 static bool_t clnttcp_freeres();
103 static bool_t clnttcp_control();
104 static void clnttcp_destroy();
105
106 static struct clnt_ops tcp_ops = {
107 clnttcp_call,
108 clnttcp_abort,
109 clnttcp_geterr,
110 clnttcp_freeres,
111 clnttcp_destroy,
112 clnttcp_control
113 };
114
115 struct ct_data {
116 int ct_sock;
117 bool_t ct_closeit;
118 struct timeval ct_wait;
119 bool_t ct_waitset; /* wait set by clnt_control? */
120 struct sockaddr_in ct_addr;
121 struct rpc_err ct_error;
122 char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */
123 u_int ct_mpos; /* pos after marshal */
124 XDR ct_xdrs;
125 };
126
127 /*
128 * Create a client handle for a tcp/ip connection.
129 * If *sockp<0, *sockp is set to a newly created TCP socket and it is
130 * connected to raddr. If *sockp non-negative then
131 * raddr is ignored. The rpc/tcp package does buffering
132 * similar to stdio, so the client must pick send and receive buffer sizes,];
133 * 0 => use the default.
134 * If raddr->sin_port is 0, then a binder on the remote machine is
135 * consulted for the right port number.
136 * NB: *sockp is copied into a private area.
137 * NB: It is the clients responsibility to close *sockp.
138 * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this
139 * something more useful.
140 */
141 CLIENT *
142 clnttcp_create(raddr, prog, vers, sockp, sendsz, recvsz)
143 struct sockaddr_in *raddr;
144 u_long prog;
145 u_long vers;
146 register int *sockp;
147 u_int sendsz;
148 u_int recvsz;
149 {
150 CLIENT *h;
151 register struct ct_data *ct = NULL;
152 struct timeval now;
153 struct rpc_msg call_msg;
154 int rfd;
155
156 h = (CLIENT *)mem_alloc(sizeof(*h));
157 if (h == NULL) {
158 (void)fprintf(stderr, "clnttcp_create: out of memory\n");
159 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
160 rpc_createerr.cf_error.re_errno = errno;
161 goto fooy;
162 }
163 ct = (struct ct_data *)mem_alloc(sizeof(*ct));
164 if (ct == NULL) {
165 (void)fprintf(stderr, "clnttcp_create: out of memory\n");
166 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
167 rpc_createerr.cf_error.re_errno = errno;
168 goto fooy;
169 }
170
171 /*
172 * If no port number given ask the pmap for one
173 */
174 if (raddr->sin_port == 0) {
175 u_short port;
176 if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) {
177 mem_free((caddr_t)ct, sizeof(struct ct_data));
178 mem_free((caddr_t)h, sizeof(CLIENT));
179 return ((CLIENT *)NULL);
180 }
181 raddr->sin_port = htons(port);
182 }
183
184 /*
185 * If no socket given, open one
186 */
187 if (*sockp < 0) {
188 *sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
189 (void)bindresvport(*sockp, (struct sockaddr_in *)0);
190 if ((*sockp < 0)
191 || (connect(*sockp, (struct sockaddr *)raddr,
192 sizeof(*raddr)) < 0)) {
193 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
194 rpc_createerr.cf_error.re_errno = errno;
195 (void)close(*sockp);
196 goto fooy;
197 }
198 ct->ct_closeit = TRUE;
199 } else {
200 ct->ct_closeit = FALSE;
201 }
202
203 /*
204 * Set up private data struct
205 */
206 ct->ct_sock = *sockp;
207 ct->ct_wait.tv_usec = 0;
208 ct->ct_waitset = FALSE;
209 ct->ct_addr = *raddr;
210
211 /*
212 * Initialize call message
213 */
214 rfd = open("/dev/random", O_RDONLY, 0);
215 if ((rfd < 0) || (read(rfd, &call_msg.rm_xid, sizeof(call_msg.rm_xid)) != sizeof(call_msg.rm_xid)))
216 {
217 gettimeofday(&now, (struct timezone *)0);
218 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
219 }
220 if (rfd > 0) close(rfd);
221
222 call_msg.rm_direction = CALL;
223 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
224 call_msg.rm_call.cb_prog = prog;
225 call_msg.rm_call.cb_vers = vers;
226
227 /*
228 * pre-serialize the staic part of the call msg and stash it away
229 */
230 xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
231 XDR_ENCODE);
232 if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
233 if (ct->ct_closeit) {
234 (void)close(*sockp);
235 }
236 goto fooy;
237 }
238 ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
239 XDR_DESTROY(&(ct->ct_xdrs));
240
241 /*
242 * Create a client handle which uses xdrrec for serialization
243 * and authnone for authentication.
244 */
245 xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
246 (caddr_t)ct, readtcp, writetcp);
247 h->cl_ops = &tcp_ops;
248 h->cl_private = (caddr_t) ct;
249 h->cl_auth = authnone_create();
250 return (h);
251
252 fooy:
253 /*
254 * Something goofed, free stuff and barf
255 */
256 mem_free((caddr_t)ct, sizeof(struct ct_data));
257 mem_free((caddr_t)h, sizeof(CLIENT));
258 return ((CLIENT *)NULL);
259 }
260
261 static enum clnt_stat
262 clnttcp_call(h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
263 register CLIENT *h;
264 u_long proc;
265 xdrproc_t xdr_args;
266 caddr_t args_ptr;
267 xdrproc_t xdr_results;
268 caddr_t results_ptr;
269 struct timeval timeout;
270 {
271 register struct ct_data *ct = (struct ct_data *) h->cl_private;
272 register XDR *xdrs = &(ct->ct_xdrs);
273 struct rpc_msg reply_msg;
274 u_long x_id;
275 u_long *msg_x_id = (u_long *)(ct->ct_mcall); /* yuk */
276 register bool_t shipnow;
277 int refreshes = 2;
278
279 if (!ct->ct_waitset) {
280 ct->ct_wait = timeout;
281 }
282
283 shipnow =
284 (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
285 && timeout.tv_usec == 0) ? FALSE : TRUE;
286
287 call_again:
288 xdrs->x_op = XDR_ENCODE;
289 ct->ct_error.re_status = RPC_SUCCESS;
290 x_id = ntohl(--(*msg_x_id));
291 if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
292 (! XDR_PUTLONG(xdrs, (long *)&proc)) ||
293 (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
294 (! (*xdr_args)(xdrs, args_ptr))) {
295 if (ct->ct_error.re_status == RPC_SUCCESS)
296 ct->ct_error.re_status = RPC_CANTENCODEARGS;
297 (void)xdrrec_endofrecord(xdrs, TRUE);
298 return (ct->ct_error.re_status);
299 }
300 if (! xdrrec_endofrecord(xdrs, shipnow))
301 return (ct->ct_error.re_status = RPC_CANTSEND);
302 if (! shipnow)
303 return (RPC_SUCCESS);
304 /*
305 * Hack to provide rpc-based message passing
306 */
307 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
308 return(ct->ct_error.re_status = RPC_TIMEDOUT);
309 }
310
311
312 /*
313 * Keep receiving until we get a valid transaction id
314 */
315 xdrs->x_op = XDR_DECODE;
316 while (TRUE) {
317 reply_msg.acpted_rply.ar_verf = _null_auth;
318 reply_msg.acpted_rply.ar_results.where = NULL;
319 reply_msg.acpted_rply.ar_results.proc = xdr_void;
320 if (! xdrrec_skiprecord(xdrs))
321 return (ct->ct_error.re_status);
322 /* now decode and validate the response header */
323 if (! xdr_replymsg(xdrs, &reply_msg)) {
324 if (ct->ct_error.re_status == RPC_SUCCESS)
325 continue;
326 return (ct->ct_error.re_status);
327 }
328 if (reply_msg.rm_xid == x_id)
329 break;
330 }
331
332 /*
333 * process header
334 */
335 _seterr_reply(&reply_msg, &(ct->ct_error));
336 if (ct->ct_error.re_status == RPC_SUCCESS) {
337 if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
338 ct->ct_error.re_status = RPC_AUTHERROR;
339 ct->ct_error.re_why = AUTH_INVALIDRESP;
340 } else if (! (*xdr_results)(xdrs, results_ptr)) {
341 if (ct->ct_error.re_status == RPC_SUCCESS)
342 ct->ct_error.re_status = RPC_CANTDECODERES;
343 }
344 /* free verifier ... */
345 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
346 xdrs->x_op = XDR_FREE;
347 (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
348 }
349 } /* end successful completion */
350 else {
351 /* maybe our credentials need to be refreshed ... */
352 if (refreshes-- && AUTH_REFRESH(h->cl_auth))
353 goto call_again;
354 } /* end of unsuccessful completion */
355 return (ct->ct_error.re_status);
356 }
357
358 static void
359 clnttcp_geterr(h, errp)
360 CLIENT *h;
361 struct rpc_err *errp;
362 {
363 register struct ct_data *ct =
364 (struct ct_data *) h->cl_private;
365
366 *errp = ct->ct_error;
367 }
368
369 static bool_t
370 clnttcp_freeres(cl, xdr_res, res_ptr)
371 CLIENT *cl;
372 xdrproc_t xdr_res;
373 caddr_t res_ptr;
374 {
375 register struct ct_data *ct = (struct ct_data *)cl->cl_private;
376 register XDR *xdrs = &(ct->ct_xdrs);
377
378 xdrs->x_op = XDR_FREE;
379 return ((*xdr_res)(xdrs, res_ptr));
380 }
381
382 static void
383 clnttcp_abort()
384 {
385 }
386
387 static bool_t
388 clnttcp_control(cl, request, info)
389 CLIENT *cl;
390 int request;
391 char *info;
392 {
393 register struct ct_data *ct = (struct ct_data *)cl->cl_private;
394
395 switch (request) {
396 case CLSET_TIMEOUT:
397 ct->ct_wait = *(struct timeval *)info;
398 ct->ct_waitset = TRUE;
399 break;
400 case CLGET_TIMEOUT:
401 *(struct timeval *)info = ct->ct_wait;
402 break;
403 case CLGET_SERVER_ADDR:
404 *(struct sockaddr_in *)info = ct->ct_addr;
405 break;
406 default:
407 return (FALSE);
408 }
409 return (TRUE);
410 }
411
412
413 static void
414 clnttcp_destroy(h)
415 CLIENT *h;
416 {
417 register struct ct_data *ct =
418 (struct ct_data *) h->cl_private;
419
420 if (ct->ct_closeit) {
421 (void)close(ct->ct_sock);
422 }
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(ct, buf, len)
435 register struct ct_data *ct;
436 caddr_t buf;
437 register int len;
438 {
439 fd_set mask;
440 fd_set readfds;
441
442 if (len == 0)
443 return (0);
444 FD_ZERO(&mask);
445 FD_SET(ct->ct_sock, &mask);
446 while (TRUE) {
447 readfds = mask;
448 switch (select(ct->ct_sock+1, &readfds, NULL, NULL,
449 &(ct->ct_wait))) {
450 case 0:
451 ct->ct_error.re_status = RPC_TIMEDOUT;
452 return (-1);
453
454 case -1:
455 if (errno == EINTR)
456 continue;
457 ct->ct_error.re_status = RPC_CANTRECV;
458 ct->ct_error.re_errno = errno;
459 return (-1);
460 }
461 break;
462 }
463 switch (len = read(ct->ct_sock, buf, len)) {
464
465 case 0:
466 /* premature eof */
467 ct->ct_error.re_errno = ECONNRESET;
468 ct->ct_error.re_status = RPC_CANTRECV;
469 len = -1; /* it's really an error */
470 break;
471
472 case -1:
473 ct->ct_error.re_errno = errno;
474 ct->ct_error.re_status = RPC_CANTRECV;
475 break;
476 }
477 return (len);
478 }
479
480 static int
481 writetcp(ct, buf, len)
482 struct ct_data *ct;
483 caddr_t buf;
484 int len;
485 {
486 register int i, cnt;
487
488 for (cnt = len; cnt > 0; cnt -= i, buf += i) {
489 if ((i = write(ct->ct_sock, buf, cnt)) == -1) {
490 ct->ct_error.re_errno = errno;
491 ct->ct_error.re_status = RPC_CANTSEND;
492 return (-1);
493 }
494 }
495 return (len);
496 }