xref: /illumos-gate/usr/src/ucblib/librpcsoc/clnt_tcp.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 	int i;
123 
124 	h  = (CLIENT *)mem_alloc(sizeof (*h));
125 	if (h == NULL) {
126 		(void) syslog(LOG_ERR, "clnttcp_create: out of memory");
127 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
128 		rpc_createerr.cf_error.re_errno = errno;
129 		goto fooy;
130 	}
131 	ct = (struct ct_data *)mem_alloc(sizeof (*ct));
132 	if (ct == NULL) {
133 		(void) syslog(LOG_ERR, "clnttcp_create: out of memory");
134 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
135 		rpc_createerr.cf_error.re_errno = errno;
136 		goto fooy;
137 	}
138 
139 	/*
140 	 * If no port number given ask the pmap for one
141 	 */
142 	if (raddr->sin_port == 0) {
143 		u_short port;
144 		if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP))
145 		    == 0) {
146 			mem_free((caddr_t)ct, sizeof (struct ct_data));
147 			mem_free((caddr_t)h, sizeof (CLIENT));
148 			return ((CLIENT *)NULL);
149 		}
150 		raddr->sin_port = htons(port);
151 	}
152 
153 	/*
154 	 * If no socket given, open one
155 	 */
156 	if (*sockp < 0) {
157 		*sockp = _socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
158 		i = bindresvport(*sockp, (struct sockaddr_in *)0);
159 		if ((*sockp < 0)||
160 			(connect(*sockp, (struct sockaddr *)raddr,
161 			sizeof (*raddr)) < 0)) {
162 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
163 			rpc_createerr.cf_error.re_errno = errno;
164 			(void) close(*sockp);
165 			goto fooy;
166 		}
167 		ct->ct_closeit = TRUE;
168 	} else {
169 		ct->ct_closeit = FALSE;
170 	}
171 
172 	/*
173 	 * Set up private data struct
174 	 */
175 	ct->ct_sock = *sockp;
176 	ct->ct_wait.tv_usec = 0;
177 	ct->ct_waitset = FALSE;
178 	ct->ct_addr = *raddr;
179 
180 	/*
181 	 * Initialize call message
182 	 */
183 	(void) gettimeofday(&now, (struct timezone *)0);
184 	call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
185 	call_msg.rm_direction = CALL;
186 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
187 	call_msg.rm_call.cb_prog = prog;
188 	call_msg.rm_call.cb_vers = vers;
189 
190 	/*
191 	 * pre-serialize the staic part of the call msg and stash it away
192 	 */
193 	xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
194 	    XDR_ENCODE);
195 	if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
196 		if (ct->ct_closeit) {
197 			(void) close(*sockp);
198 		}
199 		goto fooy;
200 	}
201 	ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
202 	XDR_DESTROY(&(ct->ct_xdrs));
203 
204 	/*
205 	 * Create a client handle which uses xdrrec for serialization
206 	 * and authnone for authentication.
207 	 */
208 	xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
209 	    (caddr_t)ct, readtcp, writetcp);
210 	h->cl_ops = clnttcp_ops();
211 	h->cl_private = (caddr_t) ct;
212 	h->cl_auth = authnone_create();
213 	return (h);
214 
215 fooy:
216 	/*
217 	 * Something goofed, free stuff and barf
218 	 */
219 	mem_free((caddr_t)ct, sizeof (struct ct_data));
220 	mem_free((caddr_t)h, sizeof (CLIENT));
221 	return ((CLIENT *)NULL);
222 }
223 
224 static enum clnt_stat
225 clnttcp_call(h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
226 	register CLIENT *h;
227 	rpcproc_t proc;
228 	xdrproc_t xdr_args;
229 	caddr_t args_ptr;
230 	xdrproc_t xdr_results;
231 	caddr_t results_ptr;
232 	struct timeval timeout;
233 {
234 	register struct ct_data *ct = (struct ct_data *) h->cl_private;
235 	register XDR *xdrs = &(ct->ct_xdrs);
236 	struct rpc_msg reply_msg;
237 	uint32_t x_id;
238 	uint32_t *msg_x_id = (uint32_t *)(ct->ct_mcall);	/* yuk */
239 	register bool_t shipnow;
240 	int refreshes = 2;
241 
242 	if (!ct->ct_waitset) {
243 		ct->ct_wait = timeout;
244 	}
245 
246 	shipnow =
247 	    (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0 &&
248 	    timeout.tv_usec == 0) ? FALSE : TRUE;
249 
250 call_again:
251 	xdrs->x_op = XDR_ENCODE;
252 	ct->ct_error.re_status = RPC_SUCCESS;
253 	x_id = ntohl(--(*msg_x_id));
254 	if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
255 	    (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) ||
256 	    (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
257 	    (! (*xdr_args)(xdrs, args_ptr))) {
258 		if (ct->ct_error.re_status == RPC_SUCCESS)
259 			ct->ct_error.re_status = RPC_CANTENCODEARGS;
260 		(void) xdrrec_endofrecord(xdrs, TRUE);
261 		return (ct->ct_error.re_status);
262 	}
263 	if (! xdrrec_endofrecord(xdrs, shipnow))
264 		return (ct->ct_error.re_status = RPC_CANTSEND);
265 	if (! shipnow)
266 		return (RPC_SUCCESS);
267 	/*
268 	 * Hack to provide rpc-based message passing
269 	 */
270 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
271 		return (ct->ct_error.re_status = RPC_TIMEDOUT);
272 	}
273 
274 
275 	/*
276 	 * Keep receiving until we get a valid transaction id
277 	 */
278 	xdrs->x_op = XDR_DECODE;
279 	while (TRUE) {
280 		reply_msg.acpted_rply.ar_verf = _null_auth;
281 		reply_msg.acpted_rply.ar_results.where = NULL;
282 		reply_msg.acpted_rply.ar_results.proc = xdr_void;
283 		if (! xdrrec_skiprecord(xdrs))
284 			return (ct->ct_error.re_status);
285 			/* now decode and validate the response header */
286 		if (! xdr_replymsg(xdrs, &reply_msg)) {
287 			if (ct->ct_error.re_status == RPC_SUCCESS)
288 				continue;
289 			return (ct->ct_error.re_status);
290 		}
291 		if (reply_msg.rm_xid == x_id)
292 			break;
293 	}
294 
295 	/*
296 	 * process header
297 	 */
298 	__seterr_reply(&reply_msg, &(ct->ct_error));
299 	if (ct->ct_error.re_status == RPC_SUCCESS) {
300 		if (! AUTH_VALIDATE(h->cl_auth,
301 		    &reply_msg.acpted_rply.ar_verf)) {
302 			ct->ct_error.re_status = RPC_AUTHERROR;
303 			ct->ct_error.re_why = AUTH_INVALIDRESP;
304 		} else if (! (*xdr_results)(xdrs, results_ptr)) {
305 			if (ct->ct_error.re_status == RPC_SUCCESS)
306 				ct->ct_error.re_status = RPC_CANTDECODERES;
307 		}
308 		/* free verifier ... */
309 		if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
310 			xdrs->x_op = XDR_FREE;
311 			(void) xdr_opaque_auth(xdrs,
312 			    &(reply_msg.acpted_rply.ar_verf));
313 		}
314 	}  /* end successful completion */
315 	else {
316 		/* maybe our credentials need to be refreshed ... */
317 		if (refreshes-- && AUTH_REFRESH(h->cl_auth, &reply_msg))
318 			goto call_again;
319 	}  /* end of unsuccessful completion */
320 	return (ct->ct_error.re_status);
321 }
322 
323 static void
324 clnttcp_geterr(h, errp)
325 	CLIENT *h;
326 	struct rpc_err *errp;
327 {
328 	register struct ct_data *ct =
329 	    (struct ct_data *) h->cl_private;
330 
331 	*errp = ct->ct_error;
332 }
333 
334 static bool_t
335 clnttcp_freeres(cl, xdr_res, res_ptr)
336 	CLIENT *cl;
337 	xdrproc_t xdr_res;
338 	caddr_t res_ptr;
339 {
340 	register struct ct_data *ct = (struct ct_data *)cl->cl_private;
341 	register XDR *xdrs = &(ct->ct_xdrs);
342 
343 	xdrs->x_op = XDR_FREE;
344 	return ((*xdr_res)(xdrs, res_ptr));
345 }
346 
347 static void
348 clnttcp_abort()
349 {
350 }
351 
352 static bool_t
353 clnttcp_control(cl, request, info)
354 	CLIENT *cl;
355 	int request;
356 	char *info;
357 {
358 	register struct ct_data *ct = (struct ct_data *)cl->cl_private;
359 
360 	switch (request) {
361 	case CLSET_TIMEOUT:
362 		ct->ct_wait = *(struct timeval *)info;
363 		ct->ct_waitset = TRUE;
364 		break;
365 	case CLGET_TIMEOUT:
366 		*(struct timeval *)info = ct->ct_wait;
367 		break;
368 	case CLGET_SERVER_ADDR:
369 		*(struct sockaddr_in *)info = ct->ct_addr;
370 		break;
371 	case CLGET_FD:
372 		*(int *)info = ct->ct_sock;
373 		break;
374 	case CLSET_FD_CLOSE:
375 		ct->ct_closeit = TRUE;
376 		break;
377 	case CLSET_FD_NCLOSE:
378 		ct->ct_closeit = FALSE;
379 		break;
380 	default:
381 		return (FALSE);
382 	}
383 	return (TRUE);
384 }
385 
386 
387 static void
388 clnttcp_destroy(h)
389 	CLIENT *h;
390 {
391 	register struct ct_data *ct =
392 	    (struct ct_data *) h->cl_private;
393 
394 	if (ct->ct_closeit) {
395 		(void) close(ct->ct_sock);
396 	}
397 	XDR_DESTROY(&(ct->ct_xdrs));
398 	mem_free((caddr_t)ct, sizeof (struct ct_data));
399 	mem_free((caddr_t)h, sizeof (CLIENT));
400 }
401 
402 /*
403  * Interface between xdr serializer and tcp connection.
404  * Behaves like the system calls, read & write, but keeps some error state
405  * around for the rpc level.
406  */
407 static int
408 readtcp(ct, buf, len)
409 	register struct ct_data *ct;
410 	caddr_t buf;
411 	register int len;
412 {
413 	fd_set mask;
414 	fd_set readfds;
415 
416 	if (len == 0)
417 		return (0);
418 	FD_ZERO(&mask);
419 	FD_SET(ct->ct_sock, &mask);
420 	while (TRUE) {
421 		readfds = mask;
422 		switch (select(__rpc_dtbsize(),
423 		    &readfds, NULL, NULL, &(ct->ct_wait))) {
424 		case 0:
425 			ct->ct_error.re_status = RPC_TIMEDOUT;
426 			return (-1);
427 
428 		case -1:
429 			if (errno == EINTR)
430 				continue;
431 			ct->ct_error.re_status = RPC_CANTRECV;
432 			ct->ct_error.re_errno = errno;
433 			return (-1);
434 		}
435 		break;
436 	}
437 	switch (len = read(ct->ct_sock, buf, len)) {
438 
439 	case 0:
440 		/* premature eof */
441 		ct->ct_error.re_errno = ECONNRESET;
442 		ct->ct_error.re_status = RPC_CANTRECV;
443 		len = -1;  /* it's really an error */
444 		break;
445 
446 	case -1:
447 		ct->ct_error.re_errno = errno;
448 		ct->ct_error.re_status = RPC_CANTRECV;
449 		break;
450 	}
451 	return (len);
452 }
453 
454 static int
455 writetcp(ct, buf, len)
456 	struct ct_data *ct;
457 	caddr_t buf;
458 	int len;
459 {
460 	register int i, cnt;
461 
462 	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
463 		if ((i = write(ct->ct_sock, buf, cnt)) == -1) {
464 			ct->ct_error.re_errno = errno;
465 			ct->ct_error.re_status = RPC_CANTSEND;
466 			return (-1);
467 		}
468 	}
469 	return (len);
470 }
471 
472 static struct clnt_ops *
473 clnttcp_ops()
474 {
475 	static struct clnt_ops ops;
476 
477 	if (ops.cl_call == NULL) {
478 		ops.cl_call = clnttcp_call;
479 		ops.cl_abort = clnttcp_abort;
480 		ops.cl_geterr = clnttcp_geterr;
481 		ops.cl_freeres = clnttcp_freeres;
482 		ops.cl_destroy = clnttcp_destroy;
483 		ops.cl_control = clnttcp_control;
484 	}
485 	return (&ops);
486 }
487