xref: /illumos-gate/usr/src/ucblib/librpcsoc/clnt_tcp.c (revision 3c6b90be1d51de874ba4c1f05537c85375b6ab6e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1990 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright (c) 1984, 1986, 1987, 1988, 1989, 1996 AT&T
29  * All Rights Reserved
30  */
31 
32 /*
33  * University Copyright- Copyright (c) 1982, 1986, 1988
34  * The Regents of the University of California
35  * All Rights Reserved
36  *
37  * University Acknowledgment- Portions of this document are derived from
38  * software developed by the University of California, Berkeley, and its
39  * contributors.
40  */
41 
42 #pragma ident	"%Z%%M%	%I%	%E% SMI"
43 
44 /*
45  * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
46  *
47  * TCP based RPC supports 'batched calls'.
48  * A sequence of calls may be batched-up in a send buffer.  The rpc call
49  * return immediately to the client even though the call was not necessarily
50  * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
51  * the rpc timeout value is zero (see clnt.h, rpc).
52  *
53  * Clients should NOT casually batch calls that in fact return results; that is,
54  * the server side should be aware that a call is batched and not produce any
55  * return message.  Batched calls that produce many result messages can
56  * deadlock (netlock) the client and the server....
57  *
58  * Now go hang yourself.
59  */
60 
61 #include <rpc/rpc.h>
62 #include <sys/socket.h>
63 #include <sys/time.h>
64 #include <netdb.h>
65 #include <errno.h>
66 #include <rpc/pmap_clnt.h>
67 #include <syslog.h>
68 #include <malloc.h>
69 #include <stdio.h>
70 
71 #define	MCALL_MSG_SIZE 24
72 
73 extern int errno;
74 
75 static int	readtcp();
76 static int	writetcp();
77 extern int _socket(int, int, int);
78 extern pid_t getpid();
79 extern int bindresvport(int, struct sockaddr_in *);
80 extern bool_t   xdr_opaque_auth(XDR *, struct opaque_auth *);
81 static struct clnt_ops *clnttcp_ops();
82 
83 struct ct_data {
84 	int		ct_sock;
85 	bool_t		ct_closeit;
86 	struct timeval	ct_wait;
87 	bool_t		ct_waitset;	/* wait set by clnt_control? */
88 	struct sockaddr_in ct_addr;
89 	struct rpc_err	ct_error;
90 	char		ct_mcall[MCALL_MSG_SIZE];	/* marshalled callmsg */
91 	u_int		ct_mpos;			/* pos after marshal */
92 	XDR		ct_xdrs;
93 };
94 
95 /*
96  * Create a client handle for a tcp/ip connection.
97  * If *sockp<0, *sockp is set to a newly created TCP socket and it is
98  * connected to raddr.  If *sockp non-negative then
99  * raddr is ignored.  The rpc/tcp package does buffering
100  * similar to stdio, so the client must pick send and receive buffer sizes
101  * 0 => use the default.
102  * If raddr->sin_port is 0, then a binder on the remote machine is
103  * consulted for the right port number.
104  * NB: *sockp is copied into a private area.
105  * NB: It is the clients responsibility to close *sockp.
106  * NB: The rpch->cl_auth is set null authentication.  Caller may wish to
107  * set this something more useful.
108  */
109 CLIENT *
110 clnttcp_create(raddr, prog, vers, sockp, sendsz, recvsz)
111 	struct sockaddr_in *raddr;
112 	rpcprog_t prog;
113 	rpcvers_t vers;
114 	register int *sockp;
115 	u_int sendsz;
116 	u_int recvsz;
117 {
118 	CLIENT *h;
119 	register struct ct_data *ct;
120 	struct timeval now;
121 	struct rpc_msg call_msg;
122 
123 	h  = (CLIENT *)mem_alloc(sizeof (*h));
124 	if (h == NULL) {
125 		(void) syslog(LOG_ERR, "clnttcp_create: out of memory");
126 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
127 		rpc_createerr.cf_error.re_errno = errno;
128 		goto fooy;
129 	}
130 	ct = (struct ct_data *)mem_alloc(sizeof (*ct));
131 	if (ct == NULL) {
132 		(void) syslog(LOG_ERR, "clnttcp_create: out of memory");
133 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
134 		rpc_createerr.cf_error.re_errno = errno;
135 		goto fooy;
136 	}
137 
138 	/*
139 	 * If no port number given ask the pmap for one
140 	 */
141 	if (raddr->sin_port == 0) {
142 		u_short port;
143 		if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP))
144 		    == 0) {
145 			mem_free((caddr_t)ct, sizeof (struct ct_data));
146 			mem_free((caddr_t)h, sizeof (CLIENT));
147 			return ((CLIENT *)NULL);
148 		}
149 		raddr->sin_port = htons(port);
150 	}
151 
152 	/*
153 	 * If no socket given, open one
154 	 */
155 	if (*sockp < 0) {
156 		*sockp = _socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
157 		(void) bindresvport(*sockp, (struct sockaddr_in *)0);
158 		if ((*sockp < 0)||
159 			(connect(*sockp, (struct sockaddr *)raddr,
160 			sizeof (*raddr)) < 0)) {
161 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
162 			rpc_createerr.cf_error.re_errno = errno;
163 			(void) close(*sockp);
164 			goto fooy;
165 		}
166 		ct->ct_closeit = TRUE;
167 	} else {
168 		ct->ct_closeit = FALSE;
169 	}
170 
171 	/*
172 	 * Set up private data struct
173 	 */
174 	ct->ct_sock = *sockp;
175 	ct->ct_wait.tv_usec = 0;
176 	ct->ct_waitset = FALSE;
177 	ct->ct_addr = *raddr;
178 
179 	/*
180 	 * Initialize call message
181 	 */
182 	(void) gettimeofday(&now, (struct timezone *)0);
183 	call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
184 	call_msg.rm_direction = CALL;
185 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
186 	call_msg.rm_call.cb_prog = prog;
187 	call_msg.rm_call.cb_vers = vers;
188 
189 	/*
190 	 * pre-serialize the staic part of the call msg and stash it away
191 	 */
192 	xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
193 	    XDR_ENCODE);
194 	if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
195 		if (ct->ct_closeit) {
196 			(void) close(*sockp);
197 		}
198 		goto fooy;
199 	}
200 	ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
201 	XDR_DESTROY(&(ct->ct_xdrs));
202 
203 	/*
204 	 * Create a client handle which uses xdrrec for serialization
205 	 * and authnone for authentication.
206 	 */
207 	xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
208 	    (caddr_t)ct, readtcp, writetcp);
209 	h->cl_ops = clnttcp_ops();
210 	h->cl_private = (caddr_t) ct;
211 	h->cl_auth = authnone_create();
212 	return (h);
213 
214 fooy:
215 	/*
216 	 * Something goofed, free stuff and barf
217 	 */
218 	mem_free((caddr_t)ct, sizeof (struct ct_data));
219 	mem_free((caddr_t)h, sizeof (CLIENT));
220 	return ((CLIENT *)NULL);
221 }
222 
223 static enum clnt_stat
224 clnttcp_call(h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
225 	register CLIENT *h;
226 	rpcproc_t proc;
227 	xdrproc_t xdr_args;
228 	caddr_t args_ptr;
229 	xdrproc_t xdr_results;
230 	caddr_t results_ptr;
231 	struct timeval timeout;
232 {
233 	register struct ct_data *ct = (struct ct_data *) h->cl_private;
234 	register XDR *xdrs = &(ct->ct_xdrs);
235 	struct rpc_msg reply_msg;
236 	uint32_t x_id;
237 	uint32_t *msg_x_id = (uint32_t *)(ct->ct_mcall);	/* yuk */
238 	register bool_t shipnow;
239 	int refreshes = 2;
240 
241 	if (!ct->ct_waitset) {
242 		ct->ct_wait = timeout;
243 	}
244 
245 	shipnow =
246 	    (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0 &&
247 	    timeout.tv_usec == 0) ? FALSE : TRUE;
248 
249 call_again:
250 	xdrs->x_op = XDR_ENCODE;
251 	ct->ct_error.re_status = RPC_SUCCESS;
252 	x_id = ntohl(--(*msg_x_id));
253 	if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
254 	    (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
255 	    (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
256 	    (! (*xdr_args)(xdrs, args_ptr))) {
257 		if (ct->ct_error.re_status == RPC_SUCCESS)
258 			ct->ct_error.re_status = RPC_CANTENCODEARGS;
259 		(void) xdrrec_endofrecord(xdrs, TRUE);
260 		return (ct->ct_error.re_status);
261 	}
262 	if (! xdrrec_endofrecord(xdrs, shipnow))
263 		return (ct->ct_error.re_status = RPC_CANTSEND);
264 	if (! shipnow)
265 		return (RPC_SUCCESS);
266 	/*
267 	 * Hack to provide rpc-based message passing
268 	 */
269 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
270 		return (ct->ct_error.re_status = RPC_TIMEDOUT);
271 	}
272 
273 
274 	/*
275 	 * Keep receiving until we get a valid transaction id
276 	 */
277 	xdrs->x_op = XDR_DECODE;
278 	while (TRUE) {
279 		reply_msg.acpted_rply.ar_verf = _null_auth;
280 		reply_msg.acpted_rply.ar_results.where = NULL;
281 		reply_msg.acpted_rply.ar_results.proc = xdr_void;
282 		if (! xdrrec_skiprecord(xdrs))
283 			return (ct->ct_error.re_status);
284 			/* now decode and validate the response header */
285 		if (! xdr_replymsg(xdrs, &reply_msg)) {
286 			if (ct->ct_error.re_status == RPC_SUCCESS)
287 				continue;
288 			return (ct->ct_error.re_status);
289 		}
290 		if (reply_msg.rm_xid == x_id)
291 			break;
292 	}
293 
294 	/*
295 	 * process header
296 	 */
297 	__seterr_reply(&reply_msg, &(ct->ct_error));
298 	if (ct->ct_error.re_status == RPC_SUCCESS) {
299 		if (! AUTH_VALIDATE(h->cl_auth,
300 		    &reply_msg.acpted_rply.ar_verf)) {
301 			ct->ct_error.re_status = RPC_AUTHERROR;
302 			ct->ct_error.re_why = AUTH_INVALIDRESP;
303 		} else if (! (*xdr_results)(xdrs, results_ptr)) {
304 			if (ct->ct_error.re_status == RPC_SUCCESS)
305 				ct->ct_error.re_status = RPC_CANTDECODERES;
306 		}
307 		/* free verifier ... */
308 		if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
309 			xdrs->x_op = XDR_FREE;
310 			(void) xdr_opaque_auth(xdrs,
311 			    &(reply_msg.acpted_rply.ar_verf));
312 		}
313 	}  /* end successful completion */
314 	else {
315 		/* maybe our credentials need to be refreshed ... */
316 		if (refreshes-- && AUTH_REFRESH(h->cl_auth, &reply_msg))
317 			goto call_again;
318 	}  /* end of unsuccessful completion */
319 	return (ct->ct_error.re_status);
320 }
321 
322 static void
323 clnttcp_geterr(h, errp)
324 	CLIENT *h;
325 	struct rpc_err *errp;
326 {
327 	register struct ct_data *ct =
328 	    (struct ct_data *) h->cl_private;
329 
330 	*errp = ct->ct_error;
331 }
332 
333 static bool_t
334 clnttcp_freeres(cl, xdr_res, res_ptr)
335 	CLIENT *cl;
336 	xdrproc_t xdr_res;
337 	caddr_t res_ptr;
338 {
339 	register struct ct_data *ct = (struct ct_data *)cl->cl_private;
340 	register XDR *xdrs = &(ct->ct_xdrs);
341 
342 	xdrs->x_op = XDR_FREE;
343 	return ((*xdr_res)(xdrs, res_ptr));
344 }
345 
346 static void
347 clnttcp_abort()
348 {
349 }
350 
351 static bool_t
352 clnttcp_control(cl, request, info)
353 	CLIENT *cl;
354 	int request;
355 	char *info;
356 {
357 	register struct ct_data *ct = (struct ct_data *)cl->cl_private;
358 
359 	switch (request) {
360 	case CLSET_TIMEOUT:
361 		ct->ct_wait = *(struct timeval *)info;
362 		ct->ct_waitset = TRUE;
363 		break;
364 	case CLGET_TIMEOUT:
365 		*(struct timeval *)info = ct->ct_wait;
366 		break;
367 	case CLGET_SERVER_ADDR:
368 		*(struct sockaddr_in *)info = ct->ct_addr;
369 		break;
370 	case CLGET_FD:
371 		*(int *)info = ct->ct_sock;
372 		break;
373 	case CLSET_FD_CLOSE:
374 		ct->ct_closeit = TRUE;
375 		break;
376 	case CLSET_FD_NCLOSE:
377 		ct->ct_closeit = FALSE;
378 		break;
379 	default:
380 		return (FALSE);
381 	}
382 	return (TRUE);
383 }
384 
385 
386 static void
387 clnttcp_destroy(h)
388 	CLIENT *h;
389 {
390 	register struct ct_data *ct =
391 	    (struct ct_data *) h->cl_private;
392 
393 	if (ct->ct_closeit) {
394 		(void) close(ct->ct_sock);
395 	}
396 	XDR_DESTROY(&(ct->ct_xdrs));
397 	mem_free((caddr_t)ct, sizeof (struct ct_data));
398 	mem_free((caddr_t)h, sizeof (CLIENT));
399 }
400 
401 /*
402  * Interface between xdr serializer and tcp connection.
403  * Behaves like the system calls, read & write, but keeps some error state
404  * around for the rpc level.
405  */
406 static int
407 readtcp(ct, buf, len)
408 	register struct ct_data *ct;
409 	caddr_t buf;
410 	register int len;
411 {
412 	fd_set mask;
413 	fd_set readfds;
414 
415 	if (len == 0)
416 		return (0);
417 	FD_ZERO(&mask);
418 	FD_SET(ct->ct_sock, &mask);
419 	while (TRUE) {
420 		readfds = mask;
421 		switch (select(__rpc_dtbsize(),
422 		    &readfds, NULL, NULL, &(ct->ct_wait))) {
423 		case 0:
424 			ct->ct_error.re_status = RPC_TIMEDOUT;
425 			return (-1);
426 
427 		case -1:
428 			if (errno == EINTR)
429 				continue;
430 			ct->ct_error.re_status = RPC_CANTRECV;
431 			ct->ct_error.re_errno = errno;
432 			return (-1);
433 		}
434 		break;
435 	}
436 	switch (len = read(ct->ct_sock, buf, len)) {
437 
438 	case 0:
439 		/* premature eof */
440 		ct->ct_error.re_errno = ECONNRESET;
441 		ct->ct_error.re_status = RPC_CANTRECV;
442 		len = -1;  /* it's really an error */
443 		break;
444 
445 	case -1:
446 		ct->ct_error.re_errno = errno;
447 		ct->ct_error.re_status = RPC_CANTRECV;
448 		break;
449 	}
450 	return (len);
451 }
452 
453 static int
454 writetcp(ct, buf, len)
455 	struct ct_data *ct;
456 	caddr_t buf;
457 	int len;
458 {
459 	register int i, cnt;
460 
461 	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
462 		if ((i = write(ct->ct_sock, buf, cnt)) == -1) {
463 			ct->ct_error.re_errno = errno;
464 			ct->ct_error.re_status = RPC_CANTSEND;
465 			return (-1);
466 		}
467 	}
468 	return (len);
469 }
470 
471 static struct clnt_ops *
472 clnttcp_ops()
473 {
474 	static struct clnt_ops ops;
475 
476 	if (ops.cl_call == NULL) {
477 		ops.cl_call = clnttcp_call;
478 		ops.cl_abort = clnttcp_abort;
479 		ops.cl_geterr = clnttcp_geterr;
480 		ops.cl_freeres = clnttcp_freeres;
481 		ops.cl_destroy = clnttcp_destroy;
482 		ops.cl_control = clnttcp_control;
483 	}
484 	return (&ops);
485 }
486