]> git.saurik.com Git - apple/libinfo.git/blob - rpc.subproj/clnt_udp.c
Libinfo-459.40.1.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 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 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 = *total_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 #ifdef __LP64__
309 uint32_t proc; /* procedure number */
310 #else
311 u_long proc; /* procedure number */
312 #endif
313 xdrproc_t xargs; /* xdr routine for args */
314 caddr_t argsp; /* pointer to args */
315 xdrproc_t xresults; /* xdr routine for results */
316 caddr_t resultsp; /* pointer to results */
317 struct timeval utimeout; /* seconds to wait before giving up */
318 {
319 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
320 register XDR *xdrs;
321 register int outlen;
322 register int inlen;
323 u_int32_t fromlen;
324 fd_set readfds;
325 fd_set mask;
326 struct sockaddr_in from;
327 struct rpc_msg reply_msg;
328 XDR reply_xdrs;
329 struct timeval time_waited;
330 bool_t ok;
331 int nrefreshes = 2; /* number of times to refresh cred */
332 struct timeval timeout;
333
334 if (cu->cu_total_timeout.tv_sec == -1)
335 {
336 /* use supplied total timeout */
337 timeout = utimeout;
338 }
339 else
340 {
341 /* use client's total timeout */
342 timeout = cu->cu_total_timeout;
343 }
344
345 time_waited.tv_sec = 0;
346 time_waited.tv_usec = 0;
347
348 call_again:
349 xdrs = &(cu->cu_outxdrs);
350 xdrs->x_op = XDR_ENCODE;
351 XDR_SETPOS(xdrs, cu->cu_xdrpos);
352
353 /*
354 * the transaction is the first thing in the out buffer
355 */
356 (*(u_short *)(cu->cu_outbuf))++;
357 #ifdef __LP64__
358 if ((! XDR_PUTLONG(xdrs, (int *)&proc)) ||
359 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
360 (! (*xargs)(xdrs, argsp, 0)))
361 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
362 #else
363 if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
364 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
365 (! (*xargs)(xdrs, argsp, 0)))
366 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
367 #endif
368 outlen = (int)XDR_GETPOS(xdrs);
369
370 send_again:
371 if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen)
372 {
373 cu->cu_error.re_errno = errno;
374 return (cu->cu_error.re_status = RPC_CANTSEND);
375 }
376
377 /*
378 * Hack to provide rpc-based message passing
379 */
380 if (timeout.tv_sec == 0 && timeout.tv_usec == 0)
381 {
382 return (cu->cu_error.re_status = RPC_TIMEDOUT);
383 }
384
385 /*
386 * sub-optimal code appears here because we have
387 * some clock time to spare while the packets are in flight.
388 * (We assume that this is actually only executed once.)
389 */
390 reply_msg.acpted_rply.ar_verf = _null_auth;
391 reply_msg.acpted_rply.ar_results.where = resultsp;
392 reply_msg.acpted_rply.ar_results.proc = xresults;
393 FD_ZERO(&mask);
394 FD_SET(cu->cu_sock, &mask);
395 for (;;)
396 {
397 readfds = mask;
398 switch (select(cu->cu_sock+1, &readfds, NULL, NULL, &(cu->cu_retry_timeout))) {
399 case 0:
400 time_waited.tv_sec += cu->cu_retry_timeout.tv_sec;
401 time_waited.tv_usec += cu->cu_retry_timeout.tv_usec;
402 while (time_waited.tv_usec >= 1000000) {
403 time_waited.tv_sec++;
404 time_waited.tv_usec -= 1000000;
405 }
406 if ((time_waited.tv_sec < timeout.tv_sec) ||
407 ((time_waited.tv_sec == timeout.tv_sec) &&
408 (time_waited.tv_usec < timeout.tv_usec)))
409 goto send_again;
410 return (cu->cu_error.re_status = RPC_TIMEDOUT);
411
412 /*
413 * buggy in other cases because time_waited is not being
414 * updated.
415 */
416 case -1:
417 if (errno == EINTR)
418 continue;
419 cu->cu_error.re_errno = errno;
420 return (cu->cu_error.re_status = RPC_CANTRECV);
421 }
422 do {
423 fromlen = sizeof(struct sockaddr);
424 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
425 (int) cu->cu_recvsz, 0,
426 (struct sockaddr *)&from, &fromlen);
427 } while (inlen < 0 && errno == EINTR);
428 if (inlen < 0) {
429 if (errno == EWOULDBLOCK)
430 continue;
431 cu->cu_error.re_errno = errno;
432 return (cu->cu_error.re_status = RPC_CANTRECV);
433 }
434 #ifdef __LP64__
435 if (inlen < sizeof(uint32_t))
436 continue;
437 /* see if reply transaction id matches sent id */
438 if (*((uint32_t *)(cu->cu_inbuf)) != *((uint32_t *)(cu->cu_outbuf))) continue;
439 #else
440 if (inlen < sizeof(u_long))
441 continue;
442 /* see if reply transaction id matches sent id */
443 if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf))) continue;
444 #endif
445 /* we now assume we have the proper reply */
446 break;
447 }
448
449 /*
450 * now decode and validate the response
451 */
452 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
453 ok = xdr_replymsg(&reply_xdrs, &reply_msg);
454 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
455 if (ok) {
456 _seterr_reply(&reply_msg, &(cu->cu_error));
457 if (cu->cu_error.re_status == RPC_SUCCESS) {
458 if (! AUTH_VALIDATE(cl->cl_auth,
459 &reply_msg.acpted_rply.ar_verf)) {
460 cu->cu_error.re_status = RPC_AUTHERROR;
461 cu->cu_error.re_why = AUTH_INVALIDRESP;
462 }
463 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
464 xdrs->x_op = XDR_FREE;
465 (void)xdr_opaque_auth(xdrs,
466 &(reply_msg.acpted_rply.ar_verf));
467 }
468 } /* end successful completion */
469 else {
470 /* maybe our credentials need to be refreshed ... */
471 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
472 nrefreshes--;
473 goto call_again;
474 }
475 } /* end of unsuccessful completion */
476 } /* end of valid reply message */
477 else {
478 cu->cu_error.re_status = RPC_CANTDECODERES;
479 }
480 return (cu->cu_error.re_status);
481 }
482
483 static void
484 clntudp_geterr(cl, errp)
485 CLIENT *cl;
486 struct rpc_err *errp;
487 {
488 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
489
490 *errp = cu->cu_error;
491 }
492
493
494 static bool_t
495 clntudp_freeres(cl, xdr_res, res_ptr)
496 CLIENT *cl;
497 xdrproc_t xdr_res;
498 caddr_t res_ptr;
499 {
500 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
501 register XDR *xdrs = &(cu->cu_outxdrs);
502
503 xdrs->x_op = XDR_FREE;
504 return ((*xdr_res)(xdrs, res_ptr, 0));
505 }
506
507 static void
508 clntudp_abort(/*h*/)
509 /*CLIENT *h;*/
510 {
511 }
512
513 static bool_t
514 clntudp_control(cl, request, info)
515 CLIENT *cl;
516 int request;
517 char *info;
518 {
519 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
520
521 switch (request) {
522 case CLSET_TIMEOUT:
523 cu->cu_total_timeout = *(struct timeval *)info;
524 break;
525 case CLGET_TIMEOUT:
526 *(struct timeval *)info = cu->cu_total_timeout;
527 break;
528 case CLSET_RETRY_TIMEOUT:
529 cu->cu_retry_timeout = *(struct timeval *)info;
530 break;
531 case CLGET_RETRY_TIMEOUT:
532 *(struct timeval *)info = cu->cu_retry_timeout;
533 break;
534 case CLGET_SERVER_ADDR:
535 *(struct sockaddr_in *)info = cu->cu_raddr;
536 break;
537 default:
538 return (FALSE);
539 }
540 return (TRUE);
541 }
542
543 static void
544 clntudp_destroy(cl)
545 CLIENT *cl;
546 {
547 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
548
549 if (cu->cu_closeit) {
550 (void)close(cu->cu_sock);
551 }
552 XDR_DESTROY(&(cu->cu_outxdrs));
553 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
554 mem_free((caddr_t)cl, sizeof(CLIENT));
555 }