xref: /openbsd-src/lib/libc/rpc/clnt_tcp.c (revision a4afd6dad3fba28f80e70208181c06c482259988)
1 /*
2  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
3  * unrestricted use provided that this legend is included on all tape
4  * media and as a part of the software program in whole or part.  Users
5  * may copy or modify Sun RPC without charge, but are not authorized
6  * to license or distribute it to anyone else except as part of a product or
7  * program developed by the user.
8  *
9  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
10  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
11  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
12  *
13  * Sun RPC is provided with no support and without any obligation on the
14  * part of Sun Microsystems, Inc. to assist in its use, correction,
15  * modification or enhancement.
16  *
17  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
18  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
19  * OR ANY PART THEREOF.
20  *
21  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
22  * or profits or other special, indirect and consequential damages, even if
23  * Sun has been advised of the possibility of such damages.
24  *
25  * Sun Microsystems, Inc.
26  * 2550 Garcia Avenue
27  * Mountain View, California  94043
28  */
29 
30 #if defined(LIBC_SCCS) && !defined(lint)
31 static char *rcsid = "$OpenBSD: clnt_tcp.c,v 1.11 1996/12/10 07:46:37 deraadt Exp $";
32 #endif /* LIBC_SCCS and not lint */
33 
34 /*
35  * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
36  *
37  * Copyright (C) 1984, Sun Microsystems, Inc.
38  *
39  * TCP based RPC supports 'batched calls'.
40  * A sequence of calls may be batched-up in a send buffer.  The rpc call
41  * return immediately to the client even though the call was not necessarily
42  * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
43  * the rpc timeout value is zero (see clnt.h, rpc).
44  *
45  * Clients should NOT casually batch calls that in fact return results; that is,
46  * the server side should be aware that a call is batched and not produce any
47  * return message.  Batched calls that produce many result messages can
48  * deadlock (netlock) the client and the server....
49  *
50  * Now go hang yourself.
51  */
52 
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <unistd.h>
56 #include <rpc/rpc.h>
57 #include <sys/socket.h>
58 #include <netdb.h>
59 #include <errno.h>
60 #include <rpc/pmap_clnt.h>
61 
62 #define MCALL_MSG_SIZE 24
63 
64 static int	readtcp();
65 static int	writetcp();
66 
67 static enum clnt_stat	clnttcp_call();
68 static void		clnttcp_abort();
69 static void		clnttcp_geterr();
70 static bool_t		clnttcp_freeres();
71 static bool_t           clnttcp_control();
72 static void		clnttcp_destroy();
73 
74 static struct clnt_ops tcp_ops = {
75 	clnttcp_call,
76 	clnttcp_abort,
77 	clnttcp_geterr,
78 	clnttcp_freeres,
79 	clnttcp_destroy,
80 	clnttcp_control
81 };
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 set this
107  * something more useful.
108  */
109 CLIENT *
110 clnttcp_create(raddr, prog, vers, sockp, sendsz, recvsz)
111 	struct sockaddr_in *raddr;
112 	u_long prog;
113 	u_long 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 	static u_int32_t disrupt;
123 
124 	if (disrupt == 0)
125 		disrupt = (u_int32_t)(long)raddr;
126 
127 	h  = (CLIENT *)mem_alloc(sizeof(*h));
128 	if (h == NULL) {
129 		(void)fprintf(stderr, "clnttcp_create: out of memory\n");
130 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
131 		rpc_createerr.cf_error.re_errno = errno;
132 		goto fooy;
133 	}
134 	ct = (struct ct_data *)mem_alloc(sizeof(*ct));
135 	if (ct == NULL) {
136 		(void)fprintf(stderr, "clnttcp_create: out of memory\n");
137 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
138 		rpc_createerr.cf_error.re_errno = errno;
139 		goto fooy;
140 	}
141 
142 	/*
143 	 * If no port number given ask the pmap for one
144 	 */
145 	if (raddr->sin_port == 0) {
146 		u_short port;
147 		if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) {
148 			mem_free((caddr_t)ct, sizeof(struct ct_data));
149 			mem_free((caddr_t)h, sizeof(CLIENT));
150 			return ((CLIENT *)NULL);
151 		}
152 		raddr->sin_port = htons(port);
153 	}
154 
155 	/*
156 	 * If no socket given, open one
157 	 */
158 	if (*sockp < 0) {
159 		*sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
160 		(void)bindresvport(*sockp, (struct sockaddr_in *)0);
161 		if ((*sockp < 0)
162 		    || (connect(*sockp, (struct sockaddr *)raddr,
163 		    sizeof(*raddr)) < 0)) {
164 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
165 			rpc_createerr.cf_error.re_errno = errno;
166 			if (*sockp != -1)
167 				(void)close(*sockp);
168 			goto fooy;
169 		}
170 		ct->ct_closeit = TRUE;
171 	} else {
172 		ct->ct_closeit = FALSE;
173 	}
174 
175 	/*
176 	 * Set up private data struct
177 	 */
178 	ct->ct_sock = *sockp;
179 	ct->ct_wait.tv_usec = 0;
180 	ct->ct_waitset = FALSE;
181 	ct->ct_addr = *raddr;
182 
183 	/*
184 	 * Initialize call message
185 	 */
186 	(void)gettimeofday(&now, (struct timezone *)0);
187 	call_msg.rm_xid = (++disrupt) ^ getpid() ^ now.tv_sec ^ now.tv_usec;
188 	call_msg.rm_direction = CALL;
189 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
190 	call_msg.rm_call.cb_prog = prog;
191 	call_msg.rm_call.cb_vers = vers;
192 
193 	/*
194 	 * pre-serialize the staic part of the call msg and stash it away
195 	 */
196 	xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
197 	    XDR_ENCODE);
198 	if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
199 		if (ct->ct_closeit) {
200 			(void)close(*sockp);
201 		}
202 		goto fooy;
203 	}
204 	ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
205 	XDR_DESTROY(&(ct->ct_xdrs));
206 
207 	/*
208 	 * Create a client handle which uses xdrrec for serialization
209 	 * and authnone for authentication.
210 	 */
211 	xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
212 	    (caddr_t)ct, readtcp, writetcp);
213 	h->cl_ops = &tcp_ops;
214 	h->cl_private = (caddr_t) ct;
215 	h->cl_auth = authnone_create();
216 	return (h);
217 
218 fooy:
219 	/*
220 	 * Something goofed, free stuff and barf
221 	 */
222 	if (ct)
223 		mem_free((caddr_t)ct, sizeof(struct ct_data));
224 	if (h)
225 		mem_free((caddr_t)h, sizeof(CLIENT));
226 	return ((CLIENT *)NULL);
227 }
228 
229 static enum clnt_stat
230 clnttcp_call(h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
231 	register CLIENT *h;
232 	u_long proc;
233 	xdrproc_t xdr_args;
234 	caddr_t args_ptr;
235 	xdrproc_t xdr_results;
236 	caddr_t results_ptr;
237 	struct timeval timeout;
238 {
239 	register struct ct_data *ct = (struct ct_data *) h->cl_private;
240 	register XDR *xdrs = &(ct->ct_xdrs);
241 	struct rpc_msg reply_msg;
242 	u_long x_id;
243 	u_int32_t *msg_x_id = (u_int32_t *)(ct->ct_mcall);	/* yuk */
244 	register bool_t shipnow;
245 	int refreshes = 2;
246 
247 	if (!ct->ct_waitset) {
248 		ct->ct_wait = timeout;
249 	}
250 
251 	shipnow =
252 	    (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
253 	    && timeout.tv_usec == 0) ? FALSE : TRUE;
254 
255 call_again:
256 	xdrs->x_op = XDR_ENCODE;
257 	ct->ct_error.re_status = RPC_SUCCESS;
258 	x_id = ntohl(--(*msg_x_id));
259 	if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
260 	    (! XDR_PUTLONG(xdrs, (long *)&proc)) ||
261 	    (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
262 	    (! (*xdr_args)(xdrs, args_ptr))) {
263 		if (ct->ct_error.re_status == RPC_SUCCESS)
264 			ct->ct_error.re_status = RPC_CANTENCODEARGS;
265 		(void)xdrrec_endofrecord(xdrs, TRUE);
266 		return (ct->ct_error.re_status);
267 	}
268 	if (! xdrrec_endofrecord(xdrs, shipnow))
269 		return (ct->ct_error.re_status = RPC_CANTSEND);
270 	if (! shipnow)
271 		return (RPC_SUCCESS);
272 	/*
273 	 * Hack to provide rpc-based message passing
274 	 */
275 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
276 		return(ct->ct_error.re_status = RPC_TIMEDOUT);
277 	}
278 
279 
280 	/*
281 	 * Keep receiving until we get a valid transaction id
282 	 */
283 	xdrs->x_op = XDR_DECODE;
284 	while (TRUE) {
285 		reply_msg.acpted_rply.ar_verf = _null_auth;
286 		reply_msg.acpted_rply.ar_results.where = NULL;
287 		reply_msg.acpted_rply.ar_results.proc = xdr_void;
288 		if (! xdrrec_skiprecord(xdrs))
289 			return (ct->ct_error.re_status);
290 		/* now decode and validate the response header */
291 		if (! xdr_replymsg(xdrs, &reply_msg)) {
292 			if (ct->ct_error.re_status == RPC_SUCCESS)
293 				continue;
294 			return (ct->ct_error.re_status);
295 		}
296 		if (reply_msg.rm_xid == x_id)
297 			break;
298 	}
299 
300 	/*
301 	 * process header
302 	 */
303 	_seterr_reply(&reply_msg, &(ct->ct_error));
304 	if (ct->ct_error.re_status == RPC_SUCCESS) {
305 		if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
306 			ct->ct_error.re_status = RPC_AUTHERROR;
307 			ct->ct_error.re_why = AUTH_INVALIDRESP;
308 		} else if (! (*xdr_results)(xdrs, results_ptr)) {
309 			if (ct->ct_error.re_status == RPC_SUCCESS)
310 				ct->ct_error.re_status = RPC_CANTDECODERES;
311 		}
312 		/* free verifier ... */
313 		if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
314 			xdrs->x_op = XDR_FREE;
315 			(void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
316 		}
317 	}  /* end successful completion */
318 	else {
319 		/* maybe our credentials need to be refreshed ... */
320 		if (refreshes-- && AUTH_REFRESH(h->cl_auth))
321 			goto call_again;
322 	}  /* end of unsuccessful completion */
323 	return (ct->ct_error.re_status);
324 }
325 
326 static void
327 clnttcp_geterr(h, errp)
328 	CLIENT *h;
329 	struct rpc_err *errp;
330 {
331 	register struct ct_data *ct =
332 	    (struct ct_data *) h->cl_private;
333 
334 	*errp = ct->ct_error;
335 }
336 
337 static bool_t
338 clnttcp_freeres(cl, xdr_res, res_ptr)
339 	CLIENT *cl;
340 	xdrproc_t xdr_res;
341 	caddr_t res_ptr;
342 {
343 	register struct ct_data *ct = (struct ct_data *)cl->cl_private;
344 	register XDR *xdrs = &(ct->ct_xdrs);
345 
346 	xdrs->x_op = XDR_FREE;
347 	return ((*xdr_res)(xdrs, res_ptr));
348 }
349 
350 static void
351 clnttcp_abort()
352 {
353 }
354 
355 static bool_t
356 clnttcp_control(cl, request, info)
357 	CLIENT *cl;
358 	int request;
359 	char *info;
360 {
361 	register struct ct_data *ct = (struct ct_data *)cl->cl_private;
362 
363 	switch (request) {
364 	case CLSET_TIMEOUT:
365 		ct->ct_wait = *(struct timeval *)info;
366 		ct->ct_waitset = TRUE;
367 		break;
368 	case CLGET_TIMEOUT:
369 		*(struct timeval *)info = ct->ct_wait;
370 		break;
371 	case CLGET_SERVER_ADDR:
372 		*(struct sockaddr_in *)info = ct->ct_addr;
373 		break;
374 	default:
375 		return (FALSE);
376 	}
377 	return (TRUE);
378 }
379 
380 
381 static void
382 clnttcp_destroy(h)
383 	CLIENT *h;
384 {
385 	register struct ct_data *ct =
386 	    (struct ct_data *) h->cl_private;
387 
388 	if (ct->ct_closeit) {
389 		(void)close(ct->ct_sock);
390 	}
391 	XDR_DESTROY(&(ct->ct_xdrs));
392 	mem_free((caddr_t)ct, sizeof(struct ct_data));
393 	mem_free((caddr_t)h, sizeof(CLIENT));
394 }
395 
396 /*
397  * Interface between xdr serializer and tcp connection.
398  * Behaves like the system calls, read & write, but keeps some error state
399  * around for the rpc level.
400  */
401 static int
402 readtcp(ct, buf, len)
403 	register struct ct_data *ct;
404 	caddr_t buf;
405 	register int len;
406 {
407 	fd_set *fds, readfds;
408 	struct timeval start, after, duration, delta, tmp;
409 	int r, save_errno;
410 
411 	if (len == 0)
412 		return (0);
413 
414 	if (ct->ct_sock+1 > FD_SETSIZE) {
415 		int bytes = howmany(ct->ct_sock+1, NFDBITS) * sizeof(fd_mask);
416 		fds = (fd_set *)malloc(bytes);
417 		if (fds == NULL)
418 			return (-1);
419 		memset(fds, 0, bytes);
420 	} else {
421 		fds = &readfds;
422 		FD_ZERO(fds);
423 	}
424 
425 	gettimeofday(&start, NULL);
426 	delta = ct->ct_wait;
427 	while (TRUE) {
428 		/* XXX we know the other bits are still clear */
429 		FD_SET(ct->ct_sock, fds);
430 		r = select(ct->ct_sock+1, fds, NULL, NULL, &delta);
431 		save_errno = errno;
432 
433 		gettimeofday(&after, NULL);
434 		timersub(&start, &after, &duration);
435 		timersub(&delta, &duration, &tmp);
436 		delta = tmp;
437 		if (delta.tv_sec < 0 || !timerisset(&delta))
438 			r = 0;
439 
440 		switch (r) {
441 		case 0:
442 			ct->ct_error.re_status = RPC_TIMEDOUT;
443 			if (fds != &readfds)
444 				free(fds);
445 			return (-1);
446 		case -1:
447 			if (errno == EINTR)
448 				continue;
449 			ct->ct_error.re_status = RPC_CANTRECV;
450 			ct->ct_error.re_errno = save_errno;
451 			if (fds != &readfds)
452 				free(fds);
453 			return (-1);
454 		}
455 		break;
456 	}
457 	if (fds != &readfds)
458 		free(fds);
459 
460 	switch (len = read(ct->ct_sock, buf, len)) {
461 	case 0:
462 		/* premature eof */
463 		ct->ct_error.re_errno = ECONNRESET;
464 		ct->ct_error.re_status = RPC_CANTRECV;
465 		len = -1;  /* it's really an error */
466 		break;
467 	case -1:
468 		ct->ct_error.re_errno = errno;
469 		ct->ct_error.re_status = RPC_CANTRECV;
470 		break;
471 	}
472 	return (len);
473 }
474 
475 static int
476 writetcp(ct, buf, len)
477 	struct ct_data *ct;
478 	caddr_t buf;
479 	int len;
480 {
481 	register int i, cnt;
482 
483 	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
484 		if ((i = write(ct->ct_sock, buf, cnt)) == -1) {
485 			ct->ct_error.re_errno = errno;
486 			ct->ct_error.re_status = RPC_CANTSEND;
487 			return (-1);
488 		}
489 	}
490 	return (len);
491 }
492