xref: /illumos-gate/usr/src/ucblib/librpcsoc/clnt_tcp.c (revision 2b208da6458bdbaad9f45832216da636f9184765)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 1990 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * Copyright (c) 1984, 1986, 1987, 1988, 1989, 1996 AT&T
297c478bd9Sstevel@tonic-gate  * All Rights Reserved
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate /*
337c478bd9Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
347c478bd9Sstevel@tonic-gate  * The Regents of the University of California
357c478bd9Sstevel@tonic-gate  * All Rights Reserved
367c478bd9Sstevel@tonic-gate  *
377c478bd9Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
387c478bd9Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
397c478bd9Sstevel@tonic-gate  * contributors.
407c478bd9Sstevel@tonic-gate  */
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate /*
437c478bd9Sstevel@tonic-gate  * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
447c478bd9Sstevel@tonic-gate  *
457c478bd9Sstevel@tonic-gate  * TCP based RPC supports 'batched calls'.
467c478bd9Sstevel@tonic-gate  * A sequence of calls may be batched-up in a send buffer.  The rpc call
477c478bd9Sstevel@tonic-gate  * return immediately to the client even though the call was not necessarily
487c478bd9Sstevel@tonic-gate  * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
497c478bd9Sstevel@tonic-gate  * the rpc timeout value is zero (see clnt.h, rpc).
507c478bd9Sstevel@tonic-gate  *
517c478bd9Sstevel@tonic-gate  * Clients should NOT casually batch calls that in fact return results; that is,
527c478bd9Sstevel@tonic-gate  * the server side should be aware that a call is batched and not produce any
537c478bd9Sstevel@tonic-gate  * return message.  Batched calls that produce many result messages can
547c478bd9Sstevel@tonic-gate  * deadlock (netlock) the client and the server....
557c478bd9Sstevel@tonic-gate  *
567c478bd9Sstevel@tonic-gate  * Now go hang yourself.
577c478bd9Sstevel@tonic-gate  */
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate #include <rpc/rpc.h>
607c478bd9Sstevel@tonic-gate #include <sys/socket.h>
617c478bd9Sstevel@tonic-gate #include <sys/time.h>
627c478bd9Sstevel@tonic-gate #include <netdb.h>
637c478bd9Sstevel@tonic-gate #include <errno.h>
647c478bd9Sstevel@tonic-gate #include <rpc/pmap_clnt.h>
657c478bd9Sstevel@tonic-gate #include <syslog.h>
667c478bd9Sstevel@tonic-gate #include <malloc.h>
677c478bd9Sstevel@tonic-gate #include <stdio.h>
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate #define	MCALL_MSG_SIZE 24
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate extern int errno;
727c478bd9Sstevel@tonic-gate 
73*2b208da6SToomas Soome static int readtcp(void *, caddr_t, int);
74*2b208da6SToomas Soome static int writetcp(void *, caddr_t, int);
757c478bd9Sstevel@tonic-gate extern int _socket(int, int, int);
76*2b208da6SToomas Soome extern pid_t getpid(void);
777c478bd9Sstevel@tonic-gate extern int bindresvport(int, struct sockaddr_in *);
787c478bd9Sstevel@tonic-gate extern bool_t xdr_opaque_auth(XDR *, struct opaque_auth *);
79*2b208da6SToomas Soome static struct clnt_ops *clnttcp_ops(void);
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate struct ct_data {
827c478bd9Sstevel@tonic-gate 	int		ct_sock;
837c478bd9Sstevel@tonic-gate 	bool_t		ct_closeit;
847c478bd9Sstevel@tonic-gate 	struct timeval	ct_wait;
857c478bd9Sstevel@tonic-gate 	bool_t		ct_waitset;	/* wait set by clnt_control? */
867c478bd9Sstevel@tonic-gate 	struct sockaddr_in ct_addr;
877c478bd9Sstevel@tonic-gate 	struct rpc_err	ct_error;
887c478bd9Sstevel@tonic-gate 	char		ct_mcall[MCALL_MSG_SIZE];	/* marshalled callmsg */
89*2b208da6SToomas Soome 	uint_t		ct_mpos;			/* pos after marshal */
907c478bd9Sstevel@tonic-gate 	XDR		ct_xdrs;
917c478bd9Sstevel@tonic-gate };
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate /*
947c478bd9Sstevel@tonic-gate  * Create a client handle for a tcp/ip connection.
957c478bd9Sstevel@tonic-gate  * If *sockp<0, *sockp is set to a newly created TCP socket and it is
967c478bd9Sstevel@tonic-gate  * connected to raddr.  If *sockp non-negative then
977c478bd9Sstevel@tonic-gate  * raddr is ignored.  The rpc/tcp package does buffering
987c478bd9Sstevel@tonic-gate  * similar to stdio, so the client must pick send and receive buffer sizes
997c478bd9Sstevel@tonic-gate  * 0 => use the default.
1007c478bd9Sstevel@tonic-gate  * If raddr->sin_port is 0, then a binder on the remote machine is
1017c478bd9Sstevel@tonic-gate  * consulted for the right port number.
1027c478bd9Sstevel@tonic-gate  * NB: *sockp is copied into a private area.
1037c478bd9Sstevel@tonic-gate  * NB: It is the clients responsibility to close *sockp.
1047c478bd9Sstevel@tonic-gate  * NB: The rpch->cl_auth is set null authentication.  Caller may wish to
1057c478bd9Sstevel@tonic-gate  * set this something more useful.
1067c478bd9Sstevel@tonic-gate  */
1077c478bd9Sstevel@tonic-gate CLIENT *
clnttcp_create(struct sockaddr_in * raddr,rpcprog_t prog,rpcvers_t vers,int * sockp,uint_t sendsz,uint_t recvsz)108*2b208da6SToomas Soome clnttcp_create(struct sockaddr_in *raddr, rpcprog_t prog, rpcvers_t vers,
109*2b208da6SToomas Soome     int *sockp, uint_t sendsz, uint_t recvsz)
1107c478bd9Sstevel@tonic-gate {
1117c478bd9Sstevel@tonic-gate 	CLIENT *h;
112*2b208da6SToomas Soome 	struct ct_data *ct;
1137c478bd9Sstevel@tonic-gate 	struct timeval now;
1147c478bd9Sstevel@tonic-gate 	struct rpc_msg call_msg;
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate 	h  = (CLIENT *)mem_alloc(sizeof (*h));
1177c478bd9Sstevel@tonic-gate 	if (h == NULL) {
1187c478bd9Sstevel@tonic-gate 		(void) syslog(LOG_ERR, "clnttcp_create: out of memory");
1197c478bd9Sstevel@tonic-gate 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
1207c478bd9Sstevel@tonic-gate 		rpc_createerr.cf_error.re_errno = errno;
1217c478bd9Sstevel@tonic-gate 		goto fooy;
1227c478bd9Sstevel@tonic-gate 	}
1237c478bd9Sstevel@tonic-gate 	ct = (struct ct_data *)mem_alloc(sizeof (*ct));
1247c478bd9Sstevel@tonic-gate 	if (ct == NULL) {
1257c478bd9Sstevel@tonic-gate 		(void) syslog(LOG_ERR, "clnttcp_create: out of memory");
1267c478bd9Sstevel@tonic-gate 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
1277c478bd9Sstevel@tonic-gate 		rpc_createerr.cf_error.re_errno = errno;
1287c478bd9Sstevel@tonic-gate 		goto fooy;
1297c478bd9Sstevel@tonic-gate 	}
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	/*
1327c478bd9Sstevel@tonic-gate 	 * If no port number given ask the pmap for one
1337c478bd9Sstevel@tonic-gate 	 */
1347c478bd9Sstevel@tonic-gate 	if (raddr->sin_port == 0) {
135*2b208da6SToomas Soome 		ushort_t port;
1367c478bd9Sstevel@tonic-gate 		if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP))
1377c478bd9Sstevel@tonic-gate 		    == 0) {
1387c478bd9Sstevel@tonic-gate 			mem_free((caddr_t)ct, sizeof (struct ct_data));
1397c478bd9Sstevel@tonic-gate 			mem_free((caddr_t)h, sizeof (CLIENT));
1407c478bd9Sstevel@tonic-gate 			return ((CLIENT *)NULL);
1417c478bd9Sstevel@tonic-gate 		}
1427c478bd9Sstevel@tonic-gate 		raddr->sin_port = htons(port);
1437c478bd9Sstevel@tonic-gate 	}
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate 	/*
1467c478bd9Sstevel@tonic-gate 	 * If no socket given, open one
1477c478bd9Sstevel@tonic-gate 	 */
1487c478bd9Sstevel@tonic-gate 	if (*sockp < 0) {
1497c478bd9Sstevel@tonic-gate 		*sockp = _socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1503c6b90beSToomas Soome 		(void) bindresvport(*sockp, (struct sockaddr_in *)0);
1517c478bd9Sstevel@tonic-gate 		if ((*sockp < 0)||
1527c478bd9Sstevel@tonic-gate 		    (connect(*sockp, (struct sockaddr *)raddr,
1537c478bd9Sstevel@tonic-gate 		    sizeof (*raddr)) < 0)) {
1547c478bd9Sstevel@tonic-gate 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
1557c478bd9Sstevel@tonic-gate 			rpc_createerr.cf_error.re_errno = errno;
1567c478bd9Sstevel@tonic-gate 			(void) close(*sockp);
1577c478bd9Sstevel@tonic-gate 			goto fooy;
1587c478bd9Sstevel@tonic-gate 		}
1597c478bd9Sstevel@tonic-gate 		ct->ct_closeit = TRUE;
1607c478bd9Sstevel@tonic-gate 	} else {
1617c478bd9Sstevel@tonic-gate 		ct->ct_closeit = FALSE;
1627c478bd9Sstevel@tonic-gate 	}
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 	/*
1657c478bd9Sstevel@tonic-gate 	 * Set up private data struct
1667c478bd9Sstevel@tonic-gate 	 */
1677c478bd9Sstevel@tonic-gate 	ct->ct_sock = *sockp;
1687c478bd9Sstevel@tonic-gate 	ct->ct_wait.tv_usec = 0;
1697c478bd9Sstevel@tonic-gate 	ct->ct_waitset = FALSE;
1707c478bd9Sstevel@tonic-gate 	ct->ct_addr = *raddr;
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	/*
1737c478bd9Sstevel@tonic-gate 	 * Initialize call message
1747c478bd9Sstevel@tonic-gate 	 */
1757c478bd9Sstevel@tonic-gate 	(void) gettimeofday(&now, (struct timezone *)0);
1767c478bd9Sstevel@tonic-gate 	call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
1777c478bd9Sstevel@tonic-gate 	call_msg.rm_direction = CALL;
1787c478bd9Sstevel@tonic-gate 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
1797c478bd9Sstevel@tonic-gate 	call_msg.rm_call.cb_prog = prog;
1807c478bd9Sstevel@tonic-gate 	call_msg.rm_call.cb_vers = vers;
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 	/*
1837c478bd9Sstevel@tonic-gate 	 * pre-serialize the staic part of the call msg and stash it away
1847c478bd9Sstevel@tonic-gate 	 */
1857c478bd9Sstevel@tonic-gate 	xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
1867c478bd9Sstevel@tonic-gate 	    XDR_ENCODE);
1877c478bd9Sstevel@tonic-gate 	if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
1887c478bd9Sstevel@tonic-gate 		if (ct->ct_closeit) {
1897c478bd9Sstevel@tonic-gate 			(void) close(*sockp);
1907c478bd9Sstevel@tonic-gate 		}
1917c478bd9Sstevel@tonic-gate 		goto fooy;
1927c478bd9Sstevel@tonic-gate 	}
1937c478bd9Sstevel@tonic-gate 	ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
1947c478bd9Sstevel@tonic-gate 	XDR_DESTROY(&(ct->ct_xdrs));
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	/*
1977c478bd9Sstevel@tonic-gate 	 * Create a client handle which uses xdrrec for serialization
1987c478bd9Sstevel@tonic-gate 	 * and authnone for authentication.
1997c478bd9Sstevel@tonic-gate 	 */
2007c478bd9Sstevel@tonic-gate 	xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
2017c478bd9Sstevel@tonic-gate 	    (caddr_t)ct, readtcp, writetcp);
2027c478bd9Sstevel@tonic-gate 	h->cl_ops = clnttcp_ops();
2037c478bd9Sstevel@tonic-gate 	h->cl_private = (caddr_t)ct;
2047c478bd9Sstevel@tonic-gate 	h->cl_auth = authnone_create();
2057c478bd9Sstevel@tonic-gate 	return (h);
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate fooy:
2087c478bd9Sstevel@tonic-gate 	/*
2097c478bd9Sstevel@tonic-gate 	 * Something goofed, free stuff and barf
2107c478bd9Sstevel@tonic-gate 	 */
2117c478bd9Sstevel@tonic-gate 	mem_free((caddr_t)ct, sizeof (struct ct_data));
2127c478bd9Sstevel@tonic-gate 	mem_free((caddr_t)h, sizeof (CLIENT));
213*2b208da6SToomas Soome 	return (NULL);
2147c478bd9Sstevel@tonic-gate }
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate static enum clnt_stat
clnttcp_call(CLIENT * h,rpcproc_t proc,xdrproc_t xdr_args,caddr_t args_ptr,xdrproc_t xdr_results,caddr_t results_ptr,struct timeval timeout)217*2b208da6SToomas Soome clnttcp_call(CLIENT *h, rpcproc_t proc, xdrproc_t xdr_args, caddr_t args_ptr,
218*2b208da6SToomas Soome     xdrproc_t xdr_results, caddr_t results_ptr, struct timeval timeout)
2197c478bd9Sstevel@tonic-gate {
220*2b208da6SToomas Soome 	struct ct_data *ct;
221*2b208da6SToomas Soome 	XDR *xdrs;
2227c478bd9Sstevel@tonic-gate 	struct rpc_msg reply_msg;
2237c478bd9Sstevel@tonic-gate 	uint32_t x_id;
224*2b208da6SToomas Soome 	uint32_t *msg_x_id;
225*2b208da6SToomas Soome 	bool_t shipnow;
226*2b208da6SToomas Soome 	int refreshes;
227*2b208da6SToomas Soome 
228*2b208da6SToomas Soome 	ct = (struct ct_data *)h->cl_private;
229*2b208da6SToomas Soome 	xdrs = &(ct->ct_xdrs);
230*2b208da6SToomas Soome 	msg_x_id = (uint32_t *)(ct->ct_mcall);	/* yuk */
231*2b208da6SToomas Soome 	refreshes = 2;
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	if (!ct->ct_waitset) {
2347c478bd9Sstevel@tonic-gate 		ct->ct_wait = timeout;
2357c478bd9Sstevel@tonic-gate 	}
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	shipnow =
2387c478bd9Sstevel@tonic-gate 	    (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0 &&
2397c478bd9Sstevel@tonic-gate 	    timeout.tv_usec == 0) ? FALSE : TRUE;
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate call_again:
2427c478bd9Sstevel@tonic-gate 	xdrs->x_op = XDR_ENCODE;
2437c478bd9Sstevel@tonic-gate 	ct->ct_error.re_status = RPC_SUCCESS;
2447c478bd9Sstevel@tonic-gate 	x_id = ntohl(--(*msg_x_id));
2457c478bd9Sstevel@tonic-gate 	if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
2467c478bd9Sstevel@tonic-gate 	    (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
2477c478bd9Sstevel@tonic-gate 	    (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
2487c478bd9Sstevel@tonic-gate 	    (! (*xdr_args)(xdrs, args_ptr))) {
2497c478bd9Sstevel@tonic-gate 		if (ct->ct_error.re_status == RPC_SUCCESS)
2507c478bd9Sstevel@tonic-gate 			ct->ct_error.re_status = RPC_CANTENCODEARGS;
2517c478bd9Sstevel@tonic-gate 		(void) xdrrec_endofrecord(xdrs, TRUE);
2527c478bd9Sstevel@tonic-gate 		return (ct->ct_error.re_status);
2537c478bd9Sstevel@tonic-gate 	}
2547c478bd9Sstevel@tonic-gate 	if (! xdrrec_endofrecord(xdrs, shipnow))
2557c478bd9Sstevel@tonic-gate 		return (ct->ct_error.re_status = RPC_CANTSEND);
2567c478bd9Sstevel@tonic-gate 	if (! shipnow)
2577c478bd9Sstevel@tonic-gate 		return (RPC_SUCCESS);
2587c478bd9Sstevel@tonic-gate 	/*
2597c478bd9Sstevel@tonic-gate 	 * Hack to provide rpc-based message passing
2607c478bd9Sstevel@tonic-gate 	 */
2617c478bd9Sstevel@tonic-gate 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
2627c478bd9Sstevel@tonic-gate 		return (ct->ct_error.re_status = RPC_TIMEDOUT);
2637c478bd9Sstevel@tonic-gate 	}
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	/*
2677c478bd9Sstevel@tonic-gate 	 * Keep receiving until we get a valid transaction id
2687c478bd9Sstevel@tonic-gate 	 */
2697c478bd9Sstevel@tonic-gate 	xdrs->x_op = XDR_DECODE;
2707c478bd9Sstevel@tonic-gate 	while (TRUE) {
2717c478bd9Sstevel@tonic-gate 		reply_msg.acpted_rply.ar_verf = _null_auth;
2727c478bd9Sstevel@tonic-gate 		reply_msg.acpted_rply.ar_results.where = NULL;
2737c478bd9Sstevel@tonic-gate 		reply_msg.acpted_rply.ar_results.proc = xdr_void;
2747c478bd9Sstevel@tonic-gate 		if (! xdrrec_skiprecord(xdrs))
2757c478bd9Sstevel@tonic-gate 			return (ct->ct_error.re_status);
2767c478bd9Sstevel@tonic-gate 			/* now decode and validate the response header */
2777c478bd9Sstevel@tonic-gate 		if (! xdr_replymsg(xdrs, &reply_msg)) {
2787c478bd9Sstevel@tonic-gate 			if (ct->ct_error.re_status == RPC_SUCCESS)
2797c478bd9Sstevel@tonic-gate 				continue;
2807c478bd9Sstevel@tonic-gate 			return (ct->ct_error.re_status);
2817c478bd9Sstevel@tonic-gate 		}
2827c478bd9Sstevel@tonic-gate 		if (reply_msg.rm_xid == x_id)
2837c478bd9Sstevel@tonic-gate 			break;
2847c478bd9Sstevel@tonic-gate 	}
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	/*
2877c478bd9Sstevel@tonic-gate 	 * process header
2887c478bd9Sstevel@tonic-gate 	 */
2897c478bd9Sstevel@tonic-gate 	__seterr_reply(&reply_msg, &(ct->ct_error));
2907c478bd9Sstevel@tonic-gate 	if (ct->ct_error.re_status == RPC_SUCCESS) {
2917c478bd9Sstevel@tonic-gate 		if (! AUTH_VALIDATE(h->cl_auth,
2927c478bd9Sstevel@tonic-gate 		    &reply_msg.acpted_rply.ar_verf)) {
2937c478bd9Sstevel@tonic-gate 			ct->ct_error.re_status = RPC_AUTHERROR;
2947c478bd9Sstevel@tonic-gate 			ct->ct_error.re_why = AUTH_INVALIDRESP;
2957c478bd9Sstevel@tonic-gate 		} else if (! (*xdr_results)(xdrs, results_ptr)) {
2967c478bd9Sstevel@tonic-gate 			if (ct->ct_error.re_status == RPC_SUCCESS)
2977c478bd9Sstevel@tonic-gate 				ct->ct_error.re_status = RPC_CANTDECODERES;
2987c478bd9Sstevel@tonic-gate 		}
2997c478bd9Sstevel@tonic-gate 		/* free verifier ... */
3007c478bd9Sstevel@tonic-gate 		if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
3017c478bd9Sstevel@tonic-gate 			xdrs->x_op = XDR_FREE;
3027c478bd9Sstevel@tonic-gate 			(void) xdr_opaque_auth(xdrs,
3037c478bd9Sstevel@tonic-gate 			    &(reply_msg.acpted_rply.ar_verf));
3047c478bd9Sstevel@tonic-gate 		}
3057c478bd9Sstevel@tonic-gate 	}  /* end successful completion */
3067c478bd9Sstevel@tonic-gate 	else {
3077c478bd9Sstevel@tonic-gate 		/* maybe our credentials need to be refreshed ... */
3087c478bd9Sstevel@tonic-gate 		if (refreshes-- && AUTH_REFRESH(h->cl_auth, &reply_msg))
3097c478bd9Sstevel@tonic-gate 			goto call_again;
3107c478bd9Sstevel@tonic-gate 	}  /* end of unsuccessful completion */
3117c478bd9Sstevel@tonic-gate 	return (ct->ct_error.re_status);
3127c478bd9Sstevel@tonic-gate }
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate static void
clnttcp_geterr(CLIENT * h,struct rpc_err * errp)315*2b208da6SToomas Soome clnttcp_geterr(CLIENT *h, struct rpc_err *errp)
3167c478bd9Sstevel@tonic-gate {
317*2b208da6SToomas Soome 	struct ct_data *ct;
3187c478bd9Sstevel@tonic-gate 
319*2b208da6SToomas Soome 	ct = (struct ct_data *)h->cl_private;
3207c478bd9Sstevel@tonic-gate 	*errp = ct->ct_error;
3217c478bd9Sstevel@tonic-gate }
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate static bool_t
clnttcp_freeres(CLIENT * cl,xdrproc_t xdr_res,caddr_t res_ptr)324*2b208da6SToomas Soome clnttcp_freeres(CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr)
3257c478bd9Sstevel@tonic-gate {
326*2b208da6SToomas Soome 	struct ct_data *ct;
327*2b208da6SToomas Soome 	XDR *xdrs;
3287c478bd9Sstevel@tonic-gate 
329*2b208da6SToomas Soome 	ct = (struct ct_data *)cl->cl_private;
330*2b208da6SToomas Soome 	xdrs = &(ct->ct_xdrs);
3317c478bd9Sstevel@tonic-gate 	xdrs->x_op = XDR_FREE;
3327c478bd9Sstevel@tonic-gate 	return ((*xdr_res)(xdrs, res_ptr));
3337c478bd9Sstevel@tonic-gate }
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate static void
clnttcp_abort(void)336*2b208da6SToomas Soome clnttcp_abort(void)
3377c478bd9Sstevel@tonic-gate {
3387c478bd9Sstevel@tonic-gate }
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate static bool_t
clnttcp_control(CLIENT * cl,int request,char * info)341*2b208da6SToomas Soome clnttcp_control(CLIENT *cl, int request, char *info)
3427c478bd9Sstevel@tonic-gate {
343*2b208da6SToomas Soome 	struct ct_data *ct;
3447c478bd9Sstevel@tonic-gate 
345*2b208da6SToomas Soome 	ct = (struct ct_data *)cl->cl_private;
3467c478bd9Sstevel@tonic-gate 	switch (request) {
3477c478bd9Sstevel@tonic-gate 	case CLSET_TIMEOUT:
3487c478bd9Sstevel@tonic-gate 		ct->ct_wait = *(struct timeval *)info;
3497c478bd9Sstevel@tonic-gate 		ct->ct_waitset = TRUE;
3507c478bd9Sstevel@tonic-gate 		break;
3517c478bd9Sstevel@tonic-gate 	case CLGET_TIMEOUT:
3527c478bd9Sstevel@tonic-gate 		*(struct timeval *)info = ct->ct_wait;
3537c478bd9Sstevel@tonic-gate 		break;
3547c478bd9Sstevel@tonic-gate 	case CLGET_SERVER_ADDR:
3557c478bd9Sstevel@tonic-gate 		*(struct sockaddr_in *)info = ct->ct_addr;
3567c478bd9Sstevel@tonic-gate 		break;
3577c478bd9Sstevel@tonic-gate 	case CLGET_FD:
3587c478bd9Sstevel@tonic-gate 		*(int *)info = ct->ct_sock;
3597c478bd9Sstevel@tonic-gate 		break;
3607c478bd9Sstevel@tonic-gate 	case CLSET_FD_CLOSE:
3617c478bd9Sstevel@tonic-gate 		ct->ct_closeit = TRUE;
3627c478bd9Sstevel@tonic-gate 		break;
3637c478bd9Sstevel@tonic-gate 	case CLSET_FD_NCLOSE:
3647c478bd9Sstevel@tonic-gate 		ct->ct_closeit = FALSE;
3657c478bd9Sstevel@tonic-gate 		break;
3667c478bd9Sstevel@tonic-gate 	default:
3677c478bd9Sstevel@tonic-gate 		return (FALSE);
3687c478bd9Sstevel@tonic-gate 	}
3697c478bd9Sstevel@tonic-gate 	return (TRUE);
3707c478bd9Sstevel@tonic-gate }
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 
3737c478bd9Sstevel@tonic-gate static void
clnttcp_destroy(CLIENT * h)374*2b208da6SToomas Soome clnttcp_destroy(CLIENT *h)
3757c478bd9Sstevel@tonic-gate {
376*2b208da6SToomas Soome 	struct ct_data *ct;
3777c478bd9Sstevel@tonic-gate 
378*2b208da6SToomas Soome 	ct = (struct ct_data *)h->cl_private;
3797c478bd9Sstevel@tonic-gate 	if (ct->ct_closeit) {
3807c478bd9Sstevel@tonic-gate 		(void) close(ct->ct_sock);
3817c478bd9Sstevel@tonic-gate 	}
3827c478bd9Sstevel@tonic-gate 	XDR_DESTROY(&(ct->ct_xdrs));
3837c478bd9Sstevel@tonic-gate 	mem_free((caddr_t)ct, sizeof (struct ct_data));
3847c478bd9Sstevel@tonic-gate 	mem_free((caddr_t)h, sizeof (CLIENT));
3857c478bd9Sstevel@tonic-gate }
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate /*
3887c478bd9Sstevel@tonic-gate  * Interface between xdr serializer and tcp connection.
3897c478bd9Sstevel@tonic-gate  * Behaves like the system calls, read & write, but keeps some error state
3907c478bd9Sstevel@tonic-gate  * around for the rpc level.
3917c478bd9Sstevel@tonic-gate  */
3927c478bd9Sstevel@tonic-gate static int
readtcp(void * data,caddr_t buf,int len)393*2b208da6SToomas Soome readtcp(void *data, caddr_t buf, int len)
3947c478bd9Sstevel@tonic-gate {
3957c478bd9Sstevel@tonic-gate 	fd_set mask;
3967c478bd9Sstevel@tonic-gate 	fd_set readfds;
397*2b208da6SToomas Soome 	struct ct_data *ct;
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	if (len == 0)
4007c478bd9Sstevel@tonic-gate 		return (0);
401*2b208da6SToomas Soome 
402*2b208da6SToomas Soome 	ct = data;
4037c478bd9Sstevel@tonic-gate 	FD_ZERO(&mask);
4047c478bd9Sstevel@tonic-gate 	FD_SET(ct->ct_sock, &mask);
4057c478bd9Sstevel@tonic-gate 	while (TRUE) {
4067c478bd9Sstevel@tonic-gate 		readfds = mask;
4077c478bd9Sstevel@tonic-gate 		switch (select(__rpc_dtbsize(),
4087c478bd9Sstevel@tonic-gate 		    &readfds, NULL, NULL, &(ct->ct_wait))) {
4097c478bd9Sstevel@tonic-gate 		case 0:
4107c478bd9Sstevel@tonic-gate 			ct->ct_error.re_status = RPC_TIMEDOUT;
4117c478bd9Sstevel@tonic-gate 			return (-1);
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 		case -1:
4147c478bd9Sstevel@tonic-gate 			if (errno == EINTR)
4157c478bd9Sstevel@tonic-gate 				continue;
4167c478bd9Sstevel@tonic-gate 			ct->ct_error.re_status = RPC_CANTRECV;
4177c478bd9Sstevel@tonic-gate 			ct->ct_error.re_errno = errno;
4187c478bd9Sstevel@tonic-gate 			return (-1);
4197c478bd9Sstevel@tonic-gate 		}
4207c478bd9Sstevel@tonic-gate 		break;
4217c478bd9Sstevel@tonic-gate 	}
4227c478bd9Sstevel@tonic-gate 	switch (len = read(ct->ct_sock, buf, len)) {
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 	case 0:
4257c478bd9Sstevel@tonic-gate 		/* premature eof */
4267c478bd9Sstevel@tonic-gate 		ct->ct_error.re_errno = ECONNRESET;
4277c478bd9Sstevel@tonic-gate 		ct->ct_error.re_status = RPC_CANTRECV;
4287c478bd9Sstevel@tonic-gate 		len = -1;  /* it's really an error */
4297c478bd9Sstevel@tonic-gate 		break;
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 	case -1:
4327c478bd9Sstevel@tonic-gate 		ct->ct_error.re_errno = errno;
4337c478bd9Sstevel@tonic-gate 		ct->ct_error.re_status = RPC_CANTRECV;
4347c478bd9Sstevel@tonic-gate 		break;
4357c478bd9Sstevel@tonic-gate 	}
4367c478bd9Sstevel@tonic-gate 	return (len);
4377c478bd9Sstevel@tonic-gate }
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate static int
writetcp(void * data,caddr_t buf,int len)440*2b208da6SToomas Soome writetcp(void *data, caddr_t buf, int len)
4417c478bd9Sstevel@tonic-gate {
442*2b208da6SToomas Soome 	struct ct_data *ct;
443*2b208da6SToomas Soome 	int i, cnt;
4447c478bd9Sstevel@tonic-gate 
445*2b208da6SToomas Soome 	ct = data;
4467c478bd9Sstevel@tonic-gate 	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
4477c478bd9Sstevel@tonic-gate 		if ((i = write(ct->ct_sock, buf, cnt)) == -1) {
4487c478bd9Sstevel@tonic-gate 			ct->ct_error.re_errno = errno;
4497c478bd9Sstevel@tonic-gate 			ct->ct_error.re_status = RPC_CANTSEND;
4507c478bd9Sstevel@tonic-gate 			return (-1);
4517c478bd9Sstevel@tonic-gate 		}
4527c478bd9Sstevel@tonic-gate 	}
4537c478bd9Sstevel@tonic-gate 	return (len);
4547c478bd9Sstevel@tonic-gate }
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate static struct clnt_ops *
clnttcp_ops(void)457*2b208da6SToomas Soome clnttcp_ops(void)
4587c478bd9Sstevel@tonic-gate {
4597c478bd9Sstevel@tonic-gate 	static struct clnt_ops ops;
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 	if (ops.cl_call == NULL) {
4627c478bd9Sstevel@tonic-gate 		ops.cl_call = clnttcp_call;
4637c478bd9Sstevel@tonic-gate 		ops.cl_abort = clnttcp_abort;
4647c478bd9Sstevel@tonic-gate 		ops.cl_geterr = clnttcp_geterr;
4657c478bd9Sstevel@tonic-gate 		ops.cl_freeres = clnttcp_freeres;
4667c478bd9Sstevel@tonic-gate 		ops.cl_destroy = clnttcp_destroy;
4677c478bd9Sstevel@tonic-gate 		ops.cl_control = clnttcp_control;
4687c478bd9Sstevel@tonic-gate 	}
4697c478bd9Sstevel@tonic-gate 	return (&ops);
4707c478bd9Sstevel@tonic-gate }
471