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