359063c09d15f5cd5b6e6e3f974753d9a5202a27
[platform/upstream/libtirpc.git] / src / clnt_vc.c
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 /*
30  * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
31  *
32  * Copyright (C) 1984, Sun Microsystems, Inc.
33  *
34  * TCP based RPC supports 'batched calls'.
35  * A sequence of calls may be batched-up in a send buffer.  The rpc call
36  * return immediately to the client even though the call was not necessarily
37  * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
38  * the rpc timeout value is zero (see clnt.h, rpc).
39  *
40  * Clients should NOT casually batch calls that in fact return results; that is,
41  * the server side should be aware that a call is batched and not produce any
42  * return message.  Batched calls that produce many result messages can
43  * deadlock (netlock) the client and the server....
44  *
45  * Now go hang yourself.
46  */
47 #include <pthread.h>
48
49 #include <reentrant.h>
50 #include <sys/types.h>
51 #include <sys/poll.h>
52 #include <sys/syslog.h>
53 #include <sys/un.h>
54 #include <sys/uio.h>
55 #include <sys/socket.h>
56 #include <arpa/inet.h>
57 #include <assert.h>
58 #include <err.h>
59 #include <errno.h>
60 #include <netdb.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65 #include <signal.h>
66
67 #include <rpc/rpc.h>
68 #include "rpc_com.h"
69
70 #define MCALL_MSG_SIZE 24
71
72 #define CMGROUP_MAX    16
73 #define SCM_CREDS      0x03            /* process creds (struct cmsgcred) */
74
75 /*
76  * Credentials structure, used to verify the identity of a peer
77  * process that has sent us a message. This is allocated by the
78  * peer process but filled in by the kernel. This prevents the
79  * peer from lying about its identity. (Note that cmcred_groups[0]
80  * is the effective GID.)
81  */
82 struct cmsgcred {
83         pid_t   cmcred_pid;             /* PID of sending process */
84         uid_t   cmcred_uid;             /* real UID of sending process */
85         uid_t   cmcred_euid;            /* effective UID of sending process */
86         gid_t   cmcred_gid;             /* real GID of sending process */
87         short   cmcred_ngroups;         /* number or groups */
88         gid_t   cmcred_groups[CMGROUP_MAX];     /* groups */
89 };
90
91 struct cmessage {
92         struct cmsghdr cmsg;
93         struct cmsgcred cmcred;
94 };
95
96 static enum clnt_stat clnt_vc_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
97     xdrproc_t, void *, struct timeval);
98 static void clnt_vc_geterr(CLIENT *, struct rpc_err *);
99 static bool_t clnt_vc_freeres(CLIENT *, xdrproc_t, void *);
100 static void clnt_vc_abort(CLIENT *);
101 static bool_t clnt_vc_control(CLIENT *, u_int, void *);
102 static void clnt_vc_destroy(CLIENT *);
103 static struct clnt_ops *clnt_vc_ops(void);
104 static bool_t time_not_ok(struct timeval *);
105 static int read_vc(void *, void *, int);
106 static int write_vc(void *, void *, int);
107
108 struct ct_data {
109         int             ct_fd;          /* connection's fd */
110         bool_t          ct_closeit;     /* close it on destroy */
111         struct timeval  ct_wait;        /* wait interval in milliseconds */
112         bool_t          ct_waitset;     /* wait set by clnt_control? */
113         struct netbuf   ct_addr;        /* remote addr */
114         struct rpc_err  ct_error;
115         union {
116                 char    ct_mcallc[MCALL_MSG_SIZE];      /* marshalled callmsg */
117                 u_int32_t ct_mcalli;
118         } ct_u;
119         u_int           ct_mpos;        /* pos after marshal */
120         XDR             ct_xdrs;        /* XDR stream */
121 };
122
123 /*
124  *      This machinery implements per-fd locks for MT-safety.  It is not
125  *      sufficient to do per-CLIENT handle locks for MT-safety because a
126  *      user may create more than one CLIENT handle with the same fd behind
127  *      it.  Therfore, we allocate an array of flags (vc_fd_locks), protected
128  *      by the clnt_fd_lock mutex, and an array (vc_cv) of condition variables
129  *      similarly protected.  Vc_fd_lock[fd] == 1 => a call is active on some
130  *      CLIENT handle created for that fd.
131  *      The current implementation holds locks across the entire RPC and reply.
132  *      Yes, this is silly, and as soon as this code is proven to work, this
133  *      should be the first thing fixed.  One step at a time.
134  */
135 static int      *vc_fd_locks;
136 extern mutex_t  clnt_fd_lock;
137 static cond_t   *vc_cv;
138 #define release_fd_lock(fd, mask) {     \
139         mutex_lock(&clnt_fd_lock);      \
140         vc_fd_locks[fd] = 0;            \
141         mutex_unlock(&clnt_fd_lock);    \
142         thr_sigsetmask(SIG_SETMASK, &(mask), (sigset_t *) NULL);        \
143         cond_signal(&vc_cv[fd]);        \
144 }
145
146 static const char clnt_vc_errstr[] = "%s : %s";
147 static const char clnt_vc_str[] = "clnt_vc_create";
148 static const char clnt_read_vc_str[] = "read_vc";
149 static const char __no_mem_str[] = "out of memory";
150
151 /*
152  * Create a client handle for a connection.
153  * Default options are set, which the user can change using clnt_control()'s.
154  * The rpc/vc package does buffering similar to stdio, so the client
155  * must pick send and receive buffer sizes, 0 => use the default.
156  * NB: fd is copied into a private area.
157  * NB: The rpch->cl_auth is set null authentication. Caller may wish to
158  * set this something more useful.
159  *
160  * fd should be an open socket
161  */
162 CLIENT *
163 clnt_vc_create(fd, raddr, prog, vers, sendsz, recvsz)
164         int fd;                         /* open file descriptor */
165         const struct netbuf *raddr;     /* servers address */
166         const rpcprog_t prog;                   /* program number */
167         const rpcvers_t vers;                   /* version number */
168         u_int sendsz;                   /* buffer recv size */
169         u_int recvsz;                   /* buffer send size */
170 {
171         CLIENT *cl;                     /* client handle */
172         struct ct_data *ct = NULL;      /* client handle */
173         struct timeval now;
174         struct rpc_msg call_msg;
175         static u_int32_t disrupt;
176         sigset_t mask;
177         sigset_t newmask;
178         struct sockaddr_storage ss;
179         socklen_t slen;
180         struct __rpc_sockinfo si;
181
182         if (disrupt == 0)
183                 disrupt = (u_int32_t)(long)raddr;
184
185         cl = (CLIENT *)mem_alloc(sizeof (*cl));
186         ct = (struct ct_data *)mem_alloc(sizeof (*ct));
187         if ((cl == (CLIENT *)NULL) || (ct == (struct ct_data *)NULL)) {
188                 (void) syslog(LOG_ERR, clnt_vc_errstr,
189                     clnt_vc_str, __no_mem_str);
190                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
191                 rpc_createerr.cf_error.re_errno = errno;
192                 goto err;
193         }
194         ct->ct_addr.buf = NULL;
195         sigfillset(&newmask);
196         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
197         mutex_lock(&clnt_fd_lock);
198         if (vc_fd_locks == (int *) NULL) {
199                 int cv_allocsz, fd_allocsz;
200                 int dtbsize = __rpc_dtbsize();
201
202                 fd_allocsz = dtbsize * sizeof (int);
203                 vc_fd_locks = (int *) mem_alloc(fd_allocsz);
204                 if (vc_fd_locks == (int *) NULL) {
205                         mutex_unlock(&clnt_fd_lock);
206                         thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
207                         goto err;
208                 } else
209                         memset(vc_fd_locks, '\0', fd_allocsz);
210
211                 assert(vc_cv == (cond_t *) NULL);
212                 cv_allocsz = dtbsize * sizeof (cond_t);
213                 vc_cv = (cond_t *) mem_alloc(cv_allocsz);
214                 if (vc_cv == (cond_t *) NULL) {
215                         mem_free(vc_fd_locks, fd_allocsz);
216                         vc_fd_locks = (int *) NULL;
217                         mutex_unlock(&clnt_fd_lock);
218                         thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
219                         goto err;
220                 } else {
221                         int i;
222
223                         for (i = 0; i < dtbsize; i++)
224                                 cond_init(&vc_cv[i], 0, (void *) 0);
225                 }
226         } else
227                 assert(vc_cv != (cond_t *) NULL);
228
229         /*
230          * XXX - fvdl connecting while holding a mutex?
231          */
232         slen = sizeof ss;
233         if (getpeername(fd, (struct sockaddr *)&ss, &slen) < 0) {
234                 if (errno != ENOTCONN) {
235                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
236                         rpc_createerr.cf_error.re_errno = errno;
237                         mutex_unlock(&clnt_fd_lock);
238                         thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
239                         goto err;
240                 }
241                 if (connect(fd, (struct sockaddr *)raddr->buf, raddr->len) < 0){
242                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
243                         rpc_createerr.cf_error.re_errno = errno;
244                         mutex_unlock(&clnt_fd_lock);
245                         thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
246                         goto err;
247                 }
248         }
249         mutex_unlock(&clnt_fd_lock);
250         if (!__rpc_fd2sockinfo(fd, &si))
251                 goto err;
252         thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
253
254         ct->ct_closeit = FALSE;
255
256         /*
257          * Set up private data struct
258          */
259         ct->ct_fd = fd;
260         ct->ct_wait.tv_usec = 0;
261         ct->ct_waitset = FALSE;
262         ct->ct_addr.buf = malloc(raddr->maxlen);
263         if (ct->ct_addr.buf == NULL)
264                 goto err;
265         memcpy(ct->ct_addr.buf, raddr->buf, raddr->len);
266         ct->ct_addr.len = raddr->len;
267         ct->ct_addr.maxlen = raddr->maxlen;
268
269         /*
270          * Initialize call message
271          */
272         (void)gettimeofday(&now, NULL);
273         call_msg.rm_xid = ((u_int32_t)++disrupt) ^ __RPC_GETXID(&now);
274         call_msg.rm_direction = CALL;
275         call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
276         call_msg.rm_call.cb_prog = (u_int32_t)prog;
277         call_msg.rm_call.cb_vers = (u_int32_t)vers;
278
279         /*
280          * pre-serialize the static part of the call msg and stash it away
281          */
282         xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcallc, MCALL_MSG_SIZE,
283             XDR_ENCODE);
284         if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
285                 if (ct->ct_closeit) {
286                         (void)close(fd);
287                 }
288                 goto err;
289         }
290         ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
291         XDR_DESTROY(&(ct->ct_xdrs));
292
293         /*
294          * Create a client handle which uses xdrrec for serialization
295          * and authnone for authentication.
296          */
297         cl->cl_ops = clnt_vc_ops();
298         cl->cl_private = ct;
299         cl->cl_auth = authnone_create();
300         sendsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
301         recvsz = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
302         xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
303             cl->cl_private, read_vc, write_vc);
304         return (cl);
305
306 err:
307         if (cl) {
308                 if (ct) {
309                         if (ct->ct_addr.len)
310                                 mem_free(ct->ct_addr.buf, ct->ct_addr.len);
311                         mem_free(ct, sizeof (struct ct_data));
312                 }
313                 if (cl)
314                         mem_free(cl, sizeof (CLIENT));
315         }
316         return ((CLIENT *)NULL);
317 }
318
319 static enum clnt_stat
320 clnt_vc_call(cl, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
321         CLIENT *cl;
322         rpcproc_t proc;
323         xdrproc_t xdr_args;
324         void *args_ptr;
325         xdrproc_t xdr_results;
326         void *results_ptr;
327         struct timeval timeout;
328 {
329         struct ct_data *ct = (struct ct_data *) cl->cl_private;
330         XDR *xdrs = &(ct->ct_xdrs);
331         struct rpc_msg reply_msg;
332         u_int32_t x_id;
333         u_int32_t *msg_x_id = &ct->ct_u.ct_mcalli;    /* yuk */
334         bool_t shipnow;
335         int refreshes = 2;
336         sigset_t mask, newmask;
337         int rpc_lock_value;
338
339         assert(cl != NULL);
340
341         sigfillset(&newmask);
342         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
343         mutex_lock(&clnt_fd_lock);
344         while (vc_fd_locks[ct->ct_fd])
345                 cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
346         rpc_lock_value = 1;
347         vc_fd_locks[ct->ct_fd] = rpc_lock_value;
348         mutex_unlock(&clnt_fd_lock);
349         if (!ct->ct_waitset) {
350                 /* If time is not within limits, we ignore it. */
351                 if (time_not_ok(&timeout) == FALSE)
352                         ct->ct_wait = timeout;
353         }
354
355         shipnow =
356             (xdr_results == NULL && timeout.tv_sec == 0
357             && timeout.tv_usec == 0) ? FALSE : TRUE;
358
359 call_again:
360         xdrs->x_op = XDR_ENCODE;
361         ct->ct_error.re_status = RPC_SUCCESS;
362         x_id = ntohl(--(*msg_x_id));
363
364         if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcallc, ct->ct_mpos)) ||
365             (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
366             (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
367             (! (*xdr_args)(xdrs, args_ptr))) {
368                 if (ct->ct_error.re_status == RPC_SUCCESS)
369                         ct->ct_error.re_status = RPC_CANTENCODEARGS;
370                 (void)xdrrec_endofrecord(xdrs, TRUE);
371                 release_fd_lock(ct->ct_fd, mask);
372                 return (ct->ct_error.re_status);
373         }
374         if (! xdrrec_endofrecord(xdrs, shipnow)) {
375                 release_fd_lock(ct->ct_fd, mask);
376                 return (ct->ct_error.re_status = RPC_CANTSEND);
377         }
378         if (! shipnow) {
379                 release_fd_lock(ct->ct_fd, mask);
380                 return (RPC_SUCCESS);
381         }
382         /*
383          * Hack to provide rpc-based message passing
384          */
385         if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
386                 release_fd_lock(ct->ct_fd, mask);
387                 return(ct->ct_error.re_status = RPC_TIMEDOUT);
388         }
389
390
391         /*
392          * Keep receiving until we get a valid transaction id
393          */
394         xdrs->x_op = XDR_DECODE;
395         while (TRUE) {
396                 reply_msg.acpted_rply.ar_verf = _null_auth;
397                 reply_msg.acpted_rply.ar_results.where = NULL;
398                 reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void;
399                 if (! xdrrec_skiprecord(xdrs)) {
400                         release_fd_lock(ct->ct_fd, mask);
401                         return (ct->ct_error.re_status);
402                 }
403                 /* now decode and validate the response header */
404                 if (! xdr_replymsg(xdrs, &reply_msg)) {
405                         if (ct->ct_error.re_status == RPC_SUCCESS)
406                                 continue;
407                         release_fd_lock(ct->ct_fd, mask);
408                         return (ct->ct_error.re_status);
409                 }
410                 if (reply_msg.rm_xid == x_id)
411                         break;
412         }
413
414         /*
415          * process header
416          */
417         _seterr_reply(&reply_msg, &(ct->ct_error));
418         if (ct->ct_error.re_status == RPC_SUCCESS) {
419                 if (! AUTH_VALIDATE(cl->cl_auth,
420                     &reply_msg.acpted_rply.ar_verf)) {
421                         ct->ct_error.re_status = RPC_AUTHERROR;
422                         ct->ct_error.re_why = AUTH_INVALIDRESP;
423                 } else if (! (*xdr_results)(xdrs, results_ptr)) {
424                         if (ct->ct_error.re_status == RPC_SUCCESS)
425                                 ct->ct_error.re_status = RPC_CANTDECODERES;
426                 }
427                 /* free verifier ... */
428                 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
429                         xdrs->x_op = XDR_FREE;
430                         (void)xdr_opaque_auth(xdrs,
431                             &(reply_msg.acpted_rply.ar_verf));
432                 }
433         }  /* end successful completion */
434         else {
435                 /* maybe our credentials need to be refreshed ... */
436                 if (refreshes-- && AUTH_REFRESH(cl->cl_auth, &reply_msg))
437                         goto call_again;
438         }  /* end of unsuccessful completion */
439         release_fd_lock(ct->ct_fd, mask);
440         return (ct->ct_error.re_status);
441 }
442
443 static void
444 clnt_vc_geterr(cl, errp)
445         CLIENT *cl;
446         struct rpc_err *errp;
447 {
448         struct ct_data *ct;
449
450         assert(cl != NULL);
451         assert(errp != NULL);
452
453         ct = (struct ct_data *) cl->cl_private;
454         *errp = ct->ct_error;
455 }
456
457 static bool_t
458 clnt_vc_freeres(cl, xdr_res, res_ptr)
459         CLIENT *cl;
460         xdrproc_t xdr_res;
461         void *res_ptr;
462 {
463         struct ct_data *ct;
464         XDR *xdrs;
465         bool_t dummy;
466         sigset_t mask;
467         sigset_t newmask;
468
469         assert(cl != NULL);
470
471         ct = (struct ct_data *)cl->cl_private;
472         xdrs = &(ct->ct_xdrs);
473
474         sigfillset(&newmask);
475         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
476         mutex_lock(&clnt_fd_lock);
477         while (vc_fd_locks[ct->ct_fd])
478                 cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
479         xdrs->x_op = XDR_FREE;
480         dummy = (*xdr_res)(xdrs, res_ptr);
481         mutex_unlock(&clnt_fd_lock);
482         thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
483         cond_signal(&vc_cv[ct->ct_fd]);
484
485         return dummy;
486 }
487
488 /*ARGSUSED*/
489 static void
490 clnt_vc_abort(cl)
491         CLIENT *cl;
492 {
493 }
494
495 static bool_t
496 clnt_vc_control(cl, request, info)
497         CLIENT *cl;
498         u_int request;
499         void *info;
500 {
501         struct ct_data *ct;
502         void *infop = info;
503         sigset_t mask;
504         sigset_t newmask;
505         int rpc_lock_value;
506
507         assert(cl != NULL);
508
509         ct = (struct ct_data *)cl->cl_private;
510
511         sigfillset(&newmask);
512         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
513         mutex_lock(&clnt_fd_lock);
514         while (vc_fd_locks[ct->ct_fd])
515                 cond_wait(&vc_cv[ct->ct_fd], &clnt_fd_lock);
516         rpc_lock_value = 1;
517         vc_fd_locks[ct->ct_fd] = rpc_lock_value;
518         mutex_unlock(&clnt_fd_lock);
519
520         switch (request) {
521         case CLSET_FD_CLOSE:
522                 ct->ct_closeit = TRUE;
523                 release_fd_lock(ct->ct_fd, mask);
524                 return (TRUE);
525         case CLSET_FD_NCLOSE:
526                 ct->ct_closeit = FALSE;
527                 release_fd_lock(ct->ct_fd, mask);
528                 return (TRUE);
529         default:
530                 break;
531         }
532
533         /* for other requests which use info */
534         if (info == NULL) {
535                 release_fd_lock(ct->ct_fd, mask);
536                 return (FALSE);
537         }
538         switch (request) {
539         case CLSET_TIMEOUT:
540                 if (time_not_ok((struct timeval *)info)) {
541                         release_fd_lock(ct->ct_fd, mask);
542                         return (FALSE);
543                 }
544                 ct->ct_wait = *(struct timeval *)infop;
545                 ct->ct_waitset = TRUE;
546                 break;
547         case CLGET_TIMEOUT:
548                 *(struct timeval *)infop = ct->ct_wait;
549                 break;
550         case CLGET_SERVER_ADDR:
551                 (void) memcpy(info, ct->ct_addr.buf, (size_t)ct->ct_addr.len);
552                 break;
553         case CLGET_FD:
554                 *(int *)info = ct->ct_fd;
555                 break;
556         case CLGET_SVC_ADDR:
557                 /* The caller should not free this memory area */
558                 *(struct netbuf *)info = ct->ct_addr;
559                 break;
560         case CLSET_SVC_ADDR:            /* set to new address */
561                 release_fd_lock(ct->ct_fd, mask);
562                 return (FALSE);
563         case CLGET_XID:
564                 /*
565                  * use the knowledge that xid is the
566                  * first element in the call structure
567                  * This will get the xid of the PREVIOUS call
568                  */
569                 *(u_int32_t *)info =
570                     ntohl(*(u_int32_t *)(void *)&ct->ct_u.ct_mcalli);
571                 break;
572         case CLSET_XID:
573                 /* This will set the xid of the NEXT call */
574                 *(u_int32_t *)(void *)&ct->ct_u.ct_mcalli =
575                     htonl(*((u_int32_t *)info) + 1);
576                 /* increment by 1 as clnt_vc_call() decrements once */
577                 break;
578         case CLGET_VERS:
579                 /*
580                  * This RELIES on the information that, in the call body,
581                  * the version number field is the fifth field from the
582                  * begining of the RPC header. MUST be changed if the
583                  * call_struct is changed
584                  */
585                 *(u_int32_t *)info =
586                     ntohl(*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
587                     4 * BYTES_PER_XDR_UNIT));
588                 break;
589
590         case CLSET_VERS:
591                 *(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
592                     4 * BYTES_PER_XDR_UNIT) =
593                     htonl(*(u_int32_t *)info);
594                 break;
595
596         case CLGET_PROG:
597                 /*
598                  * This RELIES on the information that, in the call body,
599                  * the program number field is the fourth field from the
600                  * begining of the RPC header. MUST be changed if the
601                  * call_struct is changed
602                  */
603                 *(u_int32_t *)info =
604                     ntohl(*(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
605                     3 * BYTES_PER_XDR_UNIT));
606                 break;
607
608         case CLSET_PROG:
609                 *(u_int32_t *)(void *)(ct->ct_u.ct_mcallc +
610                     3 * BYTES_PER_XDR_UNIT) =
611                     htonl(*(u_int32_t *)info);
612                 break;
613
614         default:
615                 release_fd_lock(ct->ct_fd, mask);
616                 return (FALSE);
617         }
618         release_fd_lock(ct->ct_fd, mask);
619         return (TRUE);
620 }
621
622
623 static void
624 clnt_vc_destroy(cl)
625         CLIENT *cl;
626 {
627         struct ct_data *ct = (struct ct_data *) cl->cl_private;
628         int ct_fd = ct->ct_fd;
629         sigset_t mask;
630         sigset_t newmask;
631
632         assert(cl != NULL);
633
634         ct = (struct ct_data *) cl->cl_private;
635
636         sigfillset(&newmask);
637         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
638         mutex_lock(&clnt_fd_lock);
639         while (vc_fd_locks[ct_fd])
640                 cond_wait(&vc_cv[ct_fd], &clnt_fd_lock);
641         if (ct->ct_closeit && ct->ct_fd != -1) {
642                 (void)close(ct->ct_fd);
643         }
644         XDR_DESTROY(&(ct->ct_xdrs));
645         if (ct->ct_addr.buf)
646                 free(ct->ct_addr.buf);
647         mem_free(ct, sizeof(struct ct_data));
648         if (cl->cl_netid && cl->cl_netid[0])
649                 mem_free(cl->cl_netid, strlen(cl->cl_netid) +1);
650         if (cl->cl_tp && cl->cl_tp[0])
651                 mem_free(cl->cl_tp, strlen(cl->cl_tp) +1);
652         mem_free(cl, sizeof(CLIENT));
653         mutex_unlock(&clnt_fd_lock);
654         thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
655         cond_signal(&vc_cv[ct_fd]);
656 }
657
658 /*
659  * Interface between xdr serializer and tcp connection.
660  * Behaves like the system calls, read & write, but keeps some error state
661  * around for the rpc level.
662  */
663 static int
664 read_vc(ctp, buf, len)
665         void *ctp;
666         void *buf;
667         int len;
668 {
669         /*
670         struct sockaddr sa;
671         socklen_t sal;
672         */
673         struct ct_data *ct = (struct ct_data *)ctp;
674         struct pollfd fd;
675         int milliseconds = (int)((ct->ct_wait.tv_sec * 1000) +
676             (ct->ct_wait.tv_usec / 1000));
677
678         if (len == 0)
679                 return (0);
680         fd.fd = ct->ct_fd;
681         fd.events = POLLIN;
682         for (;;) {
683                 switch (poll(&fd, 1, milliseconds)) {
684                 case 0:
685                         ct->ct_error.re_status = RPC_TIMEDOUT;
686                         return (-1);
687
688                 case -1:
689                         if (errno == EINTR)
690                                 continue;
691                         ct->ct_error.re_status = RPC_CANTRECV;
692                         ct->ct_error.re_errno = errno;
693                         return (-1);
694                 }
695                 break;
696         }
697
698         len = read(ct->ct_fd, buf, (size_t)len);
699
700         switch (len) {
701         case 0:
702                 /* premature eof */
703                 ct->ct_error.re_errno = ECONNRESET;
704                 ct->ct_error.re_status = RPC_CANTRECV;
705                 len = -1;  /* it's really an error */
706                 break;
707
708         case -1:
709                 ct->ct_error.re_errno = errno;
710                 ct->ct_error.re_status = RPC_CANTRECV;
711                 break;
712         }
713         return (len);
714 }
715
716 static int
717 write_vc(ctp, buf, len)
718         void *ctp;
719         void *buf;
720         int len;
721 {
722         struct ct_data *ct = (struct ct_data *)ctp;
723         int i = 0, cnt;
724
725         for (cnt = len; cnt > 0; cnt -= i, buf += i) {
726             if ((i = write(ct->ct_fd, buf, (size_t)cnt)) == -1) {
727                 ct->ct_error.re_errno = errno;
728                 ct->ct_error.re_status = RPC_CANTSEND;
729                 return (-1);
730             }
731         }
732         return (len);
733 }
734
735 static struct clnt_ops *
736 clnt_vc_ops()
737 {
738         static struct clnt_ops ops;
739         extern mutex_t  ops_lock;
740         sigset_t mask, newmask;
741
742         /* VARIABLES PROTECTED BY ops_lock: ops */
743
744         sigfillset(&newmask);
745         thr_sigsetmask(SIG_SETMASK, &newmask, &mask);
746         mutex_lock(&ops_lock);
747         if (ops.cl_call == NULL) {
748                 ops.cl_call = clnt_vc_call;
749                 ops.cl_abort = clnt_vc_abort;
750                 ops.cl_geterr = clnt_vc_geterr;
751                 ops.cl_freeres = clnt_vc_freeres;
752                 ops.cl_destroy = clnt_vc_destroy;
753                 ops.cl_control = clnt_vc_control;
754         }
755         mutex_unlock(&ops_lock);
756         thr_sigsetmask(SIG_SETMASK, &(mask), NULL);
757         return (&ops);
758 }
759
760 /*
761  * Make sure that the time is not garbage.   -1 value is disallowed.
762  * Note this is different from time_not_ok in clnt_dg.c
763  */
764 static bool_t
765 time_not_ok(t)
766         struct timeval *t;
767 {
768         return (t->tv_sec <= -1 || t->tv_sec > 100000000 ||
769                 t->tv_usec <= -1 || t->tv_usec > 1000000);
770 }