2 * clnt_udp.c, Implements a UDP/IP based, client side RPC.
4 * Copyright (C) 1984, Sun Microsystems, Inc.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
16 * * Neither the name of Sun Microsystems, Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 #include <sys/socket.h>
42 #include <sys/ioctl.h>
45 #include <rpc/pmap_clnt.h>
58 #include <kernel-features.h>
60 extern bool_t xdr_opaque_auth (XDR *, struct opaque_auth *);
61 extern u_long _create_xid (void);
64 * UDP bases client side rpc operations
66 static enum clnt_stat clntudp_call (CLIENT *, u_long, xdrproc_t, caddr_t,
67 xdrproc_t, caddr_t, struct timeval);
68 static void clntudp_abort (void);
69 static void clntudp_geterr (CLIENT *, struct rpc_err *);
70 static bool_t clntudp_freeres (CLIENT *, xdrproc_t, caddr_t);
71 static bool_t clntudp_control (CLIENT *, int, char *);
72 static void clntudp_destroy (CLIENT *);
74 static const struct clnt_ops udp_ops =
85 * Private data kept per client handle
91 struct sockaddr_in cu_raddr;
93 struct timeval cu_wait;
94 struct timeval cu_total;
95 struct rpc_err cu_error;
105 * Create a UDP based client handle.
106 * If *sockp<0, *sockp is set to a newly created UPD socket.
107 * If raddr->sin_port is 0 a binder on the remote machine
108 * is consulted for the correct port number.
109 * NB: It is the clients responsibility to close *sockp.
110 * NB: The rpch->cl_auth is initialized to null authentication.
111 * Caller may wish to set this something more useful.
113 * wait is the amount of time used between retransmitting a call if
114 * no response has been heard; retransmission occurs until the actual
115 * rpc call times out.
117 * sendsz and recvsz are the maximum allowable packet sizes that can be
121 __libc_clntudp_bufcreate (struct sockaddr_in *raddr, u_long program,
122 u_long version, struct timeval wait, int *sockp,
123 u_int sendsz, u_int recvsz, int flags)
126 struct cu_data *cu = NULL;
127 struct rpc_msg call_msg;
129 cl = (CLIENT *) mem_alloc (sizeof (CLIENT));
130 sendsz = ((sendsz + 3) / 4) * 4;
131 recvsz = ((recvsz + 3) / 4) * 4;
132 cu = (struct cu_data *) mem_alloc (sizeof (*cu) + sendsz + recvsz);
133 if (cl == NULL || cu == NULL)
135 struct rpc_createerr *ce = &get_rpc_createerr ();
136 (void) __fxprintf (NULL, "%s: %s",
137 "clntudp_create", _("out of memory\n"));
138 ce->cf_stat = RPC_SYSTEMERROR;
139 ce->cf_error.re_errno = ENOMEM;
142 cu->cu_outbuf = &cu->cu_inbuf[recvsz];
144 if (raddr->sin_port == 0)
148 pmap_getport (raddr, program, version, IPPROTO_UDP)) == 0)
152 raddr->sin_port = htons (port);
154 cl->cl_ops = (struct clnt_ops *) &udp_ops;
155 cl->cl_private = (caddr_t) cu;
156 cu->cu_raddr = *raddr;
157 cu->cu_rlen = sizeof (cu->cu_raddr);
159 cu->cu_total.tv_sec = -1;
160 cu->cu_total.tv_usec = -1;
161 cu->cu_sendsz = sendsz;
162 cu->cu_recvsz = recvsz;
163 call_msg.rm_xid = _create_xid ();
164 call_msg.rm_direction = CALL;
165 call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
166 call_msg.rm_call.cb_prog = program;
167 call_msg.rm_call.cb_vers = version;
168 INTUSE(xdrmem_create) (&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
169 if (!INTUSE(xdr_callhdr) (&(cu->cu_outxdrs), &call_msg))
173 cu->cu_xdrpos = XDR_GETPOS (&(cu->cu_outxdrs));
179 # ifndef __ASSUME_SOCK_CLOEXEC
180 if (__have_sock_cloexec >= 0)
183 *sockp = __socket (AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|flags,
185 # ifndef __ASSUME_SOCK_CLOEXEC
186 if (__have_sock_cloexec == 0)
187 __have_sock_cloexec = *sockp >= 0 || errno != EINVAL ? 1 : -1;
191 #ifndef __ASSUME_SOCK_CLOEXEC
193 if (__have_sock_cloexec < 0)
196 *sockp = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
198 if (flags & SOCK_CLOEXEC)
199 __fcntl (*sockp, F_SETFD, FD_CLOEXEC);
203 if (__builtin_expect (*sockp < 0, 0))
205 struct rpc_createerr *ce = &get_rpc_createerr ();
206 ce->cf_stat = RPC_SYSTEMERROR;
207 ce->cf_error.re_errno = errno;
210 /* attempt to bind to prov port */
211 (void) bindresvport (*sockp, (struct sockaddr_in *) 0);
212 #ifndef __ASSUME_SOCK_CLOEXEC
214 if (__have_sock_cloexec < 0)
216 /* the sockets rpc controls are non-blocking */
217 (void) __ioctl (*sockp, FIONBIO, (char *) &dontblock);
222 __setsockopt (*sockp, SOL_IP, IP_RECVERR, &on, sizeof(on));
225 cu->cu_closeit = TRUE;
229 cu->cu_closeit = FALSE;
231 cu->cu_sock = *sockp;
232 cl->cl_auth = INTUSE(authnone_create) ();
236 mem_free ((caddr_t) cu, sizeof (*cu) + sendsz + recvsz);
238 mem_free ((caddr_t) cl, sizeof (CLIENT));
239 return (CLIENT *) NULL;
241 INTDEF (__libc_clntudp_bufcreate)
244 clntudp_bufcreate (struct sockaddr_in *raddr, u_long program, u_long version,
245 struct timeval wait, int *sockp, u_int sendsz,
248 return INTUSE(__libc_clntudp_bufcreate) (raddr, program, version, wait,
249 sockp, sendsz, recvsz, 0);
251 INTDEF (clntudp_bufcreate)
254 clntudp_create (raddr, program, version, wait, sockp)
255 struct sockaddr_in *raddr;
261 return INTUSE(__libc_clntudp_bufcreate) (raddr, program, version, wait,
262 sockp, UDPMSGSIZE, UDPMSGSIZE, 0);
264 INTDEF (clntudp_create)
267 is_network_up (int sock)
271 if (getifaddrs (&ifa) != 0)
274 struct ifaddrs *run = ifa;
277 if ((run->ifa_flags & IFF_UP) != 0
278 && run->ifa_addr != NULL
279 && run->ifa_addr->sa_family == AF_INET)
290 static enum clnt_stat
291 clntudp_call (cl, proc, xargs, argsp, xresults, resultsp, utimeout)
292 CLIENT *cl; /* client handle */
293 u_long proc; /* procedure number */
294 xdrproc_t xargs; /* xdr routine for args */
295 caddr_t argsp; /* pointer to args */
296 xdrproc_t xresults; /* xdr routine for results */
297 caddr_t resultsp; /* pointer to results */
298 struct timeval utimeout; /* seconds to wait before giving up */
300 struct cu_data *cu = (struct cu_data *) cl->cl_private;
306 int milliseconds = (cu->cu_wait.tv_sec * 1000) +
307 (cu->cu_wait.tv_usec / 1000);
308 struct sockaddr_in from;
309 struct rpc_msg reply_msg;
311 struct timeval time_waited;
313 int nrefreshes = 2; /* number of times to refresh cred */
314 struct timeval timeout;
315 int anyup; /* any network interface up */
317 if (cu->cu_total.tv_usec == -1)
319 timeout = utimeout; /* use supplied timeout */
323 timeout = cu->cu_total; /* use default timeout */
326 time_waited.tv_sec = 0;
327 time_waited.tv_usec = 0;
329 xdrs = &(cu->cu_outxdrs);
332 xdrs->x_op = XDR_ENCODE;
333 XDR_SETPOS (xdrs, cu->cu_xdrpos);
335 * the transaction is the first thing in the out buffer
337 (*(uint32_t *) (cu->cu_outbuf))++;
338 if ((!XDR_PUTLONG (xdrs, (long *) &proc)) ||
339 (!AUTH_MARSHALL (cl->cl_auth, xdrs)) ||
340 (!(*xargs) (xdrs, argsp)))
341 return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
342 outlen = (int) XDR_GETPOS (xdrs);
345 if (__sendto (cu->cu_sock, cu->cu_outbuf, outlen, 0,
346 (struct sockaddr *) &(cu->cu_raddr), cu->cu_rlen)
349 cu->cu_error.re_errno = errno;
350 return (cu->cu_error.re_status = RPC_CANTSEND);
354 * Hack to provide rpc-based message passing
356 if (timeout.tv_sec == 0 && timeout.tv_usec == 0)
358 return (cu->cu_error.re_status = RPC_TIMEDOUT);
362 * sub-optimal code appears here because we have
363 * some clock time to spare while the packets are in flight.
364 * (We assume that this is actually only executed once.)
366 reply_msg.acpted_rply.ar_verf = _null_auth;
367 reply_msg.acpted_rply.ar_results.where = resultsp;
368 reply_msg.acpted_rply.ar_results.proc = xresults;
374 switch (__poll (&fd, 1, milliseconds))
380 anyup = is_network_up (cu->cu_sock);
382 return (cu->cu_error.re_status = RPC_CANTRECV);
385 time_waited.tv_sec += cu->cu_wait.tv_sec;
386 time_waited.tv_usec += cu->cu_wait.tv_usec;
387 while (time_waited.tv_usec >= 1000000)
389 time_waited.tv_sec++;
390 time_waited.tv_usec -= 1000000;
392 if ((time_waited.tv_sec < timeout.tv_sec) ||
393 ((time_waited.tv_sec == timeout.tv_sec) &&
394 (time_waited.tv_usec < timeout.tv_usec)))
396 return (cu->cu_error.re_status = RPC_TIMEDOUT);
399 * buggy in other cases because time_waited is not being
405 cu->cu_error.re_errno = errno;
406 return (cu->cu_error.re_status = RPC_CANTRECV);
409 if (fd.revents & POLLERR)
412 struct cmsghdr *cmsg;
413 struct sock_extended_err *e;
414 struct sockaddr_in err_addr;
416 char *cbuf = (char *) alloca (outlen + 256);
419 iov.iov_base = cbuf + 256;
420 iov.iov_len = outlen;
421 msg.msg_name = (void *) &err_addr;
422 msg.msg_namelen = sizeof (err_addr);
426 msg.msg_control = cbuf;
427 msg.msg_controllen = 256;
428 ret = __recvmsg (cu->cu_sock, &msg, MSG_ERRQUEUE);
430 && memcmp (cbuf + 256, cu->cu_outbuf, ret) == 0
431 && (msg.msg_flags & MSG_ERRQUEUE)
432 && ((msg.msg_namelen == 0
434 || (msg.msg_namelen == sizeof (err_addr)
435 && err_addr.sin_family == AF_INET
436 && memcmp (&err_addr.sin_addr, &cu->cu_raddr.sin_addr,
437 sizeof (err_addr.sin_addr)) == 0
438 && err_addr.sin_port == cu->cu_raddr.sin_port)))
439 for (cmsg = CMSG_FIRSTHDR (&msg); cmsg;
440 cmsg = CMSG_NXTHDR (&msg, cmsg))
441 if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
443 e = (struct sock_extended_err *) CMSG_DATA(cmsg);
444 cu->cu_error.re_errno = e->ee_errno;
445 return (cu->cu_error.re_status = RPC_CANTRECV);
451 fromlen = sizeof (struct sockaddr);
452 inlen = __recvfrom (cu->cu_sock, cu->cu_inbuf,
453 (int) cu->cu_recvsz, MSG_DONTWAIT,
454 (struct sockaddr *) &from, &fromlen);
456 while (inlen < 0 && errno == EINTR);
459 if (errno == EWOULDBLOCK)
461 cu->cu_error.re_errno = errno;
462 return (cu->cu_error.re_status = RPC_CANTRECV);
467 /* see if reply transaction id matches sent id.
468 Don't do this if we only wait for a replay */
470 && (*((u_int32_t *) (cu->cu_inbuf))
471 != *((u_int32_t *) (cu->cu_outbuf))))
473 /* we now assume we have the proper reply */
478 * now decode and validate the response
480 INTUSE(xdrmem_create) (&reply_xdrs, cu->cu_inbuf, (u_int) inlen, XDR_DECODE);
481 ok = INTUSE(xdr_replymsg) (&reply_xdrs, &reply_msg);
482 /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
485 _seterr_reply (&reply_msg, &(cu->cu_error));
486 if (cu->cu_error.re_status == RPC_SUCCESS)
488 if (!AUTH_VALIDATE (cl->cl_auth,
489 &reply_msg.acpted_rply.ar_verf))
491 cu->cu_error.re_status = RPC_AUTHERROR;
492 cu->cu_error.re_why = AUTH_INVALIDRESP;
494 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
496 xdrs->x_op = XDR_FREE;
497 (void) INTUSE(xdr_opaque_auth) (xdrs,
498 &(reply_msg.acpted_rply.ar_verf));
500 } /* end successful completion */
503 /* maybe our credentials need to be refreshed ... */
504 if (nrefreshes > 0 && AUTH_REFRESH (cl->cl_auth))
509 } /* end of unsuccessful completion */
510 } /* end of valid reply message */
513 cu->cu_error.re_status = RPC_CANTDECODERES;
515 return cu->cu_error.re_status;
519 clntudp_geterr (CLIENT *cl, struct rpc_err *errp)
521 struct cu_data *cu = (struct cu_data *) cl->cl_private;
523 *errp = cu->cu_error;
528 clntudp_freeres (CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr)
530 struct cu_data *cu = (struct cu_data *) cl->cl_private;
531 XDR *xdrs = &(cu->cu_outxdrs);
533 xdrs->x_op = XDR_FREE;
534 return (*xdr_res) (xdrs, res_ptr);
543 clntudp_control (CLIENT *cl, int request, char *info)
545 struct cu_data *cu = (struct cu_data *) cl->cl_private;
550 cu->cu_closeit = TRUE;
552 case CLSET_FD_NCLOSE:
553 cu->cu_closeit = FALSE;
556 cu->cu_total = *(struct timeval *) info;
559 *(struct timeval *) info = cu->cu_total;
561 case CLSET_RETRY_TIMEOUT:
562 cu->cu_wait = *(struct timeval *) info;
564 case CLGET_RETRY_TIMEOUT:
565 *(struct timeval *) info = cu->cu_wait;
567 case CLGET_SERVER_ADDR:
568 *(struct sockaddr_in *) info = cu->cu_raddr;
571 *(int *)info = cu->cu_sock;
575 * use the knowledge that xid is the
576 * first element in the call structure *.
577 * This will get the xid of the PREVIOUS call
579 *(u_long *)info = ntohl(*(u_long *)cu->cu_outbuf);
582 /* This will set the xid of the NEXT call */
583 *(u_long *)cu->cu_outbuf = htonl(*(u_long *)info - 1);
584 /* decrement by 1 as clntudp_call() increments once */
587 * This RELIES on the information that, in the call body,
588 * the version number field is the fifth field from the
589 * begining of the RPC header. MUST be changed if the
590 * call_struct is changed
592 *(u_long *)info = ntohl(*(u_long *)(cu->cu_outbuf +
593 4 * BYTES_PER_XDR_UNIT));
596 *(u_long *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
597 = htonl(*(u_long *)info);
601 * This RELIES on the information that, in the call body,
602 * the program number field is the field from the
603 * begining of the RPC header. MUST be changed if the
604 * call_struct is changed
606 *(u_long *)info = ntohl(*(u_long *)(cu->cu_outbuf +
607 3 * BYTES_PER_XDR_UNIT));
610 *(u_long *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
611 = htonl(*(u_long *)info);
613 /* The following are only possible with TI-RPC */
616 case CLSET_PUSH_TIMOD:
617 case CLSET_POP_TIMOD:
625 clntudp_destroy (CLIENT *cl)
627 struct cu_data *cu = (struct cu_data *) cl->cl_private;
631 (void) __close (cu->cu_sock);
633 XDR_DESTROY (&(cu->cu_outxdrs));
634 mem_free ((caddr_t) cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
635 mem_free ((caddr_t) cl, sizeof (CLIENT));