]> git.saurik.com Git - apple/libinfo.git/blame - rpc.subproj/clnt_udp.c
Libinfo-459.40.1.tar.gz
[apple/libinfo.git] / rpc.subproj / clnt_udp.c
CommitLineData
03fb6eb0 1/*
b3dd680f 2 * Copyright (c) 1999-2007 Apple Inc. All rights reserved.
03fb6eb0
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
ad21edcc
A
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.
03fb6eb0
A
13 *
14 * The Original Code and all software distributed under the License are
ad21edcc 15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
03fb6eb0
A
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
ad21edcc
A
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.
03fb6eb0
A
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";*/
3b7c7bd7 56static char *rcsid = "$Id: clnt_udp.c,v 1.4 2002/03/15 22:07:49 majka Exp $";
03fb6eb0
A
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>
3b7c7bd7
A
66#include <stdlib.h>
67#include <string.h>
68#include <unistd.h>
03fb6eb0
A
69#include <rpc/rpc.h>
70#include <sys/socket.h>
3b7c7bd7 71#include <sys/fcntl.h>
03fb6eb0
A
72#include <sys/ioctl.h>
73#include <netdb.h>
74#include <errno.h>
75#include <rpc/pmap_clnt.h>
76
3b7c7bd7
A
77extern int bindresvport();
78extern bool_t xdr_opaque_auth();
79
e44d8d47 80extern u_short pmap_getport_timeout(struct sockaddr_in *address, uint32_t program, uint32_t version, uint32_t protocol, struct timeval *timeout, struct timeval *totaltimeout);
b3dd680f 81
03fb6eb0
A
82extern int errno;
83
84/*
85 * UDP bases client side rpc operations
86 */
87static enum clnt_stat clntudp_call();
88static void clntudp_abort();
89static void clntudp_geterr();
90static bool_t clntudp_freeres();
91static bool_t clntudp_control();
92static void clntudp_destroy();
93
94static 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 */
106struct cu_data {
107 int cu_sock;
108 bool_t cu_closeit;
109 struct sockaddr_in cu_raddr;
110 int cu_rlen;
b3dd680f
A
111 struct timeval cu_retry_timeout;
112 struct timeval cu_total_timeout;
03fb6eb0
A
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 */
e44d8d47 138CLIENT *
b3dd680f 139clntudp_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)
03fb6eb0
A
140{
141 CLIENT *cl;
b3dd680f 142 struct cu_data *cu = NULL;
03fb6eb0
A
143 struct timeval now;
144 struct rpc_msg call_msg;
3b7c7bd7 145 int rfd;
b3dd680f
A
146 u_short port;
147 socklen_t len;
148 unsigned int rsize;
03fb6eb0
A
149
150 cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
b3dd680f
A
151 if (cl == NULL)
152 {
03fb6eb0
A
153 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
154 rpc_createerr.cf_error.re_errno = errno;
155 goto fooy;
156 }
b3dd680f 157
03fb6eb0
A
158 sendsz = ((sendsz + 3) / 4) * 4;
159 recvsz = ((recvsz + 3) / 4) * 4;
160 cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
b3dd680f
A
161 if (cu == NULL)
162 {
03fb6eb0
A
163 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
164 rpc_createerr.cf_error.re_errno = errno;
165 goto fooy;
166 }
b3dd680f 167
03fb6eb0
A
168 cu->cu_outbuf = &cu->cu_inbuf[recvsz];
169
b3dd680f
A
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
03fb6eb0
A
175 raddr->sin_port = htons(port);
176 }
b3dd680f 177
03fb6eb0
A
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);
03fb6eb0
A
182 cu->cu_sendsz = sendsz;
183 cu->cu_recvsz = recvsz;
b3dd680f
A
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;
d49d4c81 191 if (total_timeout != NULL) cu->cu_total_timeout = *total_timeout;
3b7c7bd7
A
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 }
b3dd680f 199
3b7c7bd7
A
200 if (rfd > 0) close(rfd);
201
03fb6eb0
A
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;
b3dd680f
A
206 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
207 if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) goto fooy;
208
03fb6eb0 209 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
b3dd680f
A
210 if (*sockp < 0)
211 {
03fb6eb0
A
212 int dontblock = 1;
213
214 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
b3dd680f
A
215 if (*sockp < 0)
216 {
03fb6eb0
A
217 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
218 rpc_createerr.cf_error.re_errno = errno;
219 goto fooy;
220 }
b3dd680f 221
03fb6eb0
A
222 /* attempt to bind to prov port */
223 (void)bindresvport(*sockp, (struct sockaddr_in *)0);
b3dd680f
A
224
225 /* the socket's rpc controls are non-blocking */
03fb6eb0
A
226 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock);
227 cu->cu_closeit = TRUE;
b3dd680f
A
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 {
03fb6eb0
A
249 cu->cu_closeit = FALSE;
250 }
b3dd680f 251
03fb6eb0
A
252 cu->cu_sock = *sockp;
253 cl->cl_auth = authnone_create();
254 return (cl);
b3dd680f 255
03fb6eb0 256fooy:
b3dd680f
A
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
262CLIENT *
263clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
264#ifdef __LP64__
1c0d47b0
A
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;
b3dd680f 272#else
1c0d47b0
A
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;
b3dd680f
A
280#endif
281{
282 return clntudp_bufcreate_timeout(raddr, (uint32_t)program, (uint32_t)version, sockp, (uint32_t)sendsz, (uint32_t)recvsz, &wait, NULL);
03fb6eb0
A
283}
284
285CLIENT *
286clntudp_create(raddr, program, version, wait, sockp)
b3dd680f
A
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
03fb6eb0
A
294 struct sockaddr_in *raddr;
295 u_long program;
296 u_long version;
297 struct timeval wait;
298 register int *sockp;
b3dd680f 299#endif
03fb6eb0
A
300{
301
b3dd680f 302 return clntudp_bufcreate(raddr, program, version, wait, sockp, UDPMSGSIZE, UDPMSGSIZE);
03fb6eb0
A
303}
304
305static enum clnt_stat
306clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
307 register CLIENT *cl; /* client handle */
1c0d47b0
A
308#ifdef __LP64__
309 uint32_t proc; /* procedure number */
310#else
03fb6eb0 311 u_long proc; /* procedure number */
1c0d47b0 312#endif
03fb6eb0
A
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;
b3dd680f 323 u_int32_t fromlen;
03fb6eb0
A
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
b3dd680f
A
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;
03fb6eb0
A
343 }
344
345 time_waited.tv_sec = 0;
346 time_waited.tv_usec = 0;
b3dd680f 347
03fb6eb0
A
348call_again:
349 xdrs = &(cu->cu_outxdrs);
350 xdrs->x_op = XDR_ENCODE;
351 XDR_SETPOS(xdrs, cu->cu_xdrpos);
b3dd680f 352
03fb6eb0
A
353 /*
354 * the transaction is the first thing in the out buffer
355 */
356 (*(u_short *)(cu->cu_outbuf))++;
b3dd680f
A
357#ifdef __LP64__
358 if ((! XDR_PUTLONG(xdrs, (int *)&proc)) ||
359 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
0eb52ff2 360 (! (*xargs)(xdrs, argsp, 0)))
b3dd680f
A
361 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
362#else
03fb6eb0
A
363 if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
364 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
0eb52ff2 365 (! (*xargs)(xdrs, argsp, 0)))
03fb6eb0 366 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
b3dd680f 367#endif
03fb6eb0
A
368 outlen = (int)XDR_GETPOS(xdrs);
369
370send_again:
b3dd680f
A
371 if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen)
372 {
03fb6eb0
A
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 */
b3dd680f
A
380 if (timeout.tv_sec == 0 && timeout.tv_usec == 0)
381 {
03fb6eb0
A
382 return (cu->cu_error.re_status = RPC_TIMEDOUT);
383 }
b3dd680f 384
03fb6eb0
A
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);
b3dd680f
A
395 for (;;)
396 {
03fb6eb0 397 readfds = mask;
b3dd680f 398 switch (select(cu->cu_sock+1, &readfds, NULL, NULL, &(cu->cu_retry_timeout))) {
03fb6eb0 399 case 0:
b3dd680f
A
400 time_waited.tv_sec += cu->cu_retry_timeout.tv_sec;
401 time_waited.tv_usec += cu->cu_retry_timeout.tv_usec;
03fb6eb0
A
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 }
d49d4c81 434#ifdef __LP64__
1c0d47b0
A
435 if (inlen < sizeof(uint32_t))
436 continue;
437 /* see if reply transaction id matches sent id */
d49d4c81
A
438 if (*((uint32_t *)(cu->cu_inbuf)) != *((uint32_t *)(cu->cu_outbuf))) continue;
439#else
1c0d47b0
A
440 if (inlen < sizeof(u_long))
441 continue;
442 /* see if reply transaction id matches sent id */
d49d4c81
A
443 if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf))) continue;
444#endif
03fb6eb0
A
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
483static void
484clntudp_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
494static bool_t
495clntudp_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;
0eb52ff2 504 return ((*xdr_res)(xdrs, res_ptr, 0));
03fb6eb0
A
505}
506
507static void
508clntudp_abort(/*h*/)
509 /*CLIENT *h;*/
510{
511}
512
513static bool_t
514clntudp_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:
b3dd680f 523 cu->cu_total_timeout = *(struct timeval *)info;
03fb6eb0
A
524 break;
525 case CLGET_TIMEOUT:
b3dd680f 526 *(struct timeval *)info = cu->cu_total_timeout;
03fb6eb0
A
527 break;
528 case CLSET_RETRY_TIMEOUT:
b3dd680f 529 cu->cu_retry_timeout = *(struct timeval *)info;
03fb6eb0
A
530 break;
531 case CLGET_RETRY_TIMEOUT:
b3dd680f 532 *(struct timeval *)info = cu->cu_retry_timeout;
03fb6eb0
A
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
543static void
544clntudp_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}