xref: /onnv-gate/usr/src/stand/lib/fs/nfs/rpc.c (revision 0)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  *
26*0Sstevel@tonic-gate  * This file contains a simple implementation of RPC. Standard XDR is
27*0Sstevel@tonic-gate  * used.
28*0Sstevel@tonic-gate  */
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate #include <sys/sysmacros.h>
33*0Sstevel@tonic-gate #include <rpc/types.h>
34*0Sstevel@tonic-gate #include <errno.h>
35*0Sstevel@tonic-gate #include <sys/socket.h>
36*0Sstevel@tonic-gate #include <netinet/in.h>
37*0Sstevel@tonic-gate #include "socket_inet.h"
38*0Sstevel@tonic-gate #include "ipv4.h"
39*0Sstevel@tonic-gate #include <rpc/xdr.h>
40*0Sstevel@tonic-gate #include <rpc/auth.h>
41*0Sstevel@tonic-gate #include <rpc/auth_sys.h>
42*0Sstevel@tonic-gate #include <rpc/rpc_msg.h>
43*0Sstevel@tonic-gate #include <sys/t_lock.h>
44*0Sstevel@tonic-gate #include <netdb.h>
45*0Sstevel@tonic-gate #include "clnt.h"
46*0Sstevel@tonic-gate #include <rpc/rpc.h>
47*0Sstevel@tonic-gate #include "brpc.h"
48*0Sstevel@tonic-gate #include "auth_inet.h"
49*0Sstevel@tonic-gate #include "pmap.h"
50*0Sstevel@tonic-gate #include <sys/promif.h>
51*0Sstevel@tonic-gate #include "nfs_inet.h"
52*0Sstevel@tonic-gate #include <rpcsvc/nfs_prot.h>
53*0Sstevel@tonic-gate #include <rpc/auth_unix.h>
54*0Sstevel@tonic-gate #include <sys/salib.h>
55*0Sstevel@tonic-gate #include "mac.h"
56*0Sstevel@tonic-gate #include <sys/bootdebug.h>
57*0Sstevel@tonic-gate 
58*0Sstevel@tonic-gate #define	dprintf	if (boothowto & RB_DEBUG) printf
59*0Sstevel@tonic-gate 
60*0Sstevel@tonic-gate static struct in_addr cached_destination;
61*0Sstevel@tonic-gate 
62*0Sstevel@tonic-gate void
63*0Sstevel@tonic-gate rpc_disperr(struct rpc_err *stat)
64*0Sstevel@tonic-gate {
65*0Sstevel@tonic-gate 	if (boothowto & RB_DEBUG) {
66*0Sstevel@tonic-gate 		switch (stat->re_status) {
67*0Sstevel@tonic-gate 		case RPC_CANTENCODEARGS:
68*0Sstevel@tonic-gate 			printf("RPC: Can't encode arguments.\n");
69*0Sstevel@tonic-gate 			break;
70*0Sstevel@tonic-gate 		case RPC_CANTDECODERES:
71*0Sstevel@tonic-gate 			printf("RPC: Can't decode result.\n");
72*0Sstevel@tonic-gate 			break;
73*0Sstevel@tonic-gate 		case RPC_CANTSEND:
74*0Sstevel@tonic-gate 			printf("RPC: Unable to send (%s).\n",
75*0Sstevel@tonic-gate 			    strerror(errno));
76*0Sstevel@tonic-gate 			break;
77*0Sstevel@tonic-gate 		case RPC_CANTRECV:
78*0Sstevel@tonic-gate 			printf("RPC: Unable to receive (%s).\n",
79*0Sstevel@tonic-gate 			    strerror(errno));
80*0Sstevel@tonic-gate 			break;
81*0Sstevel@tonic-gate 		case RPC_TIMEDOUT:
82*0Sstevel@tonic-gate 			printf("RPC: Timed out.\n");
83*0Sstevel@tonic-gate 			break;
84*0Sstevel@tonic-gate 		case RPC_VERSMISMATCH:
85*0Sstevel@tonic-gate 			printf("RPC: Incompatible versions of RPC.\n");
86*0Sstevel@tonic-gate 			break;
87*0Sstevel@tonic-gate 		case RPC_AUTHERROR:
88*0Sstevel@tonic-gate 			printf("RPC: Authentication error:\n");
89*0Sstevel@tonic-gate 			switch (stat->re_why) {
90*0Sstevel@tonic-gate 			case AUTH_BADCRED:
91*0Sstevel@tonic-gate 				printf("remote: bogus credentials "
92*0Sstevel@tonic-gate 				    "(seal broken).\n");
93*0Sstevel@tonic-gate 				break;
94*0Sstevel@tonic-gate 			case AUTH_REJECTEDCRED:
95*0Sstevel@tonic-gate 				printf("remote: client should begin new "
96*0Sstevel@tonic-gate 				    "session.\n");
97*0Sstevel@tonic-gate 				break;
98*0Sstevel@tonic-gate 			case AUTH_BADVERF:
99*0Sstevel@tonic-gate 				printf("remote: bogus verifier "
100*0Sstevel@tonic-gate 				    "(seal broken).\n");
101*0Sstevel@tonic-gate 				break;
102*0Sstevel@tonic-gate 			case AUTH_REJECTEDVERF:
103*0Sstevel@tonic-gate 				printf("remote: verifier expired or was "
104*0Sstevel@tonic-gate 				    "replayed.\n");
105*0Sstevel@tonic-gate 				break;
106*0Sstevel@tonic-gate 			case AUTH_TOOWEAK:
107*0Sstevel@tonic-gate 				printf("remote: rejected due to security "
108*0Sstevel@tonic-gate 				    "reasons.\n");
109*0Sstevel@tonic-gate 				break;
110*0Sstevel@tonic-gate 			case AUTH_INVALIDRESP:
111*0Sstevel@tonic-gate 				printf("local: bogus response verifier.\n");
112*0Sstevel@tonic-gate 				break;
113*0Sstevel@tonic-gate 			case AUTH_FAILED:
114*0Sstevel@tonic-gate 				/* FALLTHRU */
115*0Sstevel@tonic-gate 			default:
116*0Sstevel@tonic-gate 				printf("local: unknown error.\n");
117*0Sstevel@tonic-gate 				break;
118*0Sstevel@tonic-gate 			}
119*0Sstevel@tonic-gate 			break;
120*0Sstevel@tonic-gate 		case RPC_PROGUNAVAIL:
121*0Sstevel@tonic-gate 			printf("RPC: Program unavailable.\n");
122*0Sstevel@tonic-gate 			break;
123*0Sstevel@tonic-gate 		case RPC_PROGVERSMISMATCH:
124*0Sstevel@tonic-gate 			printf("RPC: Program/version mismatch.\n");
125*0Sstevel@tonic-gate 			break;
126*0Sstevel@tonic-gate 		case RPC_PROCUNAVAIL:
127*0Sstevel@tonic-gate 			printf("RPC: Procedure unavailable.\n");
128*0Sstevel@tonic-gate 			break;
129*0Sstevel@tonic-gate 		case RPC_CANTDECODEARGS:
130*0Sstevel@tonic-gate 			printf("RPC: Server can't decode arguments.\n");
131*0Sstevel@tonic-gate 			break;
132*0Sstevel@tonic-gate 		case RPC_SYSTEMERROR:
133*0Sstevel@tonic-gate 			printf("RPC: Remote system error.\n");
134*0Sstevel@tonic-gate 			break;
135*0Sstevel@tonic-gate 		case RPC_UNKNOWNHOST:
136*0Sstevel@tonic-gate 			printf("RPC: Unknown host.\n");
137*0Sstevel@tonic-gate 			break;
138*0Sstevel@tonic-gate 		case RPC_UNKNOWNPROTO:
139*0Sstevel@tonic-gate 			printf("RPC: Unknown protocol.\n");
140*0Sstevel@tonic-gate 			break;
141*0Sstevel@tonic-gate 		case RPC_PMAPFAILURE:
142*0Sstevel@tonic-gate 			printf("RPC: Port mapper failure.\n");
143*0Sstevel@tonic-gate 			break;
144*0Sstevel@tonic-gate 		case RPC_PROGNOTREGISTERED:
145*0Sstevel@tonic-gate 			printf("RPC: Program not registered.\n");
146*0Sstevel@tonic-gate 			break;
147*0Sstevel@tonic-gate 		case RPC_FAILED:
148*0Sstevel@tonic-gate 			printf("RPC: Failed (unspecified error).\n");
149*0Sstevel@tonic-gate 			break;
150*0Sstevel@tonic-gate 		default:
151*0Sstevel@tonic-gate 			printf("RPC: (unknown error code).\n");
152*0Sstevel@tonic-gate 			break;
153*0Sstevel@tonic-gate 		}
154*0Sstevel@tonic-gate 	}
155*0Sstevel@tonic-gate }
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate /*
158*0Sstevel@tonic-gate  * rpc_hdr: sets the fields in the rpc msg header.
159*0Sstevel@tonic-gate  *
160*0Sstevel@tonic-gate  * Returns: TRUE on success, FALSE if failure.
161*0Sstevel@tonic-gate  */
162*0Sstevel@tonic-gate /*ARGSUSED*/
163*0Sstevel@tonic-gate static bool_t
164*0Sstevel@tonic-gate rpc_hdr(XDR *xdrs, uint_t xid, rpcprog_t prog, rpcvers_t vers, rpcproc_t proc)
165*0Sstevel@tonic-gate {
166*0Sstevel@tonic-gate 	struct rpc_msg call_msg;
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate 	/* setup header */
169*0Sstevel@tonic-gate 	call_msg.rm_xid = xid;
170*0Sstevel@tonic-gate 	call_msg.rm_direction = CALL;
171*0Sstevel@tonic-gate 	call_msg.rm_call.cb_rpcvers = (rpcvers_t)RPC_MSG_VERSION;
172*0Sstevel@tonic-gate 	call_msg.rm_call.cb_prog = prog;
173*0Sstevel@tonic-gate 	call_msg.rm_call.cb_vers = vers;
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate 	/* xdr the header. */
176*0Sstevel@tonic-gate 	if (xdr_callhdr(xdrs, &call_msg) == FALSE)
177*0Sstevel@tonic-gate 		return (FALSE);
178*0Sstevel@tonic-gate 	else
179*0Sstevel@tonic-gate 		return (TRUE);
180*0Sstevel@tonic-gate }
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate /*
183*0Sstevel@tonic-gate  * our version of brpc_call(). We cache in portnumber in to->sin_port for
184*0Sstevel@tonic-gate  * your convenience. to and from addresses are taken and received in network
185*0Sstevel@tonic-gate  * order.
186*0Sstevel@tonic-gate  */
187*0Sstevel@tonic-gate enum clnt_stat
188*0Sstevel@tonic-gate brpc_call(
189*0Sstevel@tonic-gate 	rpcprog_t	prog,		/* rpc program number to call. */
190*0Sstevel@tonic-gate 	rpcvers_t	vers,		/* rpc program version */
191*0Sstevel@tonic-gate 	rpcproc_t	proc,		/* rpc procedure to call */
192*0Sstevel@tonic-gate 	xdrproc_t	in_xdr,		/* routine to serialize arguments */
193*0Sstevel@tonic-gate 	caddr_t		args,		/* arg vector for remote call */
194*0Sstevel@tonic-gate 	xdrproc_t	out_xdr,	/* routine to deserialize results */
195*0Sstevel@tonic-gate 	caddr_t		ret,		/* addr of buf to place results in */
196*0Sstevel@tonic-gate 	int		rexmit,		/* retransmission interval (secs) */
197*0Sstevel@tonic-gate 	int		wait_time,	/* how long (secs) to wait (resp) */
198*0Sstevel@tonic-gate 	struct sockaddr_in 	*to,		/* destination */
199*0Sstevel@tonic-gate 	struct sockaddr_in	*from_who,	/* responder's port/address */
200*0Sstevel@tonic-gate 	uint_t			auth)		/* type of auth wanted. */
201*0Sstevel@tonic-gate {
202*0Sstevel@tonic-gate 	int s;
203*0Sstevel@tonic-gate 	char hostname[MAXHOSTNAMELEN];
204*0Sstevel@tonic-gate 	struct sockaddr_in from;	/* us. */
205*0Sstevel@tonic-gate 	socklen_t from_len;
206*0Sstevel@tonic-gate 	XDR xmit_xdrs, rcv_xdrs;	/* xdr memory */
207*0Sstevel@tonic-gate 	AUTH *xmit_auth;		/* our chosen auth cookie */
208*0Sstevel@tonic-gate 	gid_t fake_gids = 1;		/* fake gids list for auth_unix */
209*0Sstevel@tonic-gate 	caddr_t trm_msg, rcv_msg;	/* outgoing/incoming rpc mesgs */
210*0Sstevel@tonic-gate 	struct rpc_msg reply;		/* our reply msg header */
211*0Sstevel@tonic-gate 	int trm_len, rcv_len;
212*0Sstevel@tonic-gate 	struct rpc_err rpc_error;	/* to store RPC errors in on rcv. */
213*0Sstevel@tonic-gate 	static uint_t xid;		/* current xid */
214*0Sstevel@tonic-gate 	uint_t xmit_len;		/* How much of the buffer we used */
215*0Sstevel@tonic-gate 	int nrefreshes = 2;		/* # of times to refresh cred */
216*0Sstevel@tonic-gate 	int flags = 0;			/* send flags */
217*0Sstevel@tonic-gate 	uint_t xdelay;
218*0Sstevel@tonic-gate 	int errors, preserve_errno;
219*0Sstevel@tonic-gate 	uint32_t timeout;
220*0Sstevel@tonic-gate 	socklen_t optlen;
221*0Sstevel@tonic-gate 
222*0Sstevel@tonic-gate 	xmit_auth = NULL;
223*0Sstevel@tonic-gate 
224*0Sstevel@tonic-gate 	trm_len = mac_get_mtu();
225*0Sstevel@tonic-gate 	trm_msg = bkmem_alloc(trm_len);
226*0Sstevel@tonic-gate 	rcv_msg = bkmem_alloc(NFSBUF_SIZE);
227*0Sstevel@tonic-gate 
228*0Sstevel@tonic-gate 	if (trm_msg == NULL || rcv_msg == NULL) {
229*0Sstevel@tonic-gate 		errno = ENOMEM;
230*0Sstevel@tonic-gate 		rpc_error.re_status = RPC_CANTSEND;
231*0Sstevel@tonic-gate 		goto gt_error;
232*0Sstevel@tonic-gate 	}
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate 	if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
235*0Sstevel@tonic-gate 		rpc_error.re_status = RPC_CANTSEND;
236*0Sstevel@tonic-gate 		goto gt_error;
237*0Sstevel@tonic-gate 	}
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate 	if (dontroute) {
240*0Sstevel@tonic-gate 		(void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
241*0Sstevel@tonic-gate 		    (const void *)&dontroute, sizeof (dontroute));
242*0Sstevel@tonic-gate 	}
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate 	if (to->sin_addr.s_addr == cached_destination.s_addr) {
245*0Sstevel@tonic-gate 		optlen = sizeof (timeout);
246*0Sstevel@tonic-gate 		(void) getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout,
247*0Sstevel@tonic-gate 		    &optlen);
248*0Sstevel@tonic-gate 	} else {
249*0Sstevel@tonic-gate 		cached_destination.s_addr = htonl(INADDR_ANY);
250*0Sstevel@tonic-gate 	}
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate 	/* Bind our endpoint. */
253*0Sstevel@tonic-gate 	from.sin_family = AF_INET;
254*0Sstevel@tonic-gate 	ipv4_getipaddr(&from.sin_addr);
255*0Sstevel@tonic-gate 	from.sin_addr.s_addr = htonl(from.sin_addr.s_addr);
256*0Sstevel@tonic-gate 	from.sin_port = get_source_port(B_TRUE);
257*0Sstevel@tonic-gate 
258*0Sstevel@tonic-gate 	if (bind(s, (struct sockaddr *)&from, sizeof (from)) < 0) {
259*0Sstevel@tonic-gate 		rpc_error.re_status = RPC_CANTSEND;
260*0Sstevel@tonic-gate 		goto gt_error;
261*0Sstevel@tonic-gate 	}
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate 	bzero((caddr_t)&rpc_error, sizeof (struct rpc_err));
264*0Sstevel@tonic-gate 
265*0Sstevel@tonic-gate 	/* initialize reply's rpc_msg struct, so we can decode later. */
266*0Sstevel@tonic-gate 	reply.acpted_rply.ar_verf = _null_auth;	/* struct copy */
267*0Sstevel@tonic-gate 	reply.acpted_rply.ar_results.where = ret;
268*0Sstevel@tonic-gate 	reply.acpted_rply.ar_results.proc = out_xdr;
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate 	if (ntohs(to->sin_port) == 0) {
271*0Sstevel@tonic-gate 		/* snag the udp port we need. */
272*0Sstevel@tonic-gate 		if ((to->sin_port = (in_port_t)bpmap_getport(prog, vers,
273*0Sstevel@tonic-gate 		    &(rpc_error.re_status), to, NULL)) == 0)
274*0Sstevel@tonic-gate 			goto gt_error;
275*0Sstevel@tonic-gate 		to->sin_port = htons(to->sin_port);
276*0Sstevel@tonic-gate 	}
277*0Sstevel@tonic-gate 
278*0Sstevel@tonic-gate 	/* generate xid - increment */
279*0Sstevel@tonic-gate 	if (xid == 0)
280*0Sstevel@tonic-gate 		xid = (uint_t)(prom_gettime() / 1000) + 1;
281*0Sstevel@tonic-gate 	else
282*0Sstevel@tonic-gate 		xid++;
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate 	/* set up outgoing pkt as xdr modified. */
285*0Sstevel@tonic-gate 	xdrmem_create(&xmit_xdrs, trm_msg, trm_len, XDR_ENCODE);
286*0Sstevel@tonic-gate 
287*0Sstevel@tonic-gate 	/* setup rpc header */
288*0Sstevel@tonic-gate 	if (rpc_hdr(&xmit_xdrs, xid, prog, vers, proc) != TRUE) {
289*0Sstevel@tonic-gate 		dprintf("brpc_call: cannot setup rpc header.\n");
290*0Sstevel@tonic-gate 		rpc_error.re_status = RPC_FAILED;
291*0Sstevel@tonic-gate 		goto gt_error;
292*0Sstevel@tonic-gate 	}
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 	/* setup authentication */
295*0Sstevel@tonic-gate 	switch (auth) {
296*0Sstevel@tonic-gate 	case AUTH_NONE:
297*0Sstevel@tonic-gate 		xmit_auth = authnone_create();
298*0Sstevel@tonic-gate 		break;
299*0Sstevel@tonic-gate 	case AUTH_UNIX:
300*0Sstevel@tonic-gate 		/*
301*0Sstevel@tonic-gate 		 * Assumes we've configured the stack and thus know our
302*0Sstevel@tonic-gate 		 * IP address/hostname, either by using DHCP or rarp/bootparams.
303*0Sstevel@tonic-gate 		 */
304*0Sstevel@tonic-gate 		gethostname(hostname, sizeof (hostname));
305*0Sstevel@tonic-gate 		xmit_auth = authunix_create(hostname, 0, 1, 1, &fake_gids);
306*0Sstevel@tonic-gate 		break;
307*0Sstevel@tonic-gate 	default:
308*0Sstevel@tonic-gate 		dprintf("brpc_call: Unsupported authentication type: %d\n",
309*0Sstevel@tonic-gate 		    auth);
310*0Sstevel@tonic-gate 		rpc_error.re_status = RPC_AUTHERROR;
311*0Sstevel@tonic-gate 		goto gt_error;
312*0Sstevel@tonic-gate 	/*NOTREACHED*/
313*0Sstevel@tonic-gate 	}
314*0Sstevel@tonic-gate 
315*0Sstevel@tonic-gate 	/*
316*0Sstevel@tonic-gate 	 * rpc_hdr puts everything in the xmit buffer for the header
317*0Sstevel@tonic-gate 	 * EXCEPT the proc. Put it, and our authentication info into
318*0Sstevel@tonic-gate 	 * it now, serializing as we go. We will be at the place where
319*0Sstevel@tonic-gate 	 * we left off.
320*0Sstevel@tonic-gate 	 */
321*0Sstevel@tonic-gate 	xmit_xdrs.x_op = XDR_ENCODE;
322*0Sstevel@tonic-gate 	if ((XDR_PUTINT32(&xmit_xdrs, (int32_t *)&proc) == FALSE) ||
323*0Sstevel@tonic-gate 	    (AUTH_MARSHALL(xmit_auth, &xmit_xdrs, NULL) == FALSE) ||
324*0Sstevel@tonic-gate 	    ((*in_xdr)(&xmit_xdrs, args) == FALSE)) {
325*0Sstevel@tonic-gate 		rpc_error.re_status = RPC_CANTENCODEARGS;
326*0Sstevel@tonic-gate 		goto gt_error;
327*0Sstevel@tonic-gate 	} else
328*0Sstevel@tonic-gate 		xmit_len = (int)XDR_GETPOS(&xmit_xdrs); /* for sendto */
329*0Sstevel@tonic-gate 
330*0Sstevel@tonic-gate 	/*
331*0Sstevel@tonic-gate 	 * Right now the outgoing packet should be all serialized and
332*0Sstevel@tonic-gate 	 * ready to go... Set up timers.
333*0Sstevel@tonic-gate 	 */
334*0Sstevel@tonic-gate 
335*0Sstevel@tonic-gate 	xdelay = (rexmit == 0) ? RPC_REXMIT_MSEC : (rexmit * 1000);
336*0Sstevel@tonic-gate 	(void) setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (void *)&xdelay,
337*0Sstevel@tonic-gate 	    sizeof (xdelay));
338*0Sstevel@tonic-gate 	wait_time = (wait_time == 0) ? RPC_RCVWAIT_MSEC : (wait_time * 1000);
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate 	wait_time += prom_gettime();
341*0Sstevel@tonic-gate 
342*0Sstevel@tonic-gate 	/*
343*0Sstevel@tonic-gate 	 * send out the request. The first item in the receive buffer will
344*0Sstevel@tonic-gate 	 * be the xid. Check if it is correct.
345*0Sstevel@tonic-gate 	 */
346*0Sstevel@tonic-gate 	errors = 0;
347*0Sstevel@tonic-gate 	rpc_error.re_status = RPC_TIMEDOUT;
348*0Sstevel@tonic-gate 	do {
349*0Sstevel@tonic-gate 		if (sendto(s, trm_msg, xmit_len, flags, (struct sockaddr *)to,
350*0Sstevel@tonic-gate 		    sizeof (struct sockaddr_in)) < 0) {
351*0Sstevel@tonic-gate 			rpc_error.re_status = RPC_CANTSEND;
352*0Sstevel@tonic-gate 			goto gt_error;
353*0Sstevel@tonic-gate 		}
354*0Sstevel@tonic-gate 
355*0Sstevel@tonic-gate 		from_len = sizeof (struct sockaddr_in);
356*0Sstevel@tonic-gate 		while ((rcv_len = recvfrom(s, rcv_msg, NFSBUF_SIZE,
357*0Sstevel@tonic-gate 		    MSG_DONTWAIT, (struct sockaddr *)from_who,
358*0Sstevel@tonic-gate 		    &from_len)) > 0 || errors < RPC_ALLOWABLE_ERRORS) {
359*0Sstevel@tonic-gate 			if (rcv_len < 0) {
360*0Sstevel@tonic-gate 				if (errno == EWOULDBLOCK ||
361*0Sstevel@tonic-gate 				    errno == ETIMEDOUT) {
362*0Sstevel@tonic-gate 					break; /* timeout */
363*0Sstevel@tonic-gate 				}
364*0Sstevel@tonic-gate 				rpc_error.re_status = RPC_CANTRECV;
365*0Sstevel@tonic-gate 				goto gt_error;
366*0Sstevel@tonic-gate 			}
367*0Sstevel@tonic-gate 			if (ntohl(*((uint32_t *)(rcv_msg))) != xid) {
368*0Sstevel@tonic-gate 				dprintf("brpc_call: xid: 0x%x != 0x%x\n",
369*0Sstevel@tonic-gate 				    *(uint32_t *)(rcv_msg), xid);
370*0Sstevel@tonic-gate 				continue;
371*0Sstevel@tonic-gate 			}
372*0Sstevel@tonic-gate 			/*
373*0Sstevel@tonic-gate 			 * Let's deserialize the data into our 'ret' buffer.
374*0Sstevel@tonic-gate 			 */
375*0Sstevel@tonic-gate 			xdrmem_create(&rcv_xdrs, rcv_msg, rcv_len, XDR_DECODE);
376*0Sstevel@tonic-gate 			if (xdr_replymsg(&rcv_xdrs, &reply) == FALSE) {
377*0Sstevel@tonic-gate 				rpc_error.re_status = RPC_CANTDECODERES;
378*0Sstevel@tonic-gate 				goto gt_error;
379*0Sstevel@tonic-gate 			}
380*0Sstevel@tonic-gate 			_seterr_reply(&reply, &rpc_error);
381*0Sstevel@tonic-gate 			switch (rpc_error.re_status) {
382*0Sstevel@tonic-gate 			case RPC_SUCCESS:
383*0Sstevel@tonic-gate 				/*
384*0Sstevel@tonic-gate 				 * XXX - validate for unix and none
385*0Sstevel@tonic-gate 				 * always return true.
386*0Sstevel@tonic-gate 				 */
387*0Sstevel@tonic-gate 				if (AUTH_VALIDATE(xmit_auth,
388*0Sstevel@tonic-gate 				    &reply.acpted_rply.ar_verf) == FALSE) {
389*0Sstevel@tonic-gate 					rpc_error.re_status = RPC_AUTHERROR;
390*0Sstevel@tonic-gate 					rpc_error.re_why = AUTH_INVALIDRESP;
391*0Sstevel@tonic-gate 					errors++;
392*0Sstevel@tonic-gate 				}
393*0Sstevel@tonic-gate 				if (reply.acpted_rply.ar_verf.oa_base !=
394*0Sstevel@tonic-gate 				    0) {
395*0Sstevel@tonic-gate 					xmit_xdrs.x_op = XDR_FREE;
396*0Sstevel@tonic-gate 					(void) xdr_opaque_auth(
397*0Sstevel@tonic-gate 					    &xmit_xdrs,
398*0Sstevel@tonic-gate 					    &reply.acpted_rply.ar_verf);
399*0Sstevel@tonic-gate 				}
400*0Sstevel@tonic-gate 				break;
401*0Sstevel@tonic-gate 
402*0Sstevel@tonic-gate 			case RPC_AUTHERROR:
403*0Sstevel@tonic-gate 				/*
404*0Sstevel@tonic-gate 				 * Let's see if our credentials need
405*0Sstevel@tonic-gate 				 * refreshing
406*0Sstevel@tonic-gate 				 */
407*0Sstevel@tonic-gate 				if (nrefreshes > 0 && AUTH_REFRESH(xmit_auth,
408*0Sstevel@tonic-gate 				    NULL, NULL)) {
409*0Sstevel@tonic-gate 					nrefreshes--;
410*0Sstevel@tonic-gate 				}
411*0Sstevel@tonic-gate 				errors++;
412*0Sstevel@tonic-gate 				break;
413*0Sstevel@tonic-gate 
414*0Sstevel@tonic-gate 			case RPC_PROCUNAVAIL:
415*0Sstevel@tonic-gate 				/*
416*0Sstevel@tonic-gate 				 * Might be a silly portmapper implementation
417*0Sstevel@tonic-gate 				 * erroneously responding to our rpc broadcast
418*0Sstevel@tonic-gate 				 * indirect portmapper call. For this
419*0Sstevel@tonic-gate 				 * particular case, we don't increment the
420*0Sstevel@tonic-gate 				 * error counter because we want to keep
421*0Sstevel@tonic-gate 				 * sifting for successful replies...
422*0Sstevel@tonic-gate 				 */
423*0Sstevel@tonic-gate 				if (to->sin_addr.s_addr !=
424*0Sstevel@tonic-gate 				    ntohl(INADDR_BROADCAST))
425*0Sstevel@tonic-gate 					errors++;
426*0Sstevel@tonic-gate 				break;
427*0Sstevel@tonic-gate 
428*0Sstevel@tonic-gate 			case RPC_PROGVERSMISMATCH:
429*0Sstevel@tonic-gate 				/*
430*0Sstevel@tonic-gate 				 * Successfully talked to server, but they
431*0Sstevel@tonic-gate 				 * don't speak our lingo.
432*0Sstevel@tonic-gate 				 */
433*0Sstevel@tonic-gate 				goto gt_error;
434*0Sstevel@tonic-gate 
435*0Sstevel@tonic-gate 			default:
436*0Sstevel@tonic-gate 				/* Just keep trying till there's no data... */
437*0Sstevel@tonic-gate 				errors++;
438*0Sstevel@tonic-gate 				break;
439*0Sstevel@tonic-gate 			}
440*0Sstevel@tonic-gate 
441*0Sstevel@tonic-gate 			if (rpc_error.re_status != RPC_SUCCESS) {
442*0Sstevel@tonic-gate 				dprintf("brpc_call: from: %s, error: ",
443*0Sstevel@tonic-gate 				    inet_ntoa(from_who->sin_addr));
444*0Sstevel@tonic-gate 				rpc_disperr(&rpc_error);
445*0Sstevel@tonic-gate 			} else
446*0Sstevel@tonic-gate 				break;
447*0Sstevel@tonic-gate 		}
448*0Sstevel@tonic-gate 
449*0Sstevel@tonic-gate 		/*
450*0Sstevel@tonic-gate 		 * If we're having trouble reassembling datagrams, let the
451*0Sstevel@tonic-gate 		 * application know ASAP so that it can take the appropriate
452*0Sstevel@tonic-gate 		 * actions.
453*0Sstevel@tonic-gate 		 */
454*0Sstevel@tonic-gate 
455*0Sstevel@tonic-gate 	} while (rpc_error.re_status != RPC_SUCCESS && errno != ETIMEDOUT &&
456*0Sstevel@tonic-gate 	    prom_gettime() < wait_time);
457*0Sstevel@tonic-gate 
458*0Sstevel@tonic-gate gt_error:
459*0Sstevel@tonic-gate 	if (xmit_auth != NULL)
460*0Sstevel@tonic-gate 		AUTH_DESTROY(xmit_auth);
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	if (trm_msg != NULL)
463*0Sstevel@tonic-gate 		bkmem_free(trm_msg, trm_len);
464*0Sstevel@tonic-gate 	if (rcv_msg != NULL)
465*0Sstevel@tonic-gate 		bkmem_free(rcv_msg, NFSBUF_SIZE);
466*0Sstevel@tonic-gate 
467*0Sstevel@tonic-gate 	if (rpc_error.re_status != RPC_SUCCESS)
468*0Sstevel@tonic-gate 		rpc_disperr(&rpc_error);
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 	/*
471*0Sstevel@tonic-gate 	 * socket calls reset errno. Since we want to hold onto the errno
472*0Sstevel@tonic-gate 	 * value if it is ETIMEDOUT to communicate to our caller that this
473*0Sstevel@tonic-gate 	 * RPC_TIMEDOUT situation is due to a stack problem (we're getting
474*0Sstevel@tonic-gate 	 * a reply, but the stack simply can't assemble it.), we need to
475*0Sstevel@tonic-gate 	 * preserve errno's value over the socket_close().
476*0Sstevel@tonic-gate 	 */
477*0Sstevel@tonic-gate 	preserve_errno = (errno == ETIMEDOUT) ? errno : 0;
478*0Sstevel@tonic-gate 	(void) socket_close(s);
479*0Sstevel@tonic-gate 	errno = preserve_errno;
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate 	return (rpc_error.re_status);
482*0Sstevel@tonic-gate }
483