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