xref: /reactos/dll/3rdparty/libtirpc/src/clnt_dg.c (revision 8a978a17)
1 /*
2  * Copyright (c) 2009, Sun Microsystems, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * - Redistributions of source code must retain the above copyright notice,
8  *   this list of conditions and the following disclaimer.
9  * - Redistributions in binary form must reproduce the above copyright notice,
10  *   this list of conditions and the following disclaimer in the documentation
11  *   and/or other materials provided with the distribution.
12  * - Neither the name of Sun Microsystems, Inc. nor the names of its
13  *   contributors may be used to endorse or promote products derived
14  *   from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 /*
29  * Copyright (c) 1986-1991 by Sun Microsystems Inc.
30  */
31 
32 /*
33  * Implements a connectionless client side RPC.
34  */
35 #include <wintirpc.h>
36 //#include <pthread.h>
37 #include <reentrant.h>
38 #include <sys/types.h>
39 //#include <sys/socket.h>
40 //#include <stdint.h>
41 //#include <sys/poll.h>
42 
43 //#include <sys/time.h>
44 
45 //#include <sys/ioctl.h>
46 #include <rpc/clnt.h>
47 //#include <arpa/inet.h>
48 #include <rpc/rpc.h>
49 #include <rpc/xdr.h>
50 #include <errno.h>
51 #include <stdlib.h>
52 #include <string.h>
53 //#include <signal.h>
54 //#include <unistd.h>
55 //#include <err.h>
56 #include "rpc_com.h"
57 
58 #ifdef IP_RECVERR
59 #include <asm/types.h>
60 #include <linux/errqueue.h>
61 #include <sys/uio.h>
62 #endif
63 
64 
65 #define MAX_DEFAULT_FDS                 20000
66 
67 static struct clnt_ops *clnt_dg_ops(void);
68 static bool_t time_not_ok(struct timeval *);
69 static enum clnt_stat clnt_dg_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
70 	    xdrproc_t, void *, struct timeval);
71 static void clnt_dg_geterr(CLIENT *, struct rpc_err *);
72 static bool_t clnt_dg_freeres(CLIENT *, xdrproc_t, void *);
73 static void clnt_dg_abort(CLIENT *);
74 static bool_t clnt_dg_control(CLIENT *, u_int, void *);
75 static void clnt_dg_destroy(CLIENT *);
76 
77 
78 /*
79  *	This machinery implements per-fd locks for MT-safety.  It is not
80  *	sufficient to do per-CLIENT handle locks for MT-safety because a
81  *	user may create more than one CLIENT handle with the same fd behind
82  *	it.  Therfore, we allocate an array of flags (dg_fd_locks), protected
83  *	by the clnt_fd_lock mutex, and an array (dg_cv) of condition variables
84  *	similarly protected.  Dg_fd_lock[fd] == 1 => a call is activte on some
85  *	CLIENT handle created for that fd.
86  *	The current implementation holds locks across the entire RPC and reply,
87  *	including retransmissions.  Yes, this is silly, and as soon as this
88  *	code is proven to work, this should be the first thing fixed.  One step
89  *	at a time.
90  */
91 static int	*dg_fd_locks;
92 extern mutex_t clnt_fd_lock;
93 static cond_t	*dg_cv;
94 #ifndef _WIN32
95 #define	release_fd_lock(fd, mask) {		\
96 	mutex_lock(&clnt_fd_lock);	\
97 	dg_fd_locks[fd] = 0;		\
98 	mutex_unlock(&clnt_fd_lock);	\
99 	thr_sigsetmask(SIG_SETMASK, &(mask), NULL); \
100 	cond_signal(&dg_cv[fd]);	\
101 }
102 #else
103 /* XXX Needs Windows signal/event stuff XXX */
104 #define	release_fd_lock(fd, mask) {		\
105 	mutex_lock(&clnt_fd_lock);	\
106 	dg_fd_locks[WINSOCK_HANDLE_HASH(fd)] = 0;		\
107 	mutex_unlock(&clnt_fd_lock);	\
108 	\
109 	cond_signal(&dg_cv[WINSOCK_HANDLE_HASH(fd)]);	\
110 }
111 #endif
112 
113 static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
114 
115 /* VARIABLES PROTECTED BY clnt_fd_lock: dg_fd_locks, dg_cv */
116 
117 /*
118  * Private data kept per client handle
119  */
120 struct cu_data {
121 	SOCKET				cu_fd;		/* connections fd */
122 	bool_t				cu_closeit;	/* opened by library */
123 	struct sockaddr_storage	cu_raddr;	/* remote address */
124 	int					cu_rlen;
125 	struct timeval		cu_wait;	/* retransmit interval */
126 	struct timeval		cu_total;	/* total time for the call */
127 	struct rpc_err		cu_error;
128 	XDR					cu_outxdrs;
129 	u_int				cu_xdrpos;
130 	u_int				cu_sendsz;	/* send size */
131 	char				*cu_outbuf;
132 	u_int				cu_recvsz;	/* recv size */
133 	int					cu_async;
134 	int					cu_connect;	/* Use connect(). */
135 	int					cu_connected;	/* Have done connect(). */
136 	char				cu_inbuf[1];
137 };
138 
139 /*
140  * Connection less client creation returns with client handle parameters.
141  * Default options are set, which the user can change using clnt_control().
142  * fd should be open and bound.
143  * NB: The rpch->cl_auth is initialized to null authentication.
144  * 	Caller may wish to set this something more useful.
145  *
146  * sendsz and recvsz are the maximum allowable packet sizes that can be
147  * sent and received. Normally they are the same, but they can be
148  * changed to improve the program efficiency and buffer allocation.
149  * If they are 0, use the transport default.
150  *
151  * If svcaddr is NULL, returns NULL.
152  */
153 CLIENT *
154 clnt_dg_create(fd, svcaddr, program, version, sendsz, recvsz)
155 	SOCKET fd;				/* open file descriptor */
156 	const struct netbuf *svcaddr;	/* servers address */
157 	rpcprog_t program;		/* program number */
158 	rpcvers_t version;		/* version number */
159 	u_int sendsz;			/* buffer recv size */
160 	u_int recvsz;			/* buffer send size */
161 {
162 	CLIENT *cl = NULL;		/* client handle */
163 	struct cu_data *cu = NULL;	/* private data */
164 	struct timeval now;
165 	struct rpc_msg call_msg;
166 #ifndef _WIN32
167 	sigset_t mask;
168 	sigset_t newmask;
169 #else
170 	/* XXX Need Windows signal/event stuff here XXX */
171 #endif
172 	struct __rpc_sockinfo si;
173 	u_long one = 1;
174 
175 #ifndef _WIN32
176 	sigfillset(&newmask);
177 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
178 #else
179 	/* XXX Need Windows signal/event stuff here XXX */
180 #endif
181 	mutex_lock(&clnt_fd_lock);
182 	if (dg_fd_locks == (int *) NULL) {
183 		int cv_allocsz;
184 		size_t fd_allocsz;
185 		int dtbsize = __rpc_dtbsize();
186 
187 		fd_allocsz = dtbsize * sizeof (int);
188 		dg_fd_locks = (int *) mem_alloc(fd_allocsz);
189 		if (dg_fd_locks == (int *) NULL) {
190 			mutex_unlock(&clnt_fd_lock);
191 //			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
192 			goto err1;
193 		} else
194 			memset(dg_fd_locks, 0, fd_allocsz);
195 
196 		cv_allocsz = dtbsize * sizeof (cond_t);
197 		dg_cv = (cond_t *) mem_alloc(cv_allocsz);
198 		if (dg_cv == (cond_t *) NULL) {
199 			mem_free(dg_fd_locks, fd_allocsz);
200 			dg_fd_locks = (int *) NULL;
201 			mutex_unlock(&clnt_fd_lock);
202 //			thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
203 			goto err1;
204 		} else {
205 			int i;
206 
207 			for (i = 0; i < dtbsize; i++)
208 				cond_init(&dg_cv[i], 0, (void *) 0);
209 		}
210 	}
211 
212 	mutex_unlock(&clnt_fd_lock);
213 //	thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
214 
215 	if (svcaddr == NULL) {
216 		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
217 		return (NULL);
218 	}
219 
220 	if (!__rpc_fd2sockinfo(fd, &si)) {
221 		rpc_createerr.cf_stat = RPC_TLIERROR;
222 		rpc_createerr.cf_error.re_errno = 0;
223 		return (NULL);
224 	}
225 	/*
226 	 * Find the receive and the send size
227 	 */
228 	sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
229 	recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
230 	if ((sendsz == 0) || (recvsz == 0)) {
231 		rpc_createerr.cf_stat = RPC_TLIERROR; /* XXX */
232 		rpc_createerr.cf_error.re_errno = 0;
233 		return (NULL);
234 	}
235 
236 	if ((cl = mem_alloc(sizeof (CLIENT))) == NULL)
237 		goto err1;
238 	/*
239 	 * Should be multiple of 4 for XDR.
240 	 */
241 	sendsz = ((sendsz + 3) / 4) * 4;
242 	recvsz = ((recvsz + 3) / 4) * 4;
243 	cu = mem_alloc(sizeof (*cu) + sendsz + recvsz);
244 	if (cu == NULL)
245 		goto err1;
246 	(void) memcpy(&cu->cu_raddr, svcaddr->buf, (size_t)svcaddr->len);
247 	cu->cu_rlen = svcaddr->len;
248 	cu->cu_outbuf = &cu->cu_inbuf[recvsz];
249 	/* Other values can also be set through clnt_control() */
250 	cu->cu_wait.tv_sec = 15;	/* heuristically chosen */
251 	cu->cu_wait.tv_usec = 0;
252 	cu->cu_total.tv_sec = -1;
253 	cu->cu_total.tv_usec = -1;
254 	cu->cu_sendsz = sendsz;
255 	cu->cu_recvsz = recvsz;
256 	cu->cu_async = FALSE;
257 	cu->cu_connect = FALSE;
258 	cu->cu_connected = FALSE;
259 	(void) gettimeofday(&now, NULL);
260 //	call_msg.rm_xid = __RPC_GETXID(&now);
261 
262 	call_msg.rm_xid = ((u_int32_t)_getpid() ^ (u_int32_t)(&now)->tv_sec ^ (u_int32_t)(&now)->tv_usec);
263 	call_msg.rm_call.cb_prog = program;
264 	call_msg.rm_call.cb_vers = version;
265 	xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, sendsz, XDR_ENCODE);
266 	if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
267 		rpc_createerr.cf_stat = RPC_CANTENCODEARGS;  /* XXX */
268 		rpc_createerr.cf_error.re_errno = 0;
269 		goto err2;
270 	}
271 	cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
272 
273 	/* XXX fvdl - do we still want this? */
274 #if 0
275 	(void)bindresvport_sa(fd, (struct sockaddr *)svcaddr->buf);
276 #endif
277 #ifdef IP_RECVERR
278 	{
279 	int on = 1;
280 	setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on));
281 	}
282 #endif
283 	ioctlsocket(fd, FIONBIO, &one);
284 	/*
285 	 * By default, closeit is always FALSE. It is users responsibility
286 	 * to do a close on it, else the user may use clnt_control
287 	 * to let clnt_destroy do it for him/her.
288 	 */
289 	cu->cu_closeit = FALSE;
290 	cu->cu_fd = fd;
291 	cl->cl_ops = clnt_dg_ops();
292 	cl->cl_private = (caddr_t)(void *)cu;
293 	cl->cl_auth = authnone_create();
294 	cl->cl_tp = NULL;
295 	cl->cl_netid = NULL;
296 
297 	return (cl);
298 err1:
299 	//warnx(mem_err_clnt_dg);
300 	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
301 	rpc_createerr.cf_error.re_errno = errno;
302 err2:
303 	if (cl) {
304 		mem_free(cl, sizeof (CLIENT));
305 		if (cu)
306 			mem_free(cu, sizeof (*cu) + sendsz + recvsz);
307 	}
308 	return (NULL);
309 }
310 
311 static enum clnt_stat
312 clnt_dg_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
313 	CLIENT	*cl;			/* client handle */
314 	rpcproc_t	proc;		/* procedure number */
315 	xdrproc_t	xargs;		/* xdr routine for args */
316 	void		*argsp;		/* pointer to args */
317 	xdrproc_t	xresults;	/* xdr routine for results */
318 	void		*resultsp;	/* pointer to results */
319 	struct timeval	utimeout;	/* seconds to wait before giving up */
320 {
321 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
322 	XDR *xdrs;
323 	size_t outlen = 0;
324 	struct rpc_msg reply_msg;
325 	XDR reply_xdrs;
326 	bool_t ok;
327 	int nrefreshes = 2;		/* number of times to refresh cred */
328 	struct timeval timeout;
329         struct pollfd fd;
330 	int total_time, nextsend_time, tv=0;
331 	struct sockaddr *sa;
332 #ifndef _WIN32
333 	sigset_t mask;
334 	sigset_t newmask;
335 #else
336 	/* XXX Need Windows signal/event stuff here XXX */
337 #endif
338 	socklen_t inlen, salen;
339 	ssize_t recvlen = 0;
340 	int rpc_lock_value;
341 	u_int32_t xid, inval, outval;
342 #ifdef __REACTOS__
343 	fd_set infd;
344 #endif
345 
346 	outlen = 0;
347 #ifndef _WIN32
348 	sigfillset(&newmask);
349 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
350 #else
351 		/* XXX Need Windows signal/event stuff here XXX */
352 #endif
353 	mutex_lock(&clnt_fd_lock);
354 	while (dg_fd_locks[WINSOCK_HANDLE_HASH(cu->cu_fd)])
355 		cond_wait(&dg_cv[WINSOCK_HANDLE_HASH(cu->cu_fd)], &clnt_fd_lock);
356 	rpc_lock_value = 1;
357 	dg_fd_locks[WINSOCK_HANDLE_HASH(cu->cu_fd)] = rpc_lock_value;
358 	mutex_unlock(&clnt_fd_lock);
359 	if (cu->cu_total.tv_usec == -1) {
360 		timeout = utimeout;	/* use supplied timeout */
361 	} else {
362 		timeout = cu->cu_total;	/* use default timeout */
363 	}
364 	total_time = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
365 	nextsend_time = cu->cu_wait.tv_sec * 1000 + cu->cu_wait.tv_usec / 1000;
366 
367 	if (cu->cu_connect && !cu->cu_connected) {
368 		if (connect(cu->cu_fd, (struct sockaddr *)&cu->cu_raddr,
369 		    cu->cu_rlen) < 0) {
370 			cu->cu_error.re_errno = errno;
371 			cu->cu_error.re_status = RPC_CANTSEND;
372 			goto out;
373 		}
374 		cu->cu_connected = 1;
375 	}
376 	if (cu->cu_connected) {
377 		sa = NULL;
378 		salen = 0;
379 	} else {
380 		sa = (struct sockaddr *)&cu->cu_raddr;
381 		salen = cu->cu_rlen;
382 	}
383 
384 	/* Clean up in case the last call ended in a longjmp(3) call. */
385 call_again:
386 	xdrs = &(cu->cu_outxdrs);
387 	if (cu->cu_async == TRUE && xargs == NULL)
388 		goto get_reply;
389 	xdrs->x_op = XDR_ENCODE;
390 	XDR_SETPOS(xdrs, cu->cu_xdrpos);
391 	/*
392 	 * the transaction is the first thing in the out buffer
393 	 * XXX Yes, and it's in network byte order, so we should to
394 	 * be careful when we increment it, shouldn't we.
395 	 */
396 	xid = ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf));
397 	xid++;
398 	*(u_int32_t *)(void *)(cu->cu_outbuf) = htonl(xid);
399 
400 	if ((! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
401 	    (! AUTH_MARSHALL(cl->cl_auth, xdrs, NULL)) ||
402 	    (! (*xargs)(xdrs, argsp))) {
403 		cu->cu_error.re_status = RPC_CANTENCODEARGS;
404 		goto out;
405 	}
406 	outlen = (size_t)XDR_GETPOS(xdrs);
407 
408 	/*
409 	 * Hack to provide rpc-based message passing
410 	 */
411 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
412 		cu->cu_error.re_status = RPC_TIMEDOUT;
413 		goto out;
414 	}
415 
416 send_again:
417 	if (total_time <= 0) {
418 		cu->cu_error.re_status = RPC_TIMEDOUT;
419 		goto out;
420 	}
421 	nextsend_time = cu->cu_wait.tv_sec * 1000 + cu->cu_wait.tv_usec / 1000;
422 	if (sendto(cu->cu_fd, cu->cu_outbuf, (int)outlen, 0, sa, salen) != outlen) {
423 		cu->cu_error.re_errno = errno;
424 		cu->cu_error.re_status = RPC_CANTSEND;
425 		goto out;
426 	}
427 
428 get_reply:
429 
430 	/*
431 	 * sub-optimal code appears here because we have
432 	 * some clock time to spare while the packets are in flight.
433 	 * (We assume that this is actually only executed once.)
434 	 */
435 	reply_msg.acpted_rply.ar_verf = _null_auth;
436 	reply_msg.acpted_rply.ar_results.where = resultsp;
437 	reply_msg.acpted_rply.ar_results.proc = xresults;
438 
439         fd.fd = cu->cu_fd;
440         fd.events = POLLIN;
441         fd.revents = 0;
442 	while (total_time > 0) {
443 		tv = total_time < nextsend_time ? total_time : nextsend_time;
444 #ifndef __REACTOS__
445                 switch (poll(&fd, 1, tv)) {
446 #else
447 #ifdef IP_RECVERR
448 #error Not supported!
449 #endif
450                 /* ReactOS: use select instead of poll */
451                 FD_ZERO(&infd);
452                 FD_SET(cu->cu_fd, &infd);
453 
454                 timeout.tv_sec = 0;
455                 timeout.tv_usec = tv * 1000;
456 
457                 switch (select(0, &infd, NULL, NULL, &timeout)) {
458 #endif
459                 case 0:
460 		        total_time -= tv;
461 		        goto send_again;
462 				// XXX CHECK THIS FOR WINDOWS!
463                 case -1:
464                         if (errno == EINTR)
465                                 continue;
466                         cu->cu_error.re_status = RPC_CANTRECV;
467                         cu->cu_error.re_errno = errno;
468                         goto out;
469                 }
470                 break;
471         }
472 #ifdef IP_RECVERR
473       if (fd.revents & POLLERR)
474 	{
475 	  struct msghdr msg;
476 	  struct cmsghdr *cmsg;
477 	  struct sock_extended_err *e;
478 	  struct sockaddr_in err_addr;
479 	  struct sockaddr_in *sin = (struct sockaddr_in *)&cu->cu_raddr;
480 	  struct iovec iov;
481 	  char *cbuf = (char *) alloca (outlen + 256);
482 	  int ret;
483 
484 	  iov.iov_base = cbuf + 256;
485 	  iov.iov_len = outlen;
486 	  msg.msg_name = (void *) &err_addr;
487 	  msg.msg_namelen = sizeof (err_addr);
488 	  msg.msg_iov = &iov;
489 	  msg.msg_iovlen = 1;
490 	  msg.msg_flags = 0;
491 	  msg.msg_control = cbuf;
492 	  msg.msg_controllen = 256;
493 	  ret = recvmsg (cu->cu_fd, &msg, MSG_ERRQUEUE);
494 	  if (ret >= 0
495 	      && memcmp (cbuf + 256, cu->cu_outbuf, ret) == 0
496 	      && (msg.msg_flags & MSG_ERRQUEUE)
497 	      && ((msg.msg_namelen == 0
498 		   && ret >= 12)
499 		  || (msg.msg_namelen == sizeof (err_addr)
500 		      && err_addr.sin_family == AF_INET
501 		      && memcmp (&err_addr.sin_addr, &sin->sin_addr,
502 				 sizeof (err_addr.sin_addr)) == 0
503 		      && err_addr.sin_port == sin->sin_port)))
504 	    for (cmsg = CMSG_FIRSTHDR (&msg); cmsg;
505 		 cmsg = CMSG_NXTHDR (&msg, cmsg))
506 	      if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
507 		{
508 		  e = (struct sock_extended_err *) CMSG_DATA(cmsg);
509 		  cu->cu_error.re_errno = e->ee_errno;
510 		  release_fd_lock(cu->cu_fd, mask);
511 		  return (cu->cu_error.re_status = RPC_CANTRECV);
512 		}
513 	}
514 #endif
515 
516 	/* We have some data now */
517 	do {
518 		recvlen = recvfrom(cu->cu_fd, cu->cu_inbuf,
519 		    cu->cu_recvsz, 0, NULL, NULL);
520 		errno = WSAGetLastError();
521 	} while (recvlen == SOCKET_ERROR && errno == WSAEINTR);
522 	if (recvlen == SOCKET_ERROR && errno != WSAEWOULDBLOCK) {
523 		cu->cu_error.re_errno = errno;
524 		cu->cu_error.re_status = RPC_CANTRECV;
525 		goto out;
526 	}
527 
528 	if (recvlen < sizeof(u_int32_t)) {
529 		total_time -= tv;
530 		goto send_again;
531 	}
532 
533 	if (cu->cu_async == TRUE)
534 		inlen = (socklen_t)recvlen;
535 	else {
536 		memcpy(&inval, cu->cu_inbuf, sizeof(u_int32_t));
537 		memcpy(&outval, cu->cu_outbuf, sizeof(u_int32_t));
538 		if (inval != outval) {
539 			total_time -= tv;
540 			goto send_again;
541 		}
542 		inlen = (socklen_t)recvlen;
543 	}
544 
545 	/*
546 	 * now decode and validate the response
547 	 */
548 
549 	xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)recvlen, XDR_DECODE);
550 	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
551 	/* XDR_DESTROY(&reply_xdrs);	save a few cycles on noop destroy */
552 	if (ok) {
553 		if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
554 			(reply_msg.acpted_rply.ar_stat == SUCCESS))
555 			cu->cu_error.re_status = RPC_SUCCESS;
556 		else
557 			_seterr_reply(&reply_msg, &(cu->cu_error));
558 
559 		if (cu->cu_error.re_status == RPC_SUCCESS) {
560 			if (! AUTH_VALIDATE(cl->cl_auth,
561 					    &reply_msg.acpted_rply.ar_verf, 0)) {
562 				cu->cu_error.re_status = RPC_AUTHERROR;
563 				cu->cu_error.re_why = AUTH_INVALIDRESP;
564 			}
565 			if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
566 				xdrs->x_op = XDR_FREE;
567 				(void) xdr_opaque_auth(xdrs,
568 					&(reply_msg.acpted_rply.ar_verf));
569 			}
570 		}		/* end successful completion */
571 		/*
572 		 * If unsuccesful AND error is an authentication error
573 		 * then refresh credentials and try again, else break
574 		 */
575 		else if (cu->cu_error.re_status == RPC_AUTHERROR)
576 			/* maybe our credentials need to be refreshed ... */
577 			if (nrefreshes > 0 &&
578 			    AUTH_REFRESH(cl->cl_auth, &reply_msg)) {
579 				nrefreshes--;
580 				goto call_again;
581 			}
582 		/* end of unsuccessful completion */
583 	}	/* end of valid reply message */
584 	else {
585 		cu->cu_error.re_status = RPC_CANTDECODERES;
586 
587 	}
588 out:
589 	release_fd_lock(cu->cu_fd, mask);
590 	return (cu->cu_error.re_status);
591 }
592 
593 static void
594 clnt_dg_geterr(cl, errp)
595 	CLIENT *cl;
596 	struct rpc_err *errp;
597 {
598 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
599 
600 	*errp = cu->cu_error;
601 }
602 
603 static bool_t
604 clnt_dg_freeres(cl, xdr_res, res_ptr)
605 	CLIENT *cl;
606 	xdrproc_t xdr_res;
607 	void *res_ptr;
608 {
609 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
610 	XDR *xdrs = &(cu->cu_outxdrs);
611 	bool_t dummy;
612 #ifndef _WIN32
613 	sigset_t mask;
614 	sigset_t newmask;
615 
616 	sigfillset(&newmask);
617 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
618 #else
619 	/* XXX Need Windows signal/event stuff here XXX */
620 #endif
621 	mutex_lock(&clnt_fd_lock);
622 	while (dg_fd_locks[WINSOCK_HANDLE_HASH(cu->cu_fd)])
623 		cond_wait(&dg_cv[WINSOCK_HANDLE_HASH(cu->cu_fd)], &clnt_fd_lock);
624 	xdrs->x_op = XDR_FREE;
625 	dummy = (*xdr_res)(xdrs, res_ptr);
626 	mutex_unlock(&clnt_fd_lock);
627 //	thr_sigsetmask(SIG_SETMASK, &mask, NULL);
628 	cond_signal(&dg_cv[WINSOCK_HANDLE_HASH(cu->cu_fd)]);
629 	return (dummy);
630 }
631 
632 /*ARGSUSED*/
633 static void
634 clnt_dg_abort(h)
635 	CLIENT *h;
636 {
637 }
638 
639 static bool_t
640 clnt_dg_control(cl, request, info)
641 	CLIENT *cl;
642 	u_int request;
643 	void *info;
644 {
645 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
646 	struct netbuf *addr;
647 #ifndef _WIN32
648 	sigset_t mask;
649 	sigset_t newmask;
650 #else
651 	/* XXX Need Windows signal/event stuff here XXX */
652 #endif
653 	int rpc_lock_value;
654 
655 #ifndef _WIN32
656 	sigfillset(&newmask);
657 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
658 #else
659 	/* XXX Need Windows signal/event stuff here XXX */
660 #endif
661 	mutex_lock(&clnt_fd_lock);
662 	while (dg_fd_locks[WINSOCK_HANDLE_HASH(cu->cu_fd)])
663 		cond_wait(&dg_cv[WINSOCK_HANDLE_HASH(cu->cu_fd)], &clnt_fd_lock);
664     rpc_lock_value = 1;
665 	dg_fd_locks[WINSOCK_HANDLE_HASH(cu->cu_fd)] = rpc_lock_value;
666 	mutex_unlock(&clnt_fd_lock);
667 	switch (request) {
668 	case CLSET_FD_CLOSE:
669 		cu->cu_closeit = TRUE;
670 		release_fd_lock(cu->cu_fd, mask);
671 		return (TRUE);
672 	case CLSET_FD_NCLOSE:
673 		cu->cu_closeit = FALSE;
674 		release_fd_lock(cu->cu_fd, mask);
675 		return (TRUE);
676 	}
677 
678 	/* for other requests which use info */
679 	if (info == NULL) {
680 		release_fd_lock(cu->cu_fd, mask);
681 		return (FALSE);
682 	}
683 	switch (request) {
684 	case CLSET_TIMEOUT:
685 		if (time_not_ok((struct timeval *)info)) {
686 			release_fd_lock(cu->cu_fd, mask);
687 			return (FALSE);
688 		}
689 		cu->cu_total = *(struct timeval *)info;
690 		break;
691 	case CLGET_TIMEOUT:
692 		*(struct timeval *)info = cu->cu_total;
693 		break;
694 	case CLGET_SERVER_ADDR:		/* Give him the fd address */
695 		/* Now obsolete. Only for backward compatibility */
696 		(void) memcpy(info, &cu->cu_raddr, (size_t)cu->cu_rlen);
697 		break;
698 	case CLSET_RETRY_TIMEOUT:
699 		if (time_not_ok((struct timeval *)info)) {
700 			release_fd_lock(cu->cu_fd, mask);
701 			return (FALSE);
702 		}
703 		cu->cu_wait = *(struct timeval *)info;
704 		break;
705 	case CLGET_RETRY_TIMEOUT:
706 		*(struct timeval *)info = cu->cu_wait;
707 		break;
708 	case CLGET_FD:
709 		*(SOCKET *)info = cu->cu_fd;
710 		break;
711 	case CLGET_SVC_ADDR:
712 		addr = (struct netbuf *)info;
713 		addr->buf = &cu->cu_raddr;
714 		addr->len = cu->cu_rlen;
715 		addr->maxlen = sizeof cu->cu_raddr;
716 		break;
717 	case CLSET_SVC_ADDR:		/* set to new address */
718 		addr = (struct netbuf *)info;
719 		if (addr->len < sizeof cu->cu_raddr) {
720 			release_fd_lock(cu->cu_fd, mask);
721 			return (FALSE);
722 		}
723 		(void) memcpy(&cu->cu_raddr, addr->buf, addr->len);
724 		cu->cu_rlen = addr->len;
725 		break;
726 	case CLGET_XID:
727 		/*
728 		 * use the knowledge that xid is the
729 		 * first element in the call structure *.
730 		 * This will get the xid of the PREVIOUS call
731 		 */
732 		*(u_int32_t *)info =
733 		    ntohl(*(u_int32_t *)(void *)cu->cu_outbuf);
734 		break;
735 
736 	case CLSET_XID:
737 		/* This will set the xid of the NEXT call */
738 		*(u_int32_t *)(void *)cu->cu_outbuf =
739 		    htonl(*(u_int32_t *)info - 1);
740 		/* decrement by 1 as clnt_dg_call() increments once */
741 		break;
742 
743 	case CLGET_VERS:
744 		/*
745 		 * This RELIES on the information that, in the call body,
746 		 * the version number field is the fifth field from the
747 		 * begining of the RPC header. MUST be changed if the
748 		 * call_struct is changed
749 		 */
750 		*(u_int32_t *)info =
751 		    ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
752 		    4 * BYTES_PER_XDR_UNIT));
753 		break;
754 
755 	case CLSET_VERS:
756 		*(u_int32_t *)(void *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
757 			= htonl(*(u_int32_t *)info);
758 		break;
759 
760 	case CLGET_PROG:
761 		/*
762 		 * This RELIES on the information that, in the call body,
763 		 * the program number field is the fourth field from the
764 		 * begining of the RPC header. MUST be changed if the
765 		 * call_struct is changed
766 		 */
767 		*(u_int32_t *)info =
768 		    ntohl(*(u_int32_t *)(void *)(cu->cu_outbuf +
769 		    3 * BYTES_PER_XDR_UNIT));
770 		break;
771 
772 	case CLSET_PROG:
773 		*(u_int32_t *)(void *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
774 			= htonl(*(u_int32_t *)info);
775 		break;
776 	case CLSET_ASYNC:
777 		cu->cu_async = *(int *)info;
778 		break;
779 	case CLSET_CONNECT:
780 		cu->cu_connect = *(int *)info;
781 		break;
782 	default:
783 		release_fd_lock(cu->cu_fd, mask);
784 		return (FALSE);
785 	}
786 	release_fd_lock(cu->cu_fd, mask);
787 	return (TRUE);
788 }
789 
790 static void
791 clnt_dg_destroy(cl)
792 	CLIENT *cl;
793 {
794 	struct cu_data *cu = (struct cu_data *)cl->cl_private;
795 	SOCKET cu_fd = cu->cu_fd;
796 #ifndef _WIN32
797 	sigset_t mask;
798 	sigset_t newmask;
799 
800 	sigfillset(&newmask);
801 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
802 #else
803 	/* XXX Need Windows signal/event stuff here XXX */
804 #endif
805 	mutex_lock(&clnt_fd_lock);
806 	while (dg_fd_locks[WINSOCK_HANDLE_HASH(cu_fd)])
807 		cond_wait(&dg_cv[WINSOCK_HANDLE_HASH(cu_fd)], &clnt_fd_lock);
808 	if (cu->cu_closeit)
809 		(void)closesocket(cu_fd);
810 	XDR_DESTROY(&(cu->cu_outxdrs));
811 	mem_free(cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
812 	if (cl->cl_netid && cl->cl_netid[0])
813 		mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
814 	if (cl->cl_tp && cl->cl_tp[0])
815 		mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
816 	mem_free(cl, sizeof (CLIENT));
817 	mutex_unlock(&clnt_fd_lock);
818 //	thr_sigsetmask(SIG_SETMASK, &mask, NULL);
819 	cond_signal(&dg_cv[WINSOCK_HANDLE_HASH(cu_fd)]);
820 }
821 
822 static struct clnt_ops *
823 clnt_dg_ops()
824 {
825 	static struct clnt_ops ops;
826 	extern mutex_t	ops_lock;
827 #ifndef _WIN32
828 	sigset_t mask;
829 	sigset_t newmask;
830 
831 /* VARIABLES PROTECTED BY ops_lock: ops */
832 
833 	sigfillset(&newmask);
834 	thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
835 #else
836 	/* XXX Need Windows signal/event stuff here XXX */
837 #endif
838 	mutex_lock(&ops_lock);
839 	if (ops.cl_call == NULL) {
840 		ops.cl_call = clnt_dg_call;
841 		ops.cl_abort = clnt_dg_abort;
842 		ops.cl_geterr = clnt_dg_geterr;
843 		ops.cl_freeres = clnt_dg_freeres;
844 		ops.cl_destroy = clnt_dg_destroy;
845 		ops.cl_control = clnt_dg_control;
846 	}
847 	mutex_unlock(&ops_lock);
848 //	thr_sigsetmask(SIG_SETMASK, &mask, NULL);
849 	return (&ops);
850 }
851 
852 /*
853  * Make sure that the time is not garbage.  -1 value is allowed.
854  */
855 static bool_t
856 time_not_ok(t)
857 	struct timeval *t;
858 {
859 	return (t->tv_sec < -1 || t->tv_sec > 100000000 ||
860 		t->tv_usec < -1 || t->tv_usec > 1000000);
861 }
862 
863