Libinfo-173.tar.gz
[apple/libinfo.git] / rpc.subproj / clnt_udp.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
27 * unrestricted use provided that this legend is included on all tape
28 * media and as a part of the software program in whole or part. Users
29 * may copy or modify Sun RPC without charge, but are not authorized
30 * to license or distribute it to anyone else except as part of a product or
31 * program developed by the user.
32 *
33 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
34 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
35 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
36 *
37 * Sun RPC is provided with no support and without any obligation on the
38 * part of Sun Microsystems, Inc. to assist in its use, correction,
39 * modification or enhancement.
40 *
41 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
42 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
43 * OR ANY PART THEREOF.
44 *
45 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
46 * or profits or other special, indirect and consequential damages, even if
47 * Sun has been advised of the possibility of such damages.
48 *
49 * Sun Microsystems, Inc.
50 * 2550 Garcia Avenue
51 * Mountain View, California 94043
52 */
53
54 #if defined(LIBC_SCCS) && !defined(lint)
55 /*static char *sccsid = "from: @(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";*/
56 /*static char *sccsid = "from: @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC";*/
57 static char *rcsid = "$Id: clnt_udp.c,v 1.4 2002/03/15 22:07:49 majka Exp $";
58 #endif
59
60 /*
61 * clnt_udp.c, Implements a UDP/IP based, client side RPC.
62 *
63 * Copyright (C) 1984, Sun Microsystems, Inc.
64 */
65
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <unistd.h>
70 #include <rpc/rpc.h>
71 #include <sys/socket.h>
72 #include <sys/fcntl.h>
73 #include <sys/ioctl.h>
74 #include <netdb.h>
75 #include <errno.h>
76 #include <rpc/pmap_clnt.h>
77
78 extern int bindresvport();
79 extern bool_t xdr_opaque_auth();
80
81 extern int errno;
82
83 /*
84 * UDP bases client side rpc operations
85 */
86 static enum clnt_stat clntudp_call();
87 static void clntudp_abort();
88 static void clntudp_geterr();
89 static bool_t clntudp_freeres();
90 static bool_t clntudp_control();
91 static void clntudp_destroy();
92
93 static struct clnt_ops udp_ops = {
94 clntudp_call,
95 clntudp_abort,
96 clntudp_geterr,
97 clntudp_freeres,
98 clntudp_destroy,
99 clntudp_control
100 };
101
102 /*
103 * Private data kept per client handle
104 */
105 struct cu_data {
106 int cu_sock;
107 bool_t cu_closeit;
108 struct sockaddr_in cu_raddr;
109 int cu_rlen;
110 struct timeval cu_wait;
111 struct timeval cu_total;
112 struct rpc_err cu_error;
113 XDR cu_outxdrs;
114 u_int cu_xdrpos;
115 u_int cu_sendsz;
116 char *cu_outbuf;
117 u_int cu_recvsz;
118 char cu_inbuf[1];
119 };
120
121 /*
122 * Create a UDP based client handle.
123 * If *sockp<0, *sockp is set to a newly created UPD socket.
124 * If raddr->sin_port is 0 a binder on the remote machine
125 * is consulted for the correct port number.
126 * NB: It is the clients responsibility to close *sockp.
127 * NB: The rpch->cl_auth is initialized to null authentication.
128 * Caller may wish to set this something more useful.
129 *
130 * wait is the amount of time used between retransmitting a call if
131 * no response has been heard; retransmition occurs until the actual
132 * rpc call times out.
133 *
134 * sendsz and recvsz are the maximum allowable packet sizes that can be
135 * sent and received.
136 */
137 CLIENT *
138 clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
139 struct sockaddr_in *raddr;
140 u_long program;
141 u_long version;
142 struct timeval wait;
143 register int *sockp;
144 u_int sendsz;
145 u_int recvsz;
146 {
147 CLIENT *cl;
148 register struct cu_data *cu = NULL;
149 struct timeval now;
150 struct rpc_msg call_msg;
151 int rfd;
152
153 cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
154 if (cl == NULL) {
155 (void) fprintf(stderr, "clntudp_create: out of memory\n");
156 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
157 rpc_createerr.cf_error.re_errno = errno;
158 goto fooy;
159 }
160 sendsz = ((sendsz + 3) / 4) * 4;
161 recvsz = ((recvsz + 3) / 4) * 4;
162 cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
163 if (cu == NULL) {
164 (void) fprintf(stderr, "clntudp_create: out of memory\n");
165 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
166 rpc_createerr.cf_error.re_errno = errno;
167 goto fooy;
168 }
169 cu->cu_outbuf = &cu->cu_inbuf[recvsz];
170
171 if (raddr->sin_port == 0) {
172 u_short port;
173 if ((port =
174 pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
175 goto fooy;
176 }
177 raddr->sin_port = htons(port);
178 }
179 cl->cl_ops = &udp_ops;
180 cl->cl_private = (caddr_t)cu;
181 cu->cu_raddr = *raddr;
182 cu->cu_rlen = sizeof (cu->cu_raddr);
183 cu->cu_wait = wait;
184 cu->cu_total.tv_sec = -1;
185 cu->cu_total.tv_usec = -1;
186 cu->cu_sendsz = sendsz;
187 cu->cu_recvsz = recvsz;
188
189 rfd = open("/dev/random", O_RDONLY, 0);
190 if ((rfd < 0) || (read(rfd, &call_msg.rm_xid, sizeof(call_msg.rm_xid)) != sizeof(call_msg.rm_xid)))
191 {
192 gettimeofday(&now, (struct timezone *)0);
193 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
194 }
195 if (rfd > 0) close(rfd);
196
197 call_msg.rm_direction = CALL;
198 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
199 call_msg.rm_call.cb_prog = program;
200 call_msg.rm_call.cb_vers = version;
201 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
202 sendsz, XDR_ENCODE);
203 if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
204 goto fooy;
205 }
206 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
207 if (*sockp < 0) {
208 int dontblock = 1;
209
210 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
211 if (*sockp < 0) {
212 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
213 rpc_createerr.cf_error.re_errno = errno;
214 goto fooy;
215 }
216 /* attempt to bind to prov port */
217 (void)bindresvport(*sockp, (struct sockaddr_in *)0);
218 /* the sockets rpc controls are non-blocking */
219 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock);
220 cu->cu_closeit = TRUE;
221 } else {
222 cu->cu_closeit = FALSE;
223 }
224 cu->cu_sock = *sockp;
225 cl->cl_auth = authnone_create();
226 return (cl);
227 fooy:
228 if (cu)
229 mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);
230 if (cl)
231 mem_free((caddr_t)cl, sizeof(CLIENT));
232 return ((CLIENT *)NULL);
233 }
234
235 CLIENT *
236 clntudp_create(raddr, program, version, wait, sockp)
237 struct sockaddr_in *raddr;
238 u_long program;
239 u_long version;
240 struct timeval wait;
241 register int *sockp;
242 {
243
244 return(clntudp_bufcreate(raddr, program, version, wait, sockp,
245 UDPMSGSIZE, UDPMSGSIZE));
246 }
247
248 static enum clnt_stat
249 clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
250 register CLIENT *cl; /* client handle */
251 u_long proc; /* procedure number */
252 xdrproc_t xargs; /* xdr routine for args */
253 caddr_t argsp; /* pointer to args */
254 xdrproc_t xresults; /* xdr routine for results */
255 caddr_t resultsp; /* pointer to results */
256 struct timeval utimeout; /* seconds to wait before giving up */
257 {
258 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
259 register XDR *xdrs;
260 register int outlen;
261 register int inlen;
262 int fromlen;
263 fd_set readfds;
264 fd_set mask;
265 struct sockaddr_in from;
266 struct rpc_msg reply_msg;
267 XDR reply_xdrs;
268 struct timeval time_waited;
269 bool_t ok;
270 int nrefreshes = 2; /* number of times to refresh cred */
271 struct timeval timeout;
272
273 if (cu->cu_total.tv_usec == -1) {
274 timeout = utimeout; /* use supplied timeout */
275 } else {
276 timeout = cu->cu_total; /* use default timeout */
277 }
278
279 time_waited.tv_sec = 0;
280 time_waited.tv_usec = 0;
281 call_again:
282 xdrs = &(cu->cu_outxdrs);
283 xdrs->x_op = XDR_ENCODE;
284 XDR_SETPOS(xdrs, cu->cu_xdrpos);
285 /*
286 * the transaction is the first thing in the out buffer
287 */
288 (*(u_short *)(cu->cu_outbuf))++;
289 if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
290 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
291 (! (*xargs)(xdrs, argsp)))
292 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
293 outlen = (int)XDR_GETPOS(xdrs);
294
295 send_again:
296 if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
297 (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen)
298 != outlen) {
299 cu->cu_error.re_errno = errno;
300 return (cu->cu_error.re_status = RPC_CANTSEND);
301 }
302
303 /*
304 * Hack to provide rpc-based message passing
305 */
306 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
307 return (cu->cu_error.re_status = RPC_TIMEDOUT);
308 }
309 /*
310 * sub-optimal code appears here because we have
311 * some clock time to spare while the packets are in flight.
312 * (We assume that this is actually only executed once.)
313 */
314 reply_msg.acpted_rply.ar_verf = _null_auth;
315 reply_msg.acpted_rply.ar_results.where = resultsp;
316 reply_msg.acpted_rply.ar_results.proc = xresults;
317 FD_ZERO(&mask);
318 FD_SET(cu->cu_sock, &mask);
319 for (;;) {
320 readfds = mask;
321 switch (select(cu->cu_sock+1, &readfds, NULL,
322 NULL, &(cu->cu_wait))) {
323
324 case 0:
325 time_waited.tv_sec += cu->cu_wait.tv_sec;
326 time_waited.tv_usec += cu->cu_wait.tv_usec;
327 while (time_waited.tv_usec >= 1000000) {
328 time_waited.tv_sec++;
329 time_waited.tv_usec -= 1000000;
330 }
331 if ((time_waited.tv_sec < timeout.tv_sec) ||
332 ((time_waited.tv_sec == timeout.tv_sec) &&
333 (time_waited.tv_usec < timeout.tv_usec)))
334 goto send_again;
335 return (cu->cu_error.re_status = RPC_TIMEDOUT);
336
337 /*
338 * buggy in other cases because time_waited is not being
339 * updated.
340 */
341 case -1:
342 if (errno == EINTR)
343 continue;
344 cu->cu_error.re_errno = errno;
345 return (cu->cu_error.re_status = RPC_CANTRECV);
346 }
347 do {
348 fromlen = sizeof(struct sockaddr);
349 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
350 (int) cu->cu_recvsz, 0,
351 (struct sockaddr *)&from, &fromlen);
352 } while (inlen < 0 && errno == EINTR);
353 if (inlen < 0) {
354 if (errno == EWOULDBLOCK)
355 continue;
356 cu->cu_error.re_errno = errno;
357 return (cu->cu_error.re_status = RPC_CANTRECV);
358 }
359 if (inlen < sizeof(u_long))
360 continue;
361 /* see if reply transaction id matches sent id */
362 if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf)))
363 continue;
364 /* we now assume we have the proper reply */
365 break;
366 }
367
368 /*
369 * now decode and validate the response
370 */
371 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
372 ok = xdr_replymsg(&reply_xdrs, &reply_msg);
373 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
374 if (ok) {
375 _seterr_reply(&reply_msg, &(cu->cu_error));
376 if (cu->cu_error.re_status == RPC_SUCCESS) {
377 if (! AUTH_VALIDATE(cl->cl_auth,
378 &reply_msg.acpted_rply.ar_verf)) {
379 cu->cu_error.re_status = RPC_AUTHERROR;
380 cu->cu_error.re_why = AUTH_INVALIDRESP;
381 }
382 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
383 xdrs->x_op = XDR_FREE;
384 (void)xdr_opaque_auth(xdrs,
385 &(reply_msg.acpted_rply.ar_verf));
386 }
387 } /* end successful completion */
388 else {
389 /* maybe our credentials need to be refreshed ... */
390 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
391 nrefreshes--;
392 goto call_again;
393 }
394 } /* end of unsuccessful completion */
395 } /* end of valid reply message */
396 else {
397 cu->cu_error.re_status = RPC_CANTDECODERES;
398 }
399 return (cu->cu_error.re_status);
400 }
401
402 static void
403 clntudp_geterr(cl, errp)
404 CLIENT *cl;
405 struct rpc_err *errp;
406 {
407 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
408
409 *errp = cu->cu_error;
410 }
411
412
413 static bool_t
414 clntudp_freeres(cl, xdr_res, res_ptr)
415 CLIENT *cl;
416 xdrproc_t xdr_res;
417 caddr_t res_ptr;
418 {
419 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
420 register XDR *xdrs = &(cu->cu_outxdrs);
421
422 xdrs->x_op = XDR_FREE;
423 return ((*xdr_res)(xdrs, res_ptr));
424 }
425
426 static void
427 clntudp_abort(/*h*/)
428 /*CLIENT *h;*/
429 {
430 }
431
432 static bool_t
433 clntudp_control(cl, request, info)
434 CLIENT *cl;
435 int request;
436 char *info;
437 {
438 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
439
440 switch (request) {
441 case CLSET_TIMEOUT:
442 cu->cu_total = *(struct timeval *)info;
443 break;
444 case CLGET_TIMEOUT:
445 *(struct timeval *)info = cu->cu_total;
446 break;
447 case CLSET_RETRY_TIMEOUT:
448 cu->cu_wait = *(struct timeval *)info;
449 break;
450 case CLGET_RETRY_TIMEOUT:
451 *(struct timeval *)info = cu->cu_wait;
452 break;
453 case CLGET_SERVER_ADDR:
454 *(struct sockaddr_in *)info = cu->cu_raddr;
455 break;
456 default:
457 return (FALSE);
458 }
459 return (TRUE);
460 }
461
462 static void
463 clntudp_destroy(cl)
464 CLIENT *cl;
465 {
466 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
467
468 if (cu->cu_closeit) {
469 (void)close(cu->cu_sock);
470 }
471 XDR_DESTROY(&(cu->cu_outxdrs));
472 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
473 mem_free((caddr_t)cl, sizeof(CLIENT));
474 }