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