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