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