]> git.saurik.com Git - apple/libinfo.git/blob - rpc.subproj/clnt_udp.c
Libinfo-278.tar.gz
[apple/libinfo.git] / rpc.subproj / clnt_udp.c
1 /*
2 * Copyright (c) 1999-2007 Apple 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_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";*/
55 /*static char *sccsid = "from: @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC";*/
56 static char *rcsid = "$Id: clnt_udp.c,v 1.4 2002/03/15 22:07:49 majka Exp $";
57 #endif
58
59 /*
60 * clnt_udp.c, Implements a UDP/IP based, client side RPC.
61 *
62 * Copyright (C) 1984, Sun Microsystems, Inc.
63 */
64
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <unistd.h>
69 #include <rpc/rpc.h>
70 #include <sys/socket.h>
71 #include <sys/fcntl.h>
72 #include <sys/ioctl.h>
73 #include <netdb.h>
74 #include <errno.h>
75 #include <rpc/pmap_clnt.h>
76
77 extern int bindresvport();
78 extern bool_t xdr_opaque_auth();
79
80 __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);
81
82 extern int errno;
83
84 /*
85 * UDP bases client side rpc operations
86 */
87 static enum clnt_stat clntudp_call();
88 static void clntudp_abort();
89 static void clntudp_geterr();
90 static bool_t clntudp_freeres();
91 static bool_t clntudp_control();
92 static void clntudp_destroy();
93
94 static struct clnt_ops udp_ops = {
95 clntudp_call,
96 clntudp_abort,
97 clntudp_geterr,
98 clntudp_freeres,
99 clntudp_destroy,
100 clntudp_control
101 };
102
103 /*
104 * Private data kept per client handle
105 */
106 struct cu_data {
107 int cu_sock;
108 bool_t cu_closeit;
109 struct sockaddr_in cu_raddr;
110 int cu_rlen;
111 struct timeval cu_retry_timeout;
112 struct timeval cu_total_timeout;
113 struct rpc_err cu_error;
114 XDR cu_outxdrs;
115 u_int cu_xdrpos;
116 u_int cu_sendsz;
117 char *cu_outbuf;
118 u_int cu_recvsz;
119 char cu_inbuf[1];
120 };
121
122 /*
123 * Create a UDP based client handle.
124 * If *sockp<0, *sockp is set to a newly created UPD socket.
125 * If raddr->sin_port is 0 a binder on the remote machine
126 * is consulted for the correct port number.
127 * NB: It is the clients responsibility to close *sockp.
128 * NB: The rpch->cl_auth is initialized to null authentication.
129 * Caller may wish to set this something more useful.
130 *
131 * wait is the amount of time used between retransmitting a call if
132 * no response has been heard; retransmition occurs until the actual
133 * rpc call times out.
134 *
135 * sendsz and recvsz are the maximum allowable packet sizes that can be
136 * sent and received.
137 */
138 __private_extern__ CLIENT *
139 clntudp_bufcreate_timeout(struct sockaddr_in *raddr, uint32_t program, uint32_t version, int *sockp, uint32_t sendsz, uint32_t recvsz, struct timeval *retry_timeout, struct timeval *total_timeout)
140 {
141 CLIENT *cl;
142 struct cu_data *cu = NULL;
143 struct timeval now;
144 struct rpc_msg call_msg;
145 int rfd;
146 u_short port;
147 socklen_t len;
148 unsigned int rsize;
149
150 cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
151 if (cl == NULL)
152 {
153 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
154 rpc_createerr.cf_error.re_errno = errno;
155 goto fooy;
156 }
157
158 sendsz = ((sendsz + 3) / 4) * 4;
159 recvsz = ((recvsz + 3) / 4) * 4;
160 cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
161 if (cu == NULL)
162 {
163 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
164 rpc_createerr.cf_error.re_errno = errno;
165 goto fooy;
166 }
167
168 cu->cu_outbuf = &cu->cu_inbuf[recvsz];
169
170 if (raddr->sin_port == 0)
171 {
172 port = pmap_getport_timeout(raddr, program, version, IPPROTO_UDP, retry_timeout, total_timeout);
173 if (port == 0) goto fooy;
174
175 raddr->sin_port = htons(port);
176 }
177
178 cl->cl_ops = &udp_ops;
179 cl->cl_private = (caddr_t)cu;
180 cu->cu_raddr = *raddr;
181 cu->cu_rlen = sizeof (cu->cu_raddr);
182 cu->cu_sendsz = sendsz;
183 cu->cu_recvsz = recvsz;
184
185 cu->cu_retry_timeout.tv_sec = 1;
186 cu->cu_retry_timeout.tv_usec = 0;
187 if (retry_timeout != NULL) cu->cu_retry_timeout = *retry_timeout;
188
189 cu->cu_total_timeout.tv_sec = -1;
190 cu->cu_total_timeout.tv_usec = -1;
191 if (total_timeout != NULL) cu->cu_total_timeout = *retry_timeout;
192
193 rfd = open("/dev/random", O_RDONLY, 0);
194 if ((rfd < 0) || (read(rfd, &call_msg.rm_xid, sizeof(call_msg.rm_xid)) != sizeof(call_msg.rm_xid)))
195 {
196 gettimeofday(&now, (struct timezone *)0);
197 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
198 }
199
200 if (rfd > 0) close(rfd);
201
202 call_msg.rm_direction = CALL;
203 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
204 call_msg.rm_call.cb_prog = program;
205 call_msg.rm_call.cb_vers = version;
206 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
207 if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) goto fooy;
208
209 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
210 if (*sockp < 0)
211 {
212 int dontblock = 1;
213
214 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
215 if (*sockp < 0)
216 {
217 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
218 rpc_createerr.cf_error.re_errno = errno;
219 goto fooy;
220 }
221
222 /* attempt to bind to prov port */
223 (void)bindresvport(*sockp, (struct sockaddr_in *)0);
224
225 /* the socket's rpc controls are non-blocking */
226 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock);
227 cu->cu_closeit = TRUE;
228
229 /* set receive size */
230 rsize = 0;
231 len = sizeof(rsize);
232 if (getsockopt(*sockp, SOL_SOCKET, SO_RCVBUF, (char *)&rsize, &len) != 0)
233 {
234 close(*sockp);
235 *sockp = -1;
236 goto fooy;
237 }
238
239 len = sizeof(recvsz);
240 if ((recvsz > rsize) && (setsockopt(*sockp, SOL_SOCKET, SO_RCVBUF, (char *)&recvsz, len)))
241 {
242 close(*sockp);
243 *sockp = -1;
244 goto fooy;
245 }
246 }
247 else
248 {
249 cu->cu_closeit = FALSE;
250 }
251
252 cu->cu_sock = *sockp;
253 cl->cl_auth = authnone_create();
254 return (cl);
255
256 fooy:
257 if (cu) mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);
258 if (cl) mem_free((caddr_t)cl, sizeof(CLIENT));
259 return NULL;
260 }
261
262 CLIENT *
263 clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
264 #ifdef __LP64__
265 struct sockaddr_in *raddr;
266 uint32_t program;
267 uint32_t version;
268 struct timeval wait;
269 int *sockp;
270 uint32_t sendsz;
271 uint32_t recvsz;
272 #else
273 struct sockaddr_in *raddr;
274 u_long program;
275 u_long version;
276 struct timeval wait;
277 register int *sockp;
278 u_int sendsz;
279 u_int recvsz;
280 #endif
281 {
282 return clntudp_bufcreate_timeout(raddr, (uint32_t)program, (uint32_t)version, sockp, (uint32_t)sendsz, (uint32_t)recvsz, &wait, NULL);
283 }
284
285 CLIENT *
286 clntudp_create(raddr, program, version, wait, sockp)
287 #ifdef __LP64__
288 struct sockaddr_in *raddr;
289 uint32_t program;
290 uint32_t version;
291 struct timeval wait;
292 int *sockp;
293 #else
294 struct sockaddr_in *raddr;
295 u_long program;
296 u_long version;
297 struct timeval wait;
298 register int *sockp;
299 #endif
300 {
301
302 return clntudp_bufcreate(raddr, program, version, wait, sockp, UDPMSGSIZE, UDPMSGSIZE);
303 }
304
305 static enum clnt_stat
306 clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
307 register CLIENT *cl; /* client handle */
308 u_long proc; /* procedure number */
309 xdrproc_t xargs; /* xdr routine for args */
310 caddr_t argsp; /* pointer to args */
311 xdrproc_t xresults; /* xdr routine for results */
312 caddr_t resultsp; /* pointer to results */
313 struct timeval utimeout; /* seconds to wait before giving up */
314 {
315 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
316 register XDR *xdrs;
317 register int outlen;
318 register int inlen;
319 u_int32_t fromlen;
320 fd_set readfds;
321 fd_set mask;
322 struct sockaddr_in from;
323 struct rpc_msg reply_msg;
324 XDR reply_xdrs;
325 struct timeval time_waited;
326 bool_t ok;
327 int nrefreshes = 2; /* number of times to refresh cred */
328 struct timeval timeout;
329
330 if (cu->cu_total_timeout.tv_sec == -1)
331 {
332 /* use supplied total timeout */
333 timeout = utimeout;
334 }
335 else
336 {
337 /* use client's total timeout */
338 timeout = cu->cu_total_timeout;
339 }
340
341 time_waited.tv_sec = 0;
342 time_waited.tv_usec = 0;
343
344 call_again:
345 xdrs = &(cu->cu_outxdrs);
346 xdrs->x_op = XDR_ENCODE;
347 XDR_SETPOS(xdrs, cu->cu_xdrpos);
348
349 /*
350 * the transaction is the first thing in the out buffer
351 */
352 (*(u_short *)(cu->cu_outbuf))++;
353 #ifdef __LP64__
354 if ((! XDR_PUTLONG(xdrs, (int *)&proc)) ||
355 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
356 (! (*xargs)(xdrs, argsp)))
357 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
358 #else
359 if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
360 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
361 (! (*xargs)(xdrs, argsp)))
362 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
363 #endif
364 outlen = (int)XDR_GETPOS(xdrs);
365
366 send_again:
367 if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen)
368 {
369 cu->cu_error.re_errno = errno;
370 return (cu->cu_error.re_status = RPC_CANTSEND);
371 }
372
373 /*
374 * Hack to provide rpc-based message passing
375 */
376 if (timeout.tv_sec == 0 && timeout.tv_usec == 0)
377 {
378 return (cu->cu_error.re_status = RPC_TIMEDOUT);
379 }
380
381 /*
382 * sub-optimal code appears here because we have
383 * some clock time to spare while the packets are in flight.
384 * (We assume that this is actually only executed once.)
385 */
386 reply_msg.acpted_rply.ar_verf = _null_auth;
387 reply_msg.acpted_rply.ar_results.where = resultsp;
388 reply_msg.acpted_rply.ar_results.proc = xresults;
389 FD_ZERO(&mask);
390 FD_SET(cu->cu_sock, &mask);
391 for (;;)
392 {
393 readfds = mask;
394 switch (select(cu->cu_sock+1, &readfds, NULL, NULL, &(cu->cu_retry_timeout))) {
395 case 0:
396 time_waited.tv_sec += cu->cu_retry_timeout.tv_sec;
397 time_waited.tv_usec += cu->cu_retry_timeout.tv_usec;
398 while (time_waited.tv_usec >= 1000000) {
399 time_waited.tv_sec++;
400 time_waited.tv_usec -= 1000000;
401 }
402 if ((time_waited.tv_sec < timeout.tv_sec) ||
403 ((time_waited.tv_sec == timeout.tv_sec) &&
404 (time_waited.tv_usec < timeout.tv_usec)))
405 goto send_again;
406 return (cu->cu_error.re_status = RPC_TIMEDOUT);
407
408 /*
409 * buggy in other cases because time_waited is not being
410 * updated.
411 */
412 case -1:
413 if (errno == EINTR)
414 continue;
415 cu->cu_error.re_errno = errno;
416 return (cu->cu_error.re_status = RPC_CANTRECV);
417 }
418 do {
419 fromlen = sizeof(struct sockaddr);
420 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
421 (int) cu->cu_recvsz, 0,
422 (struct sockaddr *)&from, &fromlen);
423 } while (inlen < 0 && errno == EINTR);
424 if (inlen < 0) {
425 if (errno == EWOULDBLOCK)
426 continue;
427 cu->cu_error.re_errno = errno;
428 return (cu->cu_error.re_status = RPC_CANTRECV);
429 }
430 if (inlen < sizeof(u_long))
431 continue;
432 /* see if reply transaction id matches sent id */
433 if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf)))
434 continue;
435 /* we now assume we have the proper reply */
436 break;
437 }
438
439 /*
440 * now decode and validate the response
441 */
442 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
443 ok = xdr_replymsg(&reply_xdrs, &reply_msg);
444 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
445 if (ok) {
446 _seterr_reply(&reply_msg, &(cu->cu_error));
447 if (cu->cu_error.re_status == RPC_SUCCESS) {
448 if (! AUTH_VALIDATE(cl->cl_auth,
449 &reply_msg.acpted_rply.ar_verf)) {
450 cu->cu_error.re_status = RPC_AUTHERROR;
451 cu->cu_error.re_why = AUTH_INVALIDRESP;
452 }
453 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
454 xdrs->x_op = XDR_FREE;
455 (void)xdr_opaque_auth(xdrs,
456 &(reply_msg.acpted_rply.ar_verf));
457 }
458 } /* end successful completion */
459 else {
460 /* maybe our credentials need to be refreshed ... */
461 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
462 nrefreshes--;
463 goto call_again;
464 }
465 } /* end of unsuccessful completion */
466 } /* end of valid reply message */
467 else {
468 cu->cu_error.re_status = RPC_CANTDECODERES;
469 }
470 return (cu->cu_error.re_status);
471 }
472
473 static void
474 clntudp_geterr(cl, errp)
475 CLIENT *cl;
476 struct rpc_err *errp;
477 {
478 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
479
480 *errp = cu->cu_error;
481 }
482
483
484 static bool_t
485 clntudp_freeres(cl, xdr_res, res_ptr)
486 CLIENT *cl;
487 xdrproc_t xdr_res;
488 caddr_t res_ptr;
489 {
490 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
491 register XDR *xdrs = &(cu->cu_outxdrs);
492
493 xdrs->x_op = XDR_FREE;
494 return ((*xdr_res)(xdrs, res_ptr));
495 }
496
497 static void
498 clntudp_abort(/*h*/)
499 /*CLIENT *h;*/
500 {
501 }
502
503 static bool_t
504 clntudp_control(cl, request, info)
505 CLIENT *cl;
506 int request;
507 char *info;
508 {
509 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
510
511 switch (request) {
512 case CLSET_TIMEOUT:
513 cu->cu_total_timeout = *(struct timeval *)info;
514 break;
515 case CLGET_TIMEOUT:
516 *(struct timeval *)info = cu->cu_total_timeout;
517 break;
518 case CLSET_RETRY_TIMEOUT:
519 cu->cu_retry_timeout = *(struct timeval *)info;
520 break;
521 case CLGET_RETRY_TIMEOUT:
522 *(struct timeval *)info = cu->cu_retry_timeout;
523 break;
524 case CLGET_SERVER_ADDR:
525 *(struct sockaddr_in *)info = cu->cu_raddr;
526 break;
527 default:
528 return (FALSE);
529 }
530 return (TRUE);
531 }
532
533 static void
534 clntudp_destroy(cl)
535 CLIENT *cl;
536 {
537 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
538
539 if (cu->cu_closeit) {
540 (void)close(cu->cu_sock);
541 }
542 XDR_DESTROY(&(cu->cu_outxdrs));
543 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
544 mem_free((caddr_t)cl, sizeof(CLIENT));
545 }