]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/rpc/clnt_dg.c
Merge vendor lld/docs directory from r337145
[FreeBSD/FreeBSD.git] / lib / libc / rpc / clnt_dg.c
1 /*      $NetBSD: clnt_dg.c,v 1.4 2000/07/14 08:40:41 fvdl Exp $ */
2
3 /*-
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  * Copyright (c) 2009, Sun Microsystems, Inc.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without 
10  * modification, are permitted provided that the following conditions are met:
11  * - Redistributions of source code must retain the above copyright notice, 
12  *   this list of conditions and the following disclaimer.
13  * - Redistributions in binary form must reproduce the above copyright notice, 
14  *   this list of conditions and the following disclaimer in the documentation 
15  *   and/or other materials provided with the distribution.
16  * - Neither the name of Sun Microsystems, Inc. nor the names of its 
17  *   contributors may be used to endorse or promote products derived 
18  *   from this software without specific prior written permission.
19  * 
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 /*
33  * Copyright (c) 1986-1991 by Sun Microsystems Inc. 
34  */
35
36 #if defined(LIBC_SCCS) && !defined(lint)
37 #ident  "@(#)clnt_dg.c  1.23    94/04/22 SMI"
38 static char sccsid[] = "@(#)clnt_dg.c 1.19 89/03/16 Copyr 1988 Sun Micro";
39 #endif
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD$");
42
43 /*
44  * Implements a connectionless client side RPC.
45  */
46
47 #include "namespace.h"
48 #include "reentrant.h"
49 #include <sys/types.h>
50 #include <sys/event.h>
51 #include <sys/time.h>
52 #include <sys/socket.h>
53 #include <sys/ioctl.h>
54 #include <arpa/inet.h>
55 #include <rpc/rpc.h>
56 #include <rpc/rpcsec_gss.h>
57 #include <errno.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <signal.h>
61 #include <unistd.h>
62 #include <err.h>
63 #include "un-namespace.h"
64 #include "rpc_com.h"
65 #include "mt_misc.h"
66
67
68 #ifdef _FREEFALL_CONFIG
69 /*
70  * Disable RPC exponential back-off for FreeBSD.org systems.
71  */
72 #define RPC_MAX_BACKOFF         1 /* second */
73 #else
74 #define RPC_MAX_BACKOFF         30 /* seconds */
75 #endif
76
77
78 static struct clnt_ops *clnt_dg_ops(void);
79 static bool_t time_not_ok(struct timeval *);
80 static enum clnt_stat clnt_dg_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
81             xdrproc_t, void *, struct timeval);
82 static void clnt_dg_geterr(CLIENT *, struct rpc_err *);
83 static bool_t clnt_dg_freeres(CLIENT *, xdrproc_t, void *);
84 static void clnt_dg_abort(CLIENT *);
85 static bool_t clnt_dg_control(CLIENT *, u_int, void *);
86 static void clnt_dg_destroy(CLIENT *);
87
88
89
90
91 /*
92  *      This machinery implements per-fd locks for MT-safety.  It is not
93  *      sufficient to do per-CLIENT handle locks for MT-safety because a
94  *      user may create more than one CLIENT handle with the same fd behind
95  *      it.  Therfore, we allocate an array of flags (dg_fd_locks), protected
96  *      by the clnt_fd_lock mutex, and an array (dg_cv) of condition variables
97  *      similarly protected.  Dg_fd_lock[fd] == 1 => a call is activte on some
98  *      CLIENT handle created for that fd.
99  *      The current implementation holds locks across the entire RPC and reply,
100  *      including retransmissions.  Yes, this is silly, and as soon as this
101  *      code is proven to work, this should be the first thing fixed.  One step
102  *      at a time.
103  */
104 static int      *dg_fd_locks;
105 static cond_t   *dg_cv;
106 #define release_fd_lock(fd, mask) {             \
107         mutex_lock(&clnt_fd_lock);      \
108         dg_fd_locks[fd] = 0;            \
109         mutex_unlock(&clnt_fd_lock);    \
110         thr_sigsetmask(SIG_SETMASK, &(mask), NULL); \
111         cond_signal(&dg_cv[fd]);        \
112 }
113
114 static const char mem_err_clnt_dg[] = "clnt_dg_create: out of memory";
115
116 /* VARIABLES PROTECTED BY clnt_fd_lock: dg_fd_locks, dg_cv */
117
118 #define MCALL_MSG_SIZE 24
119
120 /*
121  * Private data kept per client handle
122  */
123 struct cu_data {
124         int                     cu_fd;          /* connections fd */
125         bool_t                  cu_closeit;     /* opened by library */
126         struct sockaddr_storage cu_raddr;       /* remote address */
127         int                     cu_rlen;
128         struct timeval          cu_wait;        /* retransmit interval */
129         struct timeval          cu_total;       /* total time for the call */
130         struct rpc_err          cu_error;
131         XDR                     cu_outxdrs;
132         u_int                   cu_xdrpos;
133         u_int                   cu_sendsz;      /* send size */
134         char                    cu_outhdr[MCALL_MSG_SIZE];
135         char                    *cu_outbuf;
136         u_int                   cu_recvsz;      /* recv size */
137         int                     cu_async;
138         int                     cu_connect;     /* Use connect(). */
139         int                     cu_connected;   /* Have done connect(). */
140         struct kevent           cu_kin;
141         int                     cu_kq;
142         char                    cu_inbuf[1];
143 };
144
145 /*
146  * Connection less client creation returns with client handle parameters.
147  * Default options are set, which the user can change using clnt_control().
148  * fd should be open and bound.
149  * NB: The rpch->cl_auth is initialized to null authentication.
150  *      Caller may wish to set this something more useful.
151  *
152  * sendsz and recvsz are the maximum allowable packet sizes that can be
153  * sent and received. Normally they are the same, but they can be
154  * changed to improve the program efficiency and buffer allocation.
155  * If they are 0, use the transport default.
156  *
157  * If svcaddr is NULL, returns NULL.
158  *
159  * fd      - open file descriptor
160  * svcaddr - servers address
161  * program - program number
162  * version - version number
163  * sendsz  - buffer recv size
164  * recvsz  - buffer send size
165  */
166 CLIENT *
167 clnt_dg_create(int fd, const struct netbuf *svcaddr, rpcprog_t program,
168     rpcvers_t version, u_int sendsz, u_int recvsz)
169 {
170         CLIENT *cl = NULL;              /* client handle */
171         struct cu_data *cu = NULL;      /* private data */
172         struct timeval now;
173         struct rpc_msg call_msg;
174         sigset_t mask;
175         sigset_t newmask;
176         struct __rpc_sockinfo si;
177         int one = 1;
178
179         sigfillset(&newmask);
180         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
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         call_msg.rm_call.cb_prog = program;
262         call_msg.rm_call.cb_vers = version;
263         xdrmem_create(&(cu->cu_outxdrs), cu->cu_outhdr, MCALL_MSG_SIZE,
264             XDR_ENCODE);
265         if (! xdr_callhdr(&cu->cu_outxdrs, &call_msg)) {
266                 rpc_createerr.cf_stat = RPC_CANTENCODEARGS;  /* XXX */
267                 rpc_createerr.cf_error.re_errno = 0;
268                 goto err2;
269         }
270         cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
271         XDR_DESTROY(&cu->cu_outxdrs);
272         xdrmem_create(&cu->cu_outxdrs, cu->cu_outbuf, sendsz, XDR_ENCODE);
273
274         /* XXX fvdl - do we still want this? */
275 #if 0
276         (void)bindresvport_sa(fd, (struct sockaddr *)svcaddr->buf);
277 #endif
278         _ioctl(fd, FIONBIO, (char *)(void *)&one);
279
280         /*
281          * By default, closeit is always FALSE. It is users responsibility
282          * to do a close on it, else the user may use clnt_control
283          * to let clnt_destroy do it for him/her.
284          */
285         cu->cu_closeit = FALSE;
286         cu->cu_fd = fd;
287         cl->cl_ops = clnt_dg_ops();
288         cl->cl_private = (caddr_t)(void *)cu;
289         cl->cl_auth = authnone_create();
290         cl->cl_tp = NULL;
291         cl->cl_netid = NULL;
292         cu->cu_kq = -1;
293         EV_SET(&cu->cu_kin, cu->cu_fd, EVFILT_READ, EV_ADD, 0, 0, 0);
294         return (cl);
295 err1:
296         warnx(mem_err_clnt_dg);
297         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
298         rpc_createerr.cf_error.re_errno = errno;
299 err2:
300         if (cl) {
301                 mem_free(cl, sizeof (CLIENT));
302                 if (cu)
303                         mem_free(cu, sizeof (*cu) + sendsz + recvsz);
304         }
305         return (NULL);
306 }
307
308 /*
309  * cl       - client handle
310  * proc     - procedure number
311  * xargs    - xdr routine for args
312  * argsp    - pointer to args
313  * xresults - xdr routine for results
314  * resultsp - pointer to results
315  * utimeout - seconds to wait before giving up
316  */
317 static enum clnt_stat
318 clnt_dg_call(CLIENT *cl, rpcproc_t proc, xdrproc_t xargs, void *argsp,
319     xdrproc_t xresults, void *resultsp, struct timeval utimeout)
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         int nretries = 0;               /* number of times we retransmitted */
329         struct timeval timeout;
330         struct timeval retransmit_time;
331         struct timeval next_sendtime, starttime, time_waited, tv;
332         struct timespec ts;
333         struct kevent kv;
334         struct sockaddr *sa;
335         sigset_t mask;
336         sigset_t newmask;
337         socklen_t salen;
338         ssize_t recvlen = 0;
339         int kin_len, n, rpc_lock_value;
340         u_int32_t xid;
341
342         outlen = 0;
343         sigfillset(&newmask);
344         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
345         mutex_lock(&clnt_fd_lock);
346         while (dg_fd_locks[cu->cu_fd])
347                 cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
348         if (__isthreaded)
349                 rpc_lock_value = 1;
350         else
351                 rpc_lock_value = 0;
352         dg_fd_locks[cu->cu_fd] = rpc_lock_value;
353         mutex_unlock(&clnt_fd_lock);
354         if (cu->cu_total.tv_usec == -1) {
355                 timeout = utimeout;     /* use supplied timeout */
356         } else {
357                 timeout = cu->cu_total; /* use default timeout */
358         }
359
360         if (cu->cu_connect && !cu->cu_connected) {
361                 if (_connect(cu->cu_fd, (struct sockaddr *)&cu->cu_raddr,
362                     cu->cu_rlen) < 0) {
363                         cu->cu_error.re_errno = errno;
364                         cu->cu_error.re_status = RPC_CANTSEND;
365                         goto out;
366                 }
367                 cu->cu_connected = 1;
368         }
369         if (cu->cu_connected) {
370                 sa = NULL;
371                 salen = 0;
372         } else {
373                 sa = (struct sockaddr *)&cu->cu_raddr;
374                 salen = cu->cu_rlen;
375         }
376         time_waited.tv_sec = 0;
377         time_waited.tv_usec = 0;
378         retransmit_time = next_sendtime = cu->cu_wait;
379         gettimeofday(&starttime, NULL);
380
381         /* Clean up in case the last call ended in a longjmp(3) call. */
382         if (cu->cu_kq >= 0)
383                 _close(cu->cu_kq);
384         if ((cu->cu_kq = kqueue()) < 0) {
385                 cu->cu_error.re_errno = errno;
386                 cu->cu_error.re_status = RPC_CANTSEND;
387                 goto out;
388         }
389         kin_len = 1;
390
391 call_again:
392         if (cu->cu_async == TRUE && xargs == NULL)
393                 goto get_reply;
394         /*
395          * the transaction is the first thing in the out buffer
396          * XXX Yes, and it's in network byte order, so we should to
397          * be careful when we increment it, shouldn't we.
398          */
399         xid = ntohl(*(u_int32_t *)(void *)(cu->cu_outhdr));
400         xid++;
401         *(u_int32_t *)(void *)(cu->cu_outhdr) = htonl(xid);
402 call_again_same_xid:
403         xdrs = &(cu->cu_outxdrs);
404         xdrs->x_op = XDR_ENCODE;
405         XDR_SETPOS(xdrs, 0);
406
407         if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
408                 if ((! XDR_PUTBYTES(xdrs, cu->cu_outhdr, cu->cu_xdrpos)) ||
409                     (! XDR_PUTINT32(xdrs, &proc)) ||
410                     (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
411                     (! (*xargs)(xdrs, argsp))) {
412                         cu->cu_error.re_status = RPC_CANTENCODEARGS;
413                         goto out;
414                 }
415         } else {
416                 *(uint32_t *) &cu->cu_outhdr[cu->cu_xdrpos] = htonl(proc);
417                 if (!__rpc_gss_wrap(cl->cl_auth, cu->cu_outhdr,
418                         cu->cu_xdrpos + sizeof(uint32_t),
419                         xdrs, xargs, argsp)) {
420                         cu->cu_error.re_status = RPC_CANTENCODEARGS;
421                         goto out;
422                 }
423         }
424         outlen = (size_t)XDR_GETPOS(xdrs);
425
426 send_again:
427         if (_sendto(cu->cu_fd, cu->cu_outbuf, outlen, 0, sa, salen) != outlen) {
428                 cu->cu_error.re_errno = errno;
429                 cu->cu_error.re_status = RPC_CANTSEND;
430                 goto out;
431         }
432
433         /*
434          * Hack to provide rpc-based message passing
435          */
436         if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
437                 cu->cu_error.re_status = RPC_TIMEDOUT;
438                 goto out;
439         }
440
441 get_reply:
442
443         /*
444          * sub-optimal code appears here because we have
445          * some clock time to spare while the packets are in flight.
446          * (We assume that this is actually only executed once.)
447          */
448         reply_msg.acpted_rply.ar_verf = _null_auth;
449         if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS) {
450                 reply_msg.acpted_rply.ar_results.where = resultsp;
451                 reply_msg.acpted_rply.ar_results.proc = xresults;
452         } else {
453                 reply_msg.acpted_rply.ar_results.where = NULL;
454                 reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
455         }
456
457         for (;;) {
458                 /* Decide how long to wait. */
459                 if (timercmp(&next_sendtime, &timeout, <))
460                         timersub(&next_sendtime, &time_waited, &tv);
461                 else
462                         timersub(&timeout, &time_waited, &tv);
463                 if (tv.tv_sec < 0 || tv.tv_usec < 0)
464                         tv.tv_sec = tv.tv_usec = 0;
465                 TIMEVAL_TO_TIMESPEC(&tv, &ts);
466
467                 n = _kevent(cu->cu_kq, &cu->cu_kin, kin_len, &kv, 1, &ts);
468                 /* We don't need to register the event again. */
469                 kin_len = 0;
470
471                 if (n == 1) {
472                         if (kv.flags & EV_ERROR) {
473                                 cu->cu_error.re_errno = kv.data;
474                                 cu->cu_error.re_status = RPC_CANTRECV;
475                                 goto out;
476                         }
477                         /* We have some data now */
478                         do {
479                                 recvlen = _recvfrom(cu->cu_fd, cu->cu_inbuf,
480                                     cu->cu_recvsz, 0, NULL, NULL);
481                         } while (recvlen < 0 && errno == EINTR);
482                         if (recvlen < 0 && errno != EWOULDBLOCK) {
483                                 cu->cu_error.re_errno = errno;
484                                 cu->cu_error.re_status = RPC_CANTRECV;
485                                 goto out;
486                         }
487                         if (recvlen >= sizeof(u_int32_t) &&
488                             (cu->cu_async == TRUE ||
489                             *((u_int32_t *)(void *)(cu->cu_inbuf)) ==
490                             *((u_int32_t *)(void *)(cu->cu_outbuf)))) {
491                                 /* We now assume we have the proper reply. */
492                                 break;
493                         }
494                 }
495                 if (n == -1 && errno != EINTR) {
496                         cu->cu_error.re_errno = errno;
497                         cu->cu_error.re_status = RPC_CANTRECV;
498                         goto out;
499                 }
500                 gettimeofday(&tv, NULL);
501                 timersub(&tv, &starttime, &time_waited);
502
503                 /* Check for timeout. */
504                 if (timercmp(&time_waited, &timeout, >)) {
505                         cu->cu_error.re_status = RPC_TIMEDOUT;
506                         goto out;
507                 }
508
509                 /* Retransmit if necessary. */          
510                 if (timercmp(&time_waited, &next_sendtime, >)) {
511                         /* update retransmit_time */
512                         if (retransmit_time.tv_sec < RPC_MAX_BACKOFF)
513                                 timeradd(&retransmit_time, &retransmit_time,
514                                     &retransmit_time);
515                         timeradd(&next_sendtime, &retransmit_time,
516                             &next_sendtime);
517                         nretries++;
518
519                         /*
520                          * When retransmitting a RPCSEC_GSS message,
521                          * we must use a new sequence number (handled
522                          * by __rpc_gss_wrap above).
523                          */
524                         if (cl->cl_auth->ah_cred.oa_flavor != RPCSEC_GSS)
525                                 goto send_again;
526                         else
527                                 goto call_again_same_xid;
528                 }
529         }
530
531         /*
532          * now decode and validate the response
533          */
534
535         xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)recvlen, XDR_DECODE);
536         ok = xdr_replymsg(&reply_xdrs, &reply_msg);
537         /* XDR_DESTROY(&reply_xdrs);    save a few cycles on noop destroy */
538         if (ok) {
539                 if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
540                         (reply_msg.acpted_rply.ar_stat == SUCCESS))
541                         cu->cu_error.re_status = RPC_SUCCESS;
542                 else
543                         _seterr_reply(&reply_msg, &(cu->cu_error));
544
545                 if (cu->cu_error.re_status == RPC_SUCCESS) {
546                         if (! AUTH_VALIDATE(cl->cl_auth,
547                                             &reply_msg.acpted_rply.ar_verf)) {
548                                 if (nretries &&
549                                     cl->cl_auth->ah_cred.oa_flavor
550                                     == RPCSEC_GSS)
551                                         /*
552                                          * If we retransmitted, its
553                                          * possible that we will
554                                          * receive a reply for one of
555                                          * the earlier transmissions
556                                          * (which will use an older
557                                          * RPCSEC_GSS sequence
558                                          * number). In this case, just
559                                          * go back and listen for a
560                                          * new reply. We could keep a
561                                          * record of all the seq
562                                          * numbers we have transmitted
563                                          * so far so that we could
564                                          * accept a reply for any of
565                                          * them here.
566                                          */
567                                         goto get_reply;
568                                 cu->cu_error.re_status = RPC_AUTHERROR;
569                                 cu->cu_error.re_why = AUTH_INVALIDRESP;
570                         } else {
571                                 if (cl->cl_auth->ah_cred.oa_flavor
572                                     == RPCSEC_GSS) {
573                                         if (!__rpc_gss_unwrap(cl->cl_auth,
574                                                 &reply_xdrs, xresults,
575                                                 resultsp))
576                                                 cu->cu_error.re_status =
577                                                         RPC_CANTDECODERES;
578                                 }
579                         }
580                         if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
581                                 xdrs->x_op = XDR_FREE;
582                                 (void) xdr_opaque_auth(xdrs,
583                                         &(reply_msg.acpted_rply.ar_verf));
584                         }
585                 }               /* end successful completion */
586                 /*
587                  * If unsuccessful AND error is an authentication error
588                  * then refresh credentials and try again, else break
589                  */
590                 else if (cu->cu_error.re_status == RPC_AUTHERROR)
591                         /* maybe our credentials need to be refreshed ... */
592                         if (nrefreshes > 0 &&
593                             AUTH_REFRESH(cl->cl_auth, &reply_msg)) {
594                                 nrefreshes--;
595                                 goto call_again;
596                         }
597                 /* end of unsuccessful completion */
598         }       /* end of valid reply message */
599         else {
600                 cu->cu_error.re_status = RPC_CANTDECODERES;
601
602         }
603 out:
604         if (cu->cu_kq >= 0)
605                 _close(cu->cu_kq);
606         cu->cu_kq = -1;
607         release_fd_lock(cu->cu_fd, mask);
608         return (cu->cu_error.re_status);
609 }
610
611 static void
612 clnt_dg_geterr(CLIENT *cl, struct rpc_err *errp)
613 {
614         struct cu_data *cu = (struct cu_data *)cl->cl_private;
615
616         *errp = cu->cu_error;
617 }
618
619 static bool_t
620 clnt_dg_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr)
621 {
622         struct cu_data *cu = (struct cu_data *)cl->cl_private;
623         XDR *xdrs = &(cu->cu_outxdrs);
624         bool_t dummy;
625         sigset_t mask;
626         sigset_t newmask;
627
628         sigfillset(&newmask);
629         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
630         mutex_lock(&clnt_fd_lock);
631         while (dg_fd_locks[cu->cu_fd])
632                 cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
633         xdrs->x_op = XDR_FREE;
634         dummy = (*xdr_res)(xdrs, res_ptr);
635         mutex_unlock(&clnt_fd_lock);
636         thr_sigsetmask(SIG_SETMASK, &mask, NULL);
637         cond_signal(&dg_cv[cu->cu_fd]);
638         return (dummy);
639 }
640
641 /*ARGSUSED*/
642 static void
643 clnt_dg_abort(CLIENT *h)
644 {
645 }
646
647 static bool_t
648 clnt_dg_control(CLIENT *cl, u_int request, void *info)
649 {
650         struct cu_data *cu = (struct cu_data *)cl->cl_private;
651         struct netbuf *addr;
652         sigset_t mask;
653         sigset_t newmask;
654         int rpc_lock_value;
655
656         sigfillset(&newmask);
657         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
658         mutex_lock(&clnt_fd_lock);
659         while (dg_fd_locks[cu->cu_fd])
660                 cond_wait(&dg_cv[cu->cu_fd], &clnt_fd_lock);
661         if (__isthreaded)
662                 rpc_lock_value = 1;
663         else
664                 rpc_lock_value = 0;
665         dg_fd_locks[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                 *(int *)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_outhdr);
734                 break;
735
736         case CLSET_XID:
737                 /* This will set the xid of the NEXT call */
738                 *(u_int32_t *)(void *)cu->cu_outhdr =
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                  * beginning 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_outhdr +
752                     4 * BYTES_PER_XDR_UNIT));
753                 break;
754
755         case CLSET_VERS:
756                 *(u_int32_t *)(void *)(cu->cu_outhdr + 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                  * beginning 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_outhdr +
769                     3 * BYTES_PER_XDR_UNIT));
770                 break;
771
772         case CLSET_PROG:
773                 *(u_int32_t *)(void *)(cu->cu_outhdr + 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(CLIENT *cl)
792 {
793         struct cu_data *cu = (struct cu_data *)cl->cl_private;
794         int cu_fd = cu->cu_fd;
795         sigset_t mask;
796         sigset_t newmask;
797
798         sigfillset(&newmask);
799         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
800         mutex_lock(&clnt_fd_lock);
801         while (dg_fd_locks[cu_fd])
802                 cond_wait(&dg_cv[cu_fd], &clnt_fd_lock);
803         if (cu->cu_closeit)
804                 (void)_close(cu_fd);
805         if (cu->cu_kq >= 0)
806                 _close(cu->cu_kq);
807         XDR_DESTROY(&(cu->cu_outxdrs));
808         mem_free(cu, (sizeof (*cu) + cu->cu_sendsz + cu->cu_recvsz));
809         if (cl->cl_netid && cl->cl_netid[0])
810                 mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
811         if (cl->cl_tp && cl->cl_tp[0])
812                 mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
813         mem_free(cl, sizeof (CLIENT));
814         mutex_unlock(&clnt_fd_lock);
815         thr_sigsetmask(SIG_SETMASK, &mask, NULL);
816         cond_signal(&dg_cv[cu_fd]);
817 }
818
819 static struct clnt_ops *
820 clnt_dg_ops(void)
821 {
822         static struct clnt_ops ops;
823         sigset_t mask;
824         sigset_t newmask;
825
826 /* VARIABLES PROTECTED BY ops_lock: ops */
827
828         sigfillset(&newmask);
829         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
830         mutex_lock(&ops_lock);
831         if (ops.cl_call == NULL) {
832                 ops.cl_call = clnt_dg_call;
833                 ops.cl_abort = clnt_dg_abort;
834                 ops.cl_geterr = clnt_dg_geterr;
835                 ops.cl_freeres = clnt_dg_freeres;
836                 ops.cl_destroy = clnt_dg_destroy;
837                 ops.cl_control = clnt_dg_control;
838         }
839         mutex_unlock(&ops_lock);
840         thr_sigsetmask(SIG_SETMASK, &mask, NULL);
841         return (&ops);
842 }
843
844 /*
845  * Make sure that the time is not garbage.  -1 value is allowed.
846  */
847 static bool_t
848 time_not_ok(struct timeval *t)
849 {
850         return (t->tv_sec < -1 || t->tv_sec > 100000000 ||
851                 t->tv_usec < -1 || t->tv_usec > 1000000);
852 }
853