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