Libinfo-173.1.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 * 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 int errno;
81
82 /*
83 * UDP bases client side rpc operations
84 */
85 static enum clnt_stat clntudp_call();
86 static void clntudp_abort();
87 static void clntudp_geterr();
88 static bool_t clntudp_freeres();
89 static bool_t clntudp_control();
90 static void clntudp_destroy();
91
92 static struct clnt_ops udp_ops = {
93 clntudp_call,
94 clntudp_abort,
95 clntudp_geterr,
96 clntudp_freeres,
97 clntudp_destroy,
98 clntudp_control
99 };
100
101 /*
102 * Private data kept per client handle
103 */
104 struct cu_data {
105 int cu_sock;
106 bool_t cu_closeit;
107 struct sockaddr_in cu_raddr;
108 int cu_rlen;
109 struct timeval cu_wait;
110 struct timeval cu_total;
111 struct rpc_err cu_error;
112 XDR cu_outxdrs;
113 u_int cu_xdrpos;
114 u_int cu_sendsz;
115 char *cu_outbuf;
116 u_int cu_recvsz;
117 char cu_inbuf[1];
118 };
119
120 /*
121 * Create a UDP based client handle.
122 * If *sockp<0, *sockp is set to a newly created UPD socket.
123 * If raddr->sin_port is 0 a binder on the remote machine
124 * is consulted for the correct port number.
125 * NB: It is the clients responsibility to close *sockp.
126 * NB: The rpch->cl_auth is initialized to null authentication.
127 * Caller may wish to set this something more useful.
128 *
129 * wait is the amount of time used between retransmitting a call if
130 * no response has been heard; retransmition occurs until the actual
131 * rpc call times out.
132 *
133 * sendsz and recvsz are the maximum allowable packet sizes that can be
134 * sent and received.
135 */
136 CLIENT *
137 clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
138 struct sockaddr_in *raddr;
139 u_long program;
140 u_long version;
141 struct timeval wait;
142 register int *sockp;
143 u_int sendsz;
144 u_int recvsz;
145 {
146 CLIENT *cl;
147 register struct cu_data *cu = NULL;
148 struct timeval now;
149 struct rpc_msg call_msg;
150 int rfd;
151
152 cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
153 if (cl == NULL) {
154 (void) fprintf(stderr, "clntudp_create: out of memory\n");
155 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
156 rpc_createerr.cf_error.re_errno = errno;
157 goto fooy;
158 }
159 sendsz = ((sendsz + 3) / 4) * 4;
160 recvsz = ((recvsz + 3) / 4) * 4;
161 cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
162 if (cu == NULL) {
163 (void) fprintf(stderr, "clntudp_create: out of memory\n");
164 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
165 rpc_createerr.cf_error.re_errno = errno;
166 goto fooy;
167 }
168 cu->cu_outbuf = &cu->cu_inbuf[recvsz];
169
170 if (raddr->sin_port == 0) {
171 u_short port;
172 if ((port =
173 pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
174 goto fooy;
175 }
176 raddr->sin_port = htons(port);
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_wait = wait;
183 cu->cu_total.tv_sec = -1;
184 cu->cu_total.tv_usec = -1;
185 cu->cu_sendsz = sendsz;
186 cu->cu_recvsz = recvsz;
187
188 rfd = open("/dev/random", O_RDONLY, 0);
189 if ((rfd < 0) || (read(rfd, &call_msg.rm_xid, sizeof(call_msg.rm_xid)) != sizeof(call_msg.rm_xid)))
190 {
191 gettimeofday(&now, (struct timezone *)0);
192 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
193 }
194 if (rfd > 0) close(rfd);
195
196 call_msg.rm_direction = CALL;
197 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
198 call_msg.rm_call.cb_prog = program;
199 call_msg.rm_call.cb_vers = version;
200 xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
201 sendsz, XDR_ENCODE);
202 if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
203 goto fooy;
204 }
205 cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
206 if (*sockp < 0) {
207 int dontblock = 1;
208
209 *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
210 if (*sockp < 0) {
211 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
212 rpc_createerr.cf_error.re_errno = errno;
213 goto fooy;
214 }
215 /* attempt to bind to prov port */
216 (void)bindresvport(*sockp, (struct sockaddr_in *)0);
217 /* the sockets rpc controls are non-blocking */
218 (void)ioctl(*sockp, FIONBIO, (char *) &dontblock);
219 cu->cu_closeit = TRUE;
220 } else {
221 cu->cu_closeit = FALSE;
222 }
223 cu->cu_sock = *sockp;
224 cl->cl_auth = authnone_create();
225 return (cl);
226 fooy:
227 if (cu)
228 mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);
229 if (cl)
230 mem_free((caddr_t)cl, sizeof(CLIENT));
231 return ((CLIENT *)NULL);
232 }
233
234 CLIENT *
235 clntudp_create(raddr, program, version, wait, sockp)
236 struct sockaddr_in *raddr;
237 u_long program;
238 u_long version;
239 struct timeval wait;
240 register int *sockp;
241 {
242
243 return(clntudp_bufcreate(raddr, program, version, wait, sockp,
244 UDPMSGSIZE, UDPMSGSIZE));
245 }
246
247 static enum clnt_stat
248 clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
249 register CLIENT *cl; /* client handle */
250 u_long proc; /* procedure number */
251 xdrproc_t xargs; /* xdr routine for args */
252 caddr_t argsp; /* pointer to args */
253 xdrproc_t xresults; /* xdr routine for results */
254 caddr_t resultsp; /* pointer to results */
255 struct timeval utimeout; /* seconds to wait before giving up */
256 {
257 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
258 register XDR *xdrs;
259 register int outlen;
260 register int inlen;
261 int fromlen;
262 fd_set readfds;
263 fd_set mask;
264 struct sockaddr_in from;
265 struct rpc_msg reply_msg;
266 XDR reply_xdrs;
267 struct timeval time_waited;
268 bool_t ok;
269 int nrefreshes = 2; /* number of times to refresh cred */
270 struct timeval timeout;
271
272 if (cu->cu_total.tv_usec == -1) {
273 timeout = utimeout; /* use supplied timeout */
274 } else {
275 timeout = cu->cu_total; /* use default timeout */
276 }
277
278 time_waited.tv_sec = 0;
279 time_waited.tv_usec = 0;
280 call_again:
281 xdrs = &(cu->cu_outxdrs);
282 xdrs->x_op = XDR_ENCODE;
283 XDR_SETPOS(xdrs, cu->cu_xdrpos);
284 /*
285 * the transaction is the first thing in the out buffer
286 */
287 (*(u_short *)(cu->cu_outbuf))++;
288 if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
289 (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
290 (! (*xargs)(xdrs, argsp)))
291 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
292 outlen = (int)XDR_GETPOS(xdrs);
293
294 send_again:
295 if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
296 (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen)
297 != outlen) {
298 cu->cu_error.re_errno = errno;
299 return (cu->cu_error.re_status = RPC_CANTSEND);
300 }
301
302 /*
303 * Hack to provide rpc-based message passing
304 */
305 if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
306 return (cu->cu_error.re_status = RPC_TIMEDOUT);
307 }
308 /*
309 * sub-optimal code appears here because we have
310 * some clock time to spare while the packets are in flight.
311 * (We assume that this is actually only executed once.)
312 */
313 reply_msg.acpted_rply.ar_verf = _null_auth;
314 reply_msg.acpted_rply.ar_results.where = resultsp;
315 reply_msg.acpted_rply.ar_results.proc = xresults;
316 FD_ZERO(&mask);
317 FD_SET(cu->cu_sock, &mask);
318 for (;;) {
319 readfds = mask;
320 switch (select(cu->cu_sock+1, &readfds, NULL,
321 NULL, &(cu->cu_wait))) {
322
323 case 0:
324 time_waited.tv_sec += cu->cu_wait.tv_sec;
325 time_waited.tv_usec += cu->cu_wait.tv_usec;
326 while (time_waited.tv_usec >= 1000000) {
327 time_waited.tv_sec++;
328 time_waited.tv_usec -= 1000000;
329 }
330 if ((time_waited.tv_sec < timeout.tv_sec) ||
331 ((time_waited.tv_sec == timeout.tv_sec) &&
332 (time_waited.tv_usec < timeout.tv_usec)))
333 goto send_again;
334 return (cu->cu_error.re_status = RPC_TIMEDOUT);
335
336 /*
337 * buggy in other cases because time_waited is not being
338 * updated.
339 */
340 case -1:
341 if (errno == EINTR)
342 continue;
343 cu->cu_error.re_errno = errno;
344 return (cu->cu_error.re_status = RPC_CANTRECV);
345 }
346 do {
347 fromlen = sizeof(struct sockaddr);
348 inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
349 (int) cu->cu_recvsz, 0,
350 (struct sockaddr *)&from, &fromlen);
351 } while (inlen < 0 && errno == EINTR);
352 if (inlen < 0) {
353 if (errno == EWOULDBLOCK)
354 continue;
355 cu->cu_error.re_errno = errno;
356 return (cu->cu_error.re_status = RPC_CANTRECV);
357 }
358 if (inlen < sizeof(u_long))
359 continue;
360 /* see if reply transaction id matches sent id */
361 if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf)))
362 continue;
363 /* we now assume we have the proper reply */
364 break;
365 }
366
367 /*
368 * now decode and validate the response
369 */
370 xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
371 ok = xdr_replymsg(&reply_xdrs, &reply_msg);
372 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
373 if (ok) {
374 _seterr_reply(&reply_msg, &(cu->cu_error));
375 if (cu->cu_error.re_status == RPC_SUCCESS) {
376 if (! AUTH_VALIDATE(cl->cl_auth,
377 &reply_msg.acpted_rply.ar_verf)) {
378 cu->cu_error.re_status = RPC_AUTHERROR;
379 cu->cu_error.re_why = AUTH_INVALIDRESP;
380 }
381 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
382 xdrs->x_op = XDR_FREE;
383 (void)xdr_opaque_auth(xdrs,
384 &(reply_msg.acpted_rply.ar_verf));
385 }
386 } /* end successful completion */
387 else {
388 /* maybe our credentials need to be refreshed ... */
389 if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
390 nrefreshes--;
391 goto call_again;
392 }
393 } /* end of unsuccessful completion */
394 } /* end of valid reply message */
395 else {
396 cu->cu_error.re_status = RPC_CANTDECODERES;
397 }
398 return (cu->cu_error.re_status);
399 }
400
401 static void
402 clntudp_geterr(cl, errp)
403 CLIENT *cl;
404 struct rpc_err *errp;
405 {
406 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
407
408 *errp = cu->cu_error;
409 }
410
411
412 static bool_t
413 clntudp_freeres(cl, xdr_res, res_ptr)
414 CLIENT *cl;
415 xdrproc_t xdr_res;
416 caddr_t res_ptr;
417 {
418 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
419 register XDR *xdrs = &(cu->cu_outxdrs);
420
421 xdrs->x_op = XDR_FREE;
422 return ((*xdr_res)(xdrs, res_ptr));
423 }
424
425 static void
426 clntudp_abort(/*h*/)
427 /*CLIENT *h;*/
428 {
429 }
430
431 static bool_t
432 clntudp_control(cl, request, info)
433 CLIENT *cl;
434 int request;
435 char *info;
436 {
437 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
438
439 switch (request) {
440 case CLSET_TIMEOUT:
441 cu->cu_total = *(struct timeval *)info;
442 break;
443 case CLGET_TIMEOUT:
444 *(struct timeval *)info = cu->cu_total;
445 break;
446 case CLSET_RETRY_TIMEOUT:
447 cu->cu_wait = *(struct timeval *)info;
448 break;
449 case CLGET_RETRY_TIMEOUT:
450 *(struct timeval *)info = cu->cu_wait;
451 break;
452 case CLGET_SERVER_ADDR:
453 *(struct sockaddr_in *)info = cu->cu_raddr;
454 break;
455 default:
456 return (FALSE);
457 }
458 return (TRUE);
459 }
460
461 static void
462 clntudp_destroy(cl)
463 CLIENT *cl;
464 {
465 register struct cu_data *cu = (struct cu_data *)cl->cl_private;
466
467 if (cu->cu_closeit) {
468 (void)close(cu->cu_sock);
469 }
470 XDR_DESTROY(&(cu->cu_outxdrs));
471 mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
472 mem_free((caddr_t)cl, sizeof(CLIENT));
473 }