1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23/*
24 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28/* All Rights Reserved */
29/*
30 * Portions of this source code were derived from Berkeley
31 * 4.3 BSD under license from the Regents of the University of
32 * California.
33 */
34
35#pragma ident	"%Z%%M%	%I%	%E% SMI"
36
37/*
38 * Implements a connectionless client side RPC.
39 */
40
41#include "mt.h"
42#include "rpc_mt.h"
43#include <assert.h>
44#include <rpc/rpc.h>
45#include <errno.h>
46#include <sys/poll.h>
47#include <syslog.h>
48#include <sys/types.h>
49#include <sys/kstat.h>
50#include <sys/time.h>
51#include <stdlib.h>
52#include <unistd.h>
53#include <sys/types.h>
54#include <sys/stat.h>
55#include <strings.h>
56
57
58extern int __rpc_timeval_to_msec(struct timeval *);
59extern bool_t xdr_opaque_auth(XDR *, struct opaque_auth *);
60extern bool_t __rpc_gss_wrap(AUTH *, char *, uint_t, XDR *, bool_t (*)(),
61								caddr_t);
62extern bool_t __rpc_gss_unwrap(AUTH *, XDR *, bool_t (*)(), caddr_t);
63
64
65static struct clnt_ops *clnt_dg_ops(void);
66static bool_t time_not_ok(struct timeval *);
67
68/*
69 *	This machinery implements per-fd locks for MT-safety.  It is not
70 *	sufficient to do per-CLIENT handle locks for MT-safety because a
71 *	user may create more than one CLIENT handle with the same fd behind
72 *	it.
73 *
74 *	The current implementation holds locks across the entire RPC and reply,
75 *	including retransmissions.  Yes, this is silly, and as soon as this
76 *	code is proven to work, this should be the first thing fixed.  One step
77 *	at a time.
78 */
79
80/*
81 * FD Lock handle used by various MT sync. routines
82 */
83static mutex_t dgtbl_lock = DEFAULTMUTEX;
84static	void	*dgtbl = NULL;
85
86static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
87
88
89#define	MCALL_MSG_SIZE 24
90
91/*
92 * Private data kept per client handle
93 */
94struct cu_data {
95	int			cu_fd;		/* connections fd */
96	bool_t			cu_closeit;	/* opened by library */
97	struct netbuf		cu_raddr;	/* remote address */
98	struct timeval		cu_wait;	/* retransmit interval */
99	struct timeval		cu_total;	/* total time for the call */
100	struct rpc_err		cu_error;
101	struct t_unitdata	*cu_tr_data;
102	XDR			cu_outxdrs;
103	char			*cu_outbuf_start;
104	char			cu_outbuf[MCALL_MSG_SIZE];
105	uint_t			cu_xdrpos;
106	uint_t			cu_sendsz;	/* send size */
107	uint_t			cu_recvsz;	/* recv size */
108	struct pollfd		pfdp;
109	char			cu_inbuf[1];
110};
111
112static int _rcv_unitdata_err(struct cu_data *cu);
113
114/*
115 * Connection less client creation returns with client handle parameters.
116 * Default options are set, which the user can change using clnt_control().
117 * fd should be open and bound.
118 * NB: The rpch->cl_auth is initialized to null authentication.
119 * 	Caller may wish to set this something more useful.
120 *
121 * sendsz and recvsz are the maximum allowable packet sizes that can be
122 * sent and received. Normally they are the same, but they can be
123 * changed to improve the program efficiency and buffer allocation.
124 * If they are 0, use the transport default.
125 *
126 * If svcaddr is NULL, returns NULL.
127 */
128CLIENT *
129clnt_dg_create(const int fd, struct netbuf *svcaddr, const rpcprog_t program,
130	const rpcvers_t version, const uint_t sendsz, const uint_t recvsz)
131{
132	CLIENT *cl = NULL;		/* client handle */
133	struct cu_data *cu = NULL;	/* private data */
134	struct t_unitdata *tr_data;
135	struct t_info tinfo;
136	struct timeval now;
137	struct rpc_msg call_msg;
138	uint_t ssz;
139	uint_t rsz;
140
141	sig_mutex_lock(&dgtbl_lock);
142	if ((dgtbl == NULL) && ((dgtbl = rpc_fd_init()) == NULL)) {
143		sig_mutex_unlock(&dgtbl_lock);
144		goto err1;
145	}
146	sig_mutex_unlock(&dgtbl_lock);
147
148	if (svcaddr == NULL) {
149		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
150		return (NULL);
151	}
152	if (t_getinfo(fd, &tinfo) == -1) {
153		rpc_createerr.cf_stat = RPC_TLIERROR;
154		rpc_createerr.cf_error.re_errno = 0;
155		rpc_createerr.cf_error.re_terrno = t_errno;
156		return (NULL);
157	}
158	/*
159	 * Setup to rcv datagram error, we ignore any errors returned from
160	 * __rpc_tli_set_options() as SO_DGRAM_ERRIND is only relevant to
161	 * udp/udp6 transports and this point in the code we only know that
162	 * we are using a connection less transport.
163	 */
164	if (tinfo.servtype == T_CLTS)
165		(void) __rpc_tli_set_options(fd, SOL_SOCKET, SO_DGRAM_ERRIND,
166					    1);
167	/*
168	 * Find the receive and the send size
169	 */
170	ssz = __rpc_get_t_size((int)sendsz, tinfo.tsdu);
171	rsz = __rpc_get_t_size((int)recvsz, tinfo.tsdu);
172	if ((ssz == 0) || (rsz == 0)) {
173		rpc_createerr.cf_stat = RPC_TLIERROR; /* XXX */
174		rpc_createerr.cf_error.re_errno = 0;
175		rpc_createerr.cf_error.re_terrno = 0;
176		return (NULL);
177	}
178
179	if ((cl = malloc(sizeof (CLIENT))) == NULL)
180		goto err1;
181	/*
182	 * Should be multiple of 4 for XDR.
183	 */
184	ssz = ((ssz + 3) / 4) * 4;
185	rsz = ((rsz + 3) / 4) * 4;
186	cu = malloc(sizeof (*cu) + ssz + rsz);
187	if (cu == NULL)
188		goto err1;
189	if ((cu->cu_raddr.buf = malloc(svcaddr->len)) == NULL)
190		goto err1;
191	(void) memcpy(cu->cu_raddr.buf, svcaddr->buf, (size_t)svcaddr->len);
192	cu->cu_raddr.len = cu->cu_raddr.maxlen = svcaddr->len;
193	cu->cu_outbuf_start = &cu->cu_inbuf[rsz];
194	/* Other values can also be set through clnt_control() */
195	cu->cu_wait.tv_sec = 15;	/* heuristically chosen */
196	cu->cu_wait.tv_usec = 0;
197	cu->cu_total.tv_sec = -1;
198	cu->cu_total.tv_usec = -1;
199	cu->cu_sendsz = ssz;
200	cu->cu_recvsz = rsz;
201	(void) gettimeofday(&now, NULL);
202	call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
203	call_msg.rm_call.cb_prog = program;
204	call_msg.rm_call.cb_vers = version;
205	xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, ssz, XDR_ENCODE);
206	if (!xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
207		rpc_createerr.cf_stat = RPC_CANTENCODEARGS;  /* XXX */
208		rpc_createerr.cf_error.re_errno = 0;
209		rpc_createerr.cf_error.re_terrno = 0;
210		goto err2;
211	}
212	cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
213	XDR_DESTROY(&(cu->cu_outxdrs));
214	xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf_start, ssz,
215								XDR_ENCODE);
216/* LINTED pointer alignment */
217	tr_data = (struct t_unitdata *)t_alloc(fd,
218				T_UNITDATA, T_ADDR | T_OPT);
219	if (tr_data == NULL) {
220		goto err1;
221	}
222	tr_data->udata.maxlen = cu->cu_recvsz;
223	tr_data->udata.buf = cu->cu_inbuf;
224	cu->cu_tr_data = tr_data;
225
226	/*
227	 * By default, closeit is always FALSE. It is users responsibility
228	 * to do a t_close on it, else the user may use clnt_control
229	 * to let clnt_destroy do it for him/her.
230	 */
231	cu->cu_closeit = FALSE;
232	cu->cu_fd = fd;
233	cl->cl_ops = clnt_dg_ops();
234	cl->cl_private = (caddr_t)cu;
235	cl->cl_auth = authnone_create();
236	cl->cl_tp = NULL;
237	cl->cl_netid = NULL;
238	cu->pfdp.fd = cu->cu_fd;
239	cu->pfdp.events = MASKVAL;
240	return (cl);
241err1:
242	(void) syslog(LOG_ERR, mem_err_clnt_dg);
243	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
244	rpc_createerr.cf_error.re_errno = errno;
245	rpc_createerr.cf_error.re_terrno = 0;
246err2:
247	if (cl) {
248		free(cl);
249		if (cu) {
250			free(cu->cu_raddr.buf);
251			free(cu);
252		}
253	}
254	return (NULL);
255}
256
257static enum clnt_stat
258clnt_dg_call(CLIENT *cl, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp,
259	xdrproc_t xresults, caddr_t resultsp, struct timeval utimeout)
260{
261/* LINTED pointer alignment */
262	struct cu_data *cu = (struct cu_data *)cl->cl_private;
263	XDR *xdrs;
264	int outlen;
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	struct timeval retransmit_time;
272	struct timeval poll_time;
273	struct timeval startime, curtime;
274	struct t_unitdata tu_data;
275	int res;			/* result of operations */
276	uint32_t x_id;
277
278	if (rpc_fd_lock(dgtbl, cu->cu_fd)) {
279		rpc_callerr.re_status = RPC_FAILED;
280		rpc_callerr.re_errno = errno;
281		rpc_fd_unlock(dgtbl, cu->cu_fd);
282		return (RPC_FAILED);
283	}
284
285	if (cu->cu_total.tv_usec == -1) {
286		timeout = utimeout;	/* use supplied timeout */
287	} else {
288		timeout = cu->cu_total;	/* use default timeout */
289	}
290
291	time_waited.tv_sec = 0;
292	time_waited.tv_usec = 0;
293	retransmit_time = cu->cu_wait;
294
295	tu_data.addr = cu->cu_raddr;
296
297call_again:
298	xdrs = &(cu->cu_outxdrs);
299	xdrs->x_op = XDR_ENCODE;
300	XDR_SETPOS(xdrs, 0);
301	/*
302	 * Due to little endian byte order, it is necessary to convert to host
303	 * format before incrementing xid.
304	 */
305	/* LINTED pointer cast */
306	x_id = ntohl(*(uint32_t *)(cu->cu_outbuf)) + 1;		/* set XID */
307	/* LINTED pointer cast */
308	*(uint32_t *)cu->cu_outbuf = htonl(x_id);
309
310	if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
311		if ((!XDR_PUTBYTES(xdrs, cu->cu_outbuf, cu->cu_xdrpos)) ||
312				(!XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
313				(!AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
314				(!xargs(xdrs, argsp))) {
315			rpc_fd_unlock(dgtbl, cu->cu_fd);
316			return (rpc_callerr.re_status = RPC_CANTENCODEARGS);
317		}
318	} else {
319/* LINTED pointer alignment */
320		uint32_t *u = (uint32_t *)&cu->cu_outbuf[cu->cu_xdrpos];
321		IXDR_PUT_U_INT32(u, proc);
322		if (!__rpc_gss_wrap(cl->cl_auth, cu->cu_outbuf,
323		    ((char *)u) - cu->cu_outbuf, xdrs, xargs, argsp)) {
324			rpc_fd_unlock(dgtbl, cu->cu_fd);
325			return (rpc_callerr.re_status = RPC_CANTENCODEARGS);
326		}
327	}
328	outlen = (int)XDR_GETPOS(xdrs);
329
330send_again:
331	tu_data.udata.buf = cu->cu_outbuf_start;
332	tu_data.udata.len = outlen;
333	tu_data.opt.len = 0;
334	if (t_sndudata(cu->cu_fd, &tu_data) == -1) {
335		rpc_callerr.re_terrno = t_errno;
336		rpc_callerr.re_errno = errno;
337		rpc_fd_unlock(dgtbl, cu->cu_fd);
338		return (rpc_callerr.re_status = RPC_CANTSEND);
339	}
340
341	/*
342	 * Hack to provide rpc-based message passing
343	 */
344	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
345		rpc_fd_unlock(dgtbl, cu->cu_fd);
346		return (rpc_callerr.re_status = RPC_TIMEDOUT);
347	}
348	/*
349	 * sub-optimal code appears here because we have
350	 * some clock time to spare while the packets are in flight.
351	 * (We assume that this is actually only executed once.)
352	 */
353	reply_msg.acpted_rply.ar_verf = _null_auth;
354	reply_msg.acpted_rply.ar_results.where = NULL;
355	reply_msg.acpted_rply.ar_results.proc = xdr_void;
356
357	/*
358	 * Set polling time so that we don't wait for
359	 * longer than specified by the total time to wait,
360	 * or the retransmit time.
361	 */
362	poll_time.tv_sec = timeout.tv_sec - time_waited.tv_sec;
363	poll_time.tv_usec = timeout.tv_usec - time_waited.tv_usec;
364	while (poll_time.tv_usec < 0) {
365		poll_time.tv_usec += 1000000;
366		poll_time.tv_sec--;
367	}
368
369	if (poll_time.tv_sec < 0 || (poll_time.tv_sec == 0 &&
370					poll_time.tv_usec == 0)) {
371		/*
372		 * this could happen if time_waited >= timeout
373		 */
374		rpc_fd_unlock(dgtbl, cu->cu_fd);
375		return (rpc_callerr.re_status = RPC_TIMEDOUT);
376	}
377
378	if (poll_time.tv_sec > retransmit_time.tv_sec ||
379			(poll_time.tv_sec == retransmit_time.tv_sec &&
380				poll_time.tv_usec > retransmit_time.tv_usec))
381		poll_time = retransmit_time;
382
383
384	for (;;) {
385
386		(void) gettimeofday(&startime, NULL);
387
388		switch (poll(&cu->pfdp, 1,
389				__rpc_timeval_to_msec(&poll_time))) {
390		case -1:
391			if (errno != EINTR && errno != EAGAIN) {
392				rpc_callerr.re_errno = errno;
393				rpc_callerr.re_terrno = 0;
394				rpc_fd_unlock(dgtbl, cu->cu_fd);
395				return (rpc_callerr.re_status = RPC_CANTRECV);
396			}
397			/*FALLTHROUGH*/
398
399		case 0:
400			/*
401			 * update time waited
402			 */
403timeout:			(void) gettimeofday(&curtime, NULL);
404			time_waited.tv_sec += curtime.tv_sec - startime.tv_sec;
405			time_waited.tv_usec += curtime.tv_usec -
406							startime.tv_usec;
407			while (time_waited.tv_usec >= 1000000) {
408				time_waited.tv_usec -= 1000000;
409				time_waited.tv_sec++;
410			}
411			while (time_waited.tv_usec < 0) {
412				time_waited.tv_usec += 1000000;
413				time_waited.tv_sec--;
414			}
415
416			/*
417			 * decrement time left to poll by same amount
418			 */
419			poll_time.tv_sec -= curtime.tv_sec - startime.tv_sec;
420			poll_time.tv_usec -= curtime.tv_usec - startime.tv_usec;
421			while (poll_time.tv_usec >= 1000000) {
422				poll_time.tv_usec -= 1000000;
423				poll_time.tv_sec++;
424			}
425			while (poll_time.tv_usec < 0) {
426				poll_time.tv_usec += 1000000;
427				poll_time.tv_sec--;
428			}
429
430			/*
431			 * if there's time left to poll, poll again
432			 */
433			if (poll_time.tv_sec > 0 ||
434					(poll_time.tv_sec == 0 &&
435						poll_time.tv_usec > 0))
436				continue;
437
438			/*
439			 * if there's more time left, retransmit;
440			 * otherwise, return timeout error
441			 */
442			if (time_waited.tv_sec < timeout.tv_sec ||
443				(time_waited.tv_sec == timeout.tv_sec &&
444				    time_waited.tv_usec < timeout.tv_usec)) {
445				/*
446				 * update retransmit_time
447				 */
448				retransmit_time.tv_usec *= 2;
449				retransmit_time.tv_sec *= 2;
450				while (retransmit_time.tv_usec >= 1000000) {
451					retransmit_time.tv_usec -= 1000000;
452					retransmit_time.tv_sec++;
453				}
454				if (retransmit_time.tv_sec >= RPC_MAX_BACKOFF) {
455					retransmit_time.tv_sec =
456							RPC_MAX_BACKOFF;
457					retransmit_time.tv_usec = 0;
458				}
459				/*
460				 * redo AUTH_MARSHAL if AUTH_DES or RPCSEC_GSS.
461				 */
462				if (cl->cl_auth->ah_cred.oa_flavor ==
463					AUTH_DES ||
464					cl->cl_auth->ah_cred.oa_flavor ==
465					RPCSEC_GSS)
466					goto call_again;
467				else
468					goto send_again;
469			}
470			rpc_fd_unlock(dgtbl, cu->cu_fd);
471			return (rpc_callerr.re_status = RPC_TIMEDOUT);
472
473		default:
474			break;
475		}
476
477		if (cu->pfdp.revents & POLLNVAL || (cu->pfdp.revents == 0)) {
478			rpc_callerr.re_status = RPC_CANTRECV;
479			/*
480			 *	Note:  we're faking errno here because we
481			 *	previously would have expected select() to
482			 *	return -1 with errno EBADF.  Poll(BA_OS)
483			 *	returns 0 and sets the POLLNVAL revents flag
484			 *	instead.
485			 */
486			rpc_callerr.re_errno = errno = EBADF;
487			rpc_fd_unlock(dgtbl, cu->cu_fd);
488			return (-1);
489		}
490
491		/* We have some data now */
492		do {
493			int moreflag;		/* flag indicating more data */
494
495			moreflag = 0;
496
497			res = t_rcvudata(cu->cu_fd, cu->cu_tr_data, &moreflag);
498
499			if (moreflag & T_MORE) {
500				/*
501				 * Drop this packet. I aint got any
502				 * more space.
503				 */
504				res = -1;
505				/* I should not really be doing this */
506				errno = 0;
507				/*
508				 * XXX: Not really Buffer overflow in the
509				 * sense of TLI.
510				 */
511				t_errno = TBUFOVFLW;
512			}
513		} while (res < 0 && (t_errno == TSYSERR && errno == EINTR));
514		if (res < 0) {
515			int err, errnoflag = FALSE;
516#ifdef sun
517			if (t_errno == TSYSERR && errno == EWOULDBLOCK)
518#else
519			if (t_errno == TSYSERR && errno == EAGAIN)
520#endif
521				continue;
522			if (t_errno == TLOOK) {
523				if ((err = _rcv_unitdata_err(cu)) == 0)
524					continue;
525				else if (err == 1)
526					errnoflag = TRUE;
527			} else {
528				rpc_callerr.re_terrno = t_errno;
529			}
530			if (errnoflag == FALSE)
531				rpc_callerr.re_errno = errno;
532			rpc_fd_unlock(dgtbl, cu->cu_fd);
533			return (rpc_callerr.re_status = RPC_CANTRECV);
534		}
535		if (cu->cu_tr_data->udata.len < (uint_t)sizeof (uint32_t))
536			continue;
537		/* see if reply transaction id matches sent id */
538		/* LINTED pointer alignment */
539		if (*((uint32_t *)(cu->cu_inbuf)) !=
540				/* LINTED pointer alignment */
541				*((uint32_t *)(cu->cu_outbuf)))
542			goto timeout;
543		/* we now assume we have the proper reply */
544		break;
545	}
546
547	/*
548	 * now decode and validate the response
549	 */
550
551	xdrmem_create(&reply_xdrs, cu->cu_inbuf,
552		(uint_t)cu->cu_tr_data->udata.len, XDR_DECODE);
553	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
554	/* XDR_DESTROY(&reply_xdrs);	save a few cycles on noop destroy */
555	if (ok) {
556		if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
557			(reply_msg.acpted_rply.ar_stat == SUCCESS))
558			rpc_callerr.re_status = RPC_SUCCESS;
559		else
560			__seterr_reply(&reply_msg, &(rpc_callerr));
561
562		if (rpc_callerr.re_status == RPC_SUCCESS) {
563			if (!AUTH_VALIDATE(cl->cl_auth,
564					    &reply_msg.acpted_rply.ar_verf)) {
565				rpc_callerr.re_status = RPC_AUTHERROR;
566				rpc_callerr.re_why = AUTH_INVALIDRESP;
567			} else if (cl->cl_auth->ah_cred.oa_flavor !=
568								RPCSEC_GSS) {
569				if (!(*xresults)(&reply_xdrs, resultsp)) {
570				    if (rpc_callerr.re_status == RPC_SUCCESS)
571					rpc_callerr.re_status =
572							RPC_CANTDECODERES;
573				}
574			} else if (!__rpc_gss_unwrap(cl->cl_auth, &reply_xdrs,
575						xresults, resultsp)) {
576				if (rpc_callerr.re_status == RPC_SUCCESS)
577				    rpc_callerr.re_status = RPC_CANTDECODERES;
578			}
579		}		/* end successful completion */
580		/*
581		 * If unsuccesful AND error is an authentication error
582		 * then refresh credentials and try again, else break
583		 */
584		else if (rpc_callerr.re_status == RPC_AUTHERROR)
585			/* maybe our credentials need to be refreshed ... */
586			if (nrefreshes-- &&
587			    AUTH_REFRESH(cl->cl_auth, &reply_msg))
588				goto call_again;
589			else
590				/*
591				 * We are setting rpc_callerr here given that
592				 * libnsl is not reentrant thereby
593				 * reinitializing the TSD.  If not set here then
594				 * success could be returned even though refresh
595				 * failed.
596				 */
597				rpc_callerr.re_status = RPC_AUTHERROR;
598
599		/* end of unsuccessful completion */
600		/* free verifier */
601		if (reply_msg.rm_reply.rp_stat == MSG_ACCEPTED &&
602				reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
603			xdrs->x_op = XDR_FREE;
604			(void) xdr_opaque_auth(xdrs,
605					&(reply_msg.acpted_rply.ar_verf));
606		}
607	}	/* end of valid reply message */
608	else {
609		rpc_callerr.re_status = RPC_CANTDECODERES;
610
611	}
612	rpc_fd_unlock(dgtbl, cu->cu_fd);
613	return (rpc_callerr.re_status);
614}
615
616static enum clnt_stat
617clnt_dg_send(CLIENT *cl, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp)
618{
619/* LINTED pointer alignment */
620	struct cu_data *cu = (struct cu_data *)cl->cl_private;
621	XDR *xdrs;
622	int outlen;
623	struct t_unitdata tu_data;
624	uint32_t x_id;
625
626	if (rpc_fd_lock(dgtbl, cu->cu_fd)) {
627		rpc_callerr.re_status = RPC_FAILED;
628		rpc_callerr.re_errno = errno;
629		rpc_fd_unlock(dgtbl, cu->cu_fd);
630		return (RPC_FAILED);
631	}
632
633	tu_data.addr = cu->cu_raddr;
634
635	xdrs = &(cu->cu_outxdrs);
636	xdrs->x_op = XDR_ENCODE;
637	XDR_SETPOS(xdrs, 0);
638	/*
639	 * Due to little endian byte order, it is necessary to convert to host
640	 * format before incrementing xid.
641	 */
642/* LINTED pointer alignment */
643	x_id = ntohl(*(uint32_t *)(cu->cu_outbuf)) + 1;		/* set XID */
644	/* LINTED pointer cast */
645	*(uint32_t *)cu->cu_outbuf = htonl(x_id);
646
647	if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
648		if ((!XDR_PUTBYTES(xdrs, cu->cu_outbuf, cu->cu_xdrpos)) ||
649				(!XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
650				(!AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
651				(!xargs(xdrs, argsp))) {
652			rpc_fd_unlock(dgtbl, cu->cu_fd);
653			return (rpc_callerr.re_status = RPC_CANTENCODEARGS);
654		}
655	} else {
656/* LINTED pointer alignment */
657		uint32_t *u = (uint32_t *)&cu->cu_outbuf[cu->cu_xdrpos];
658		IXDR_PUT_U_INT32(u, proc);
659		if (!__rpc_gss_wrap(cl->cl_auth, cu->cu_outbuf,
660		    ((char *)u) - cu->cu_outbuf, xdrs, xargs, argsp)) {
661			rpc_fd_unlock(dgtbl, cu->cu_fd);
662			return (rpc_callerr.re_status = RPC_CANTENCODEARGS);
663		}
664	}
665	outlen = (int)XDR_GETPOS(xdrs);
666
667	tu_data.udata.buf = cu->cu_outbuf_start;
668	tu_data.udata.len = outlen;
669	tu_data.opt.len = 0;
670	if (t_sndudata(cu->cu_fd, &tu_data) == -1) {
671		rpc_callerr.re_terrno = t_errno;
672		rpc_callerr.re_errno = errno;
673		rpc_fd_unlock(dgtbl, cu->cu_fd);
674		return (rpc_callerr.re_status = RPC_CANTSEND);
675	}
676
677	rpc_fd_unlock(dgtbl, cu->cu_fd);
678	return (rpc_callerr.re_status = RPC_SUCCESS);
679}
680
681static void
682clnt_dg_geterr(CLIENT *cl, struct rpc_err *errp)
683{
684/* LINTED pointer alignment */
685	struct cu_data *cu = (struct cu_data *)cl->cl_private;
686
687	*errp = rpc_callerr;
688}
689
690static bool_t
691clnt_dg_freeres(CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr)
692{
693/* LINTED pointer alignment */
694	struct cu_data *cu = (struct cu_data *)cl->cl_private;
695	XDR *xdrs = &(cu->cu_outxdrs);
696	bool_t stat;
697
698	(void) rpc_fd_lock(dgtbl, cu->cu_fd);
699	xdrs->x_op = XDR_FREE;
700	stat = (*xdr_res)(xdrs, res_ptr);
701	rpc_fd_unlock(dgtbl, cu->cu_fd);
702	return (stat);
703}
704
705/* ARGSUSED */
706static void
707clnt_dg_abort(CLIENT *h)
708{
709}
710
711static bool_t
712clnt_dg_control(CLIENT *cl, int request, char *info)
713{
714/* LINTED pointer alignment */
715	struct cu_data *cu = (struct cu_data *)cl->cl_private;
716	struct netbuf *addr;
717	if (rpc_fd_lock(dgtbl, cu->cu_fd)) {
718		rpc_fd_unlock(dgtbl, cu->cu_fd);
719		return (RPC_FAILED);
720	}
721
722	switch (request) {
723	case CLSET_FD_CLOSE:
724		cu->cu_closeit = TRUE;
725		rpc_fd_unlock(dgtbl, cu->cu_fd);
726		return (TRUE);
727	case CLSET_FD_NCLOSE:
728		cu->cu_closeit = FALSE;
729		rpc_fd_unlock(dgtbl, cu->cu_fd);
730		return (TRUE);
731	}
732
733	/* for other requests which use info */
734	if (info == NULL) {
735		rpc_fd_unlock(dgtbl, cu->cu_fd);
736		return (FALSE);
737	}
738	switch (request) {
739	case CLSET_TIMEOUT:
740/* LINTED pointer alignment */
741		if (time_not_ok((struct timeval *)info)) {
742			rpc_fd_unlock(dgtbl, cu->cu_fd);
743			return (FALSE);
744		}
745/* LINTED pointer alignment */
746		cu->cu_total = *(struct timeval *)info;
747		break;
748	case CLGET_TIMEOUT:
749/* LINTED pointer alignment */
750		*(struct timeval *)info = cu->cu_total;
751		break;
752	case CLGET_SERVER_ADDR:		/* Give him the fd address */
753		/* Now obsolete. Only for backword compatibility */
754		(void) memcpy(info, cu->cu_raddr.buf, (size_t)cu->cu_raddr.len);
755		break;
756	case CLSET_RETRY_TIMEOUT:
757/* LINTED pointer alignment */
758		if (time_not_ok((struct timeval *)info)) {
759			rpc_fd_unlock(dgtbl, cu->cu_fd);
760			return (FALSE);
761		}
762/* LINTED pointer alignment */
763		cu->cu_wait = *(struct timeval *)info;
764		break;
765	case CLGET_RETRY_TIMEOUT:
766/* LINTED pointer alignment */
767		*(struct timeval *)info = cu->cu_wait;
768		break;
769	case CLGET_FD:
770/* LINTED pointer alignment */
771		*(int *)info = cu->cu_fd;
772		break;
773	case CLGET_SVC_ADDR:
774/* LINTED pointer alignment */
775		*(struct netbuf *)info = cu->cu_raddr;
776		break;
777	case CLSET_SVC_ADDR:		/* set to new address */
778/* LINTED pointer alignment */
779		addr = (struct netbuf *)info;
780		if (cu->cu_raddr.maxlen < addr->len) {
781			free(cu->cu_raddr.buf);
782			if ((cu->cu_raddr.buf = malloc(addr->len)) == NULL) {
783				rpc_fd_unlock(dgtbl, cu->cu_fd);
784				return (FALSE);
785			}
786			cu->cu_raddr.maxlen = addr->len;
787		}
788		cu->cu_raddr.len = addr->len;
789		(void) memcpy(cu->cu_raddr.buf, addr->buf, addr->len);
790		break;
791	case CLGET_XID:
792		/*
793		 * use the knowledge that xid is the
794		 * first element in the call structure *.
795		 * This will get the xid of the PREVIOUS call
796		 */
797/* LINTED pointer alignment */
798		*(uint32_t *)info = ntohl(*(uint32_t *)cu->cu_outbuf);
799		break;
800
801	case CLSET_XID:
802		/* This will set the xid of the NEXT call */
803/* LINTED pointer alignment */
804		*(uint32_t *)cu->cu_outbuf =  htonl(*(uint32_t *)info - 1);
805		/* decrement by 1 as clnt_dg_call() increments once */
806		break;
807
808	case CLGET_VERS:
809		/*
810		 * This RELIES on the information that, in the call body,
811		 * the version number field is the fifth field from the
812		 * begining of the RPC header. MUST be changed if the
813		 * call_struct is changed
814		 */
815/* LINTED pointer alignment */
816		*(uint32_t *)info = ntohl(*(uint32_t *)(cu->cu_outbuf +
817						    4 * BYTES_PER_XDR_UNIT));
818		break;
819
820	case CLSET_VERS:
821/* LINTED pointer alignment */
822		*(uint32_t *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT) =
823/* LINTED pointer alignment */
824			htonl(*(uint32_t *)info);
825		break;
826
827	case CLGET_PROG:
828		/*
829		 * This RELIES on the information that, in the call body,
830		 * the program number field is the fourth field from the
831		 * begining of the RPC header. MUST be changed if the
832		 * call_struct is changed
833		 */
834/* LINTED pointer alignment */
835		*(uint32_t *)info = ntohl(*(uint32_t *)(cu->cu_outbuf +
836						    3 * BYTES_PER_XDR_UNIT));
837		break;
838
839	case CLSET_PROG:
840/* LINTED pointer alignment */
841		*(uint32_t *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT) =
842/* LINTED pointer alignment */
843			htonl(*(uint32_t *)info);
844		break;
845
846	default:
847		rpc_fd_unlock(dgtbl, cu->cu_fd);
848		return (FALSE);
849	}
850	rpc_fd_unlock(dgtbl, cu->cu_fd);
851	return (TRUE);
852}
853
854static void
855clnt_dg_destroy(CLIENT *cl)
856{
857/* LINTED pointer alignment */
858	struct cu_data *cu = (struct cu_data *)cl->cl_private;
859	int cu_fd = cu->cu_fd;
860
861	(void) rpc_fd_lock(dgtbl, cu_fd);
862	if (cu->cu_closeit)
863		(void) t_close(cu_fd);
864	XDR_DESTROY(&(cu->cu_outxdrs));
865	cu->cu_tr_data->udata.buf = NULL;
866	(void) t_free((char *)cu->cu_tr_data, T_UNITDATA);
867	free(cu->cu_raddr.buf);
868	free(cu);
869	if (cl->cl_netid && cl->cl_netid[0])
870		free(cl->cl_netid);
871	if (cl->cl_tp && cl->cl_tp[0])
872		free(cl->cl_tp);
873	free(cl);
874	rpc_fd_unlock(dgtbl, cu_fd);
875}
876
877static struct clnt_ops *
878clnt_dg_ops(void)
879{
880	static struct clnt_ops ops;
881	extern mutex_t	ops_lock;
882
883/* VARIABLES PROTECTED BY ops_lock: ops */
884
885	sig_mutex_lock(&ops_lock);
886	if (ops.cl_call == NULL) {
887		ops.cl_call = clnt_dg_call;
888		ops.cl_send = clnt_dg_send;
889		ops.cl_abort = clnt_dg_abort;
890		ops.cl_geterr = clnt_dg_geterr;
891		ops.cl_freeres = clnt_dg_freeres;
892		ops.cl_destroy = clnt_dg_destroy;
893		ops.cl_control = clnt_dg_control;
894	}
895	sig_mutex_unlock(&ops_lock);
896	return (&ops);
897}
898
899/*
900 * Make sure that the time is not garbage.  -1 value is allowed.
901 */
902static bool_t
903time_not_ok(struct timeval *t)
904{
905	return (t->tv_sec < -1 || t->tv_sec > 100000000 ||
906		t->tv_usec < -1 || t->tv_usec > 1000000);
907}
908
909/*
910 * Receive a unit data error indication.
911 * Below even when t_alloc() fails we pass uderr=NULL to t_rcvuderr()
912 * so as to just clear the error indication.
913 */
914
915static int
916_rcv_unitdata_err(struct cu_data *cu)
917{
918	int old;
919	struct t_uderr *uderr;
920
921	old = t_errno;
922	/* LINTED pointer cast */
923	uderr = (struct t_uderr *)
924		t_alloc(cu->cu_fd, T_UDERROR, T_ADDR);
925
926	if (t_rcvuderr(cu->cu_fd, uderr) == 0) {
927		if (uderr == NULL)
928			return (0);
929
930		if (uderr->addr.len != cu->cu_raddr.len ||
931		    (memcmp(uderr->addr.buf, cu->cu_raddr.buf,
932		    cu->cu_raddr.len))) {
933			(void) t_free((char *)uderr, T_UDERROR);
934			return (0);
935		}
936		rpc_callerr.re_errno = uderr->error;
937		rpc_callerr.re_terrno = TSYSERR;
938		(void) t_free((char *)uderr, T_UDERROR);
939		return (1);
940	}
941	rpc_callerr.re_terrno = old;
942	if (uderr)
943		(void) t_free((char *)uderr, T_UDERROR);
944	return (-1);
945}
946