xref: /openbsd-src/lib/libc/rpc/clnt_tcp.c (revision 62a742911104f98b9185b2c6b6007d9b1c36396c)
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.16 1998/05/19 06:58:51 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 <string.h>
56 #include <unistd.h>
57 #include <rpc/rpc.h>
58 #include <sys/socket.h>
59 #include <netdb.h>
60 #include <errno.h>
61 #include <rpc/pmap_clnt.h>
62 
63 #define MCALL_MSG_SIZE 24
64 
65 static int	readtcp();
66 static int	writetcp();
67 
68 static enum clnt_stat	clnttcp_call();
69 static void		clnttcp_abort();
70 static void		clnttcp_geterr();
71 static bool_t		clnttcp_freeres();
72 static bool_t           clnttcp_control();
73 static void		clnttcp_destroy();
74 
75 static struct clnt_ops tcp_ops = {
76 	clnttcp_call,
77 	clnttcp_abort,
78 	clnttcp_geterr,
79 	clnttcp_freeres,
80 	clnttcp_destroy,
81 	clnttcp_control
82 };
83 
84 struct ct_data {
85 	int		ct_sock;
86 	bool_t		ct_closeit;
87 	struct timeval	ct_wait;
88 	bool_t          ct_waitset;       /* wait set by clnt_control? */
89 	struct sockaddr_in ct_addr;
90 	struct rpc_err	ct_error;
91 	char		ct_mcall[MCALL_MSG_SIZE];	/* marshalled callmsg */
92 	u_int		ct_mpos;			/* pos after marshal */
93 	XDR		ct_xdrs;
94 };
95 
96 /*
97  * Create a client handle for a tcp/ip connection.
98  * If *sockp<0, *sockp is set to a newly created TCP socket and it is
99  * connected to raddr.  If *sockp non-negative then
100  * raddr is ignored.  The rpc/tcp package does buffering
101  * similar to stdio, so the client must pick send and receive buffer sizes,];
102  * 0 => use the default.
103  * If raddr->sin_port is 0, then a binder on the remote machine is
104  * consulted for the right port number.
105  * NB: *sockp is copied into a private area.
106  * NB: It is the clients responsibility to close *sockp.
107  * NB: The rpch->cl_auth is set null authentication.  Caller may wish to set this
108  * something more useful.
109  */
110 CLIENT *
111 clnttcp_create(raddr, prog, vers, sockp, sendsz, recvsz)
112 	struct sockaddr_in *raddr;
113 	u_long prog;
114 	u_long vers;
115 	register int *sockp;
116 	u_int sendsz;
117 	u_int recvsz;
118 {
119 	CLIENT *h;
120 	register struct ct_data *ct;
121 	struct timeval now;
122 	struct rpc_msg call_msg;
123 
124 	h  = (CLIENT *)mem_alloc(sizeof(*h));
125 	if (h == NULL) {
126 		(void)fprintf(stderr, "clnttcp_create: out of memory\n");
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)fprintf(stderr, "clnttcp_create: out of memory\n");
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)) == 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 			if (*sockp != -1)
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 = arc4random();
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 = &tcp_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 	if (ct)
220 		mem_free((caddr_t)ct, sizeof(struct ct_data));
221 	if (h)
222 		mem_free((caddr_t)h, sizeof(CLIENT));
223 	return ((CLIENT *)NULL);
224 }
225 
226 static enum clnt_stat
227 clnttcp_call(h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
228 	register CLIENT *h;
229 	u_long proc;
230 	xdrproc_t xdr_args;
231 	caddr_t args_ptr;
232 	xdrproc_t xdr_results;
233 	caddr_t results_ptr;
234 	struct timeval timeout;
235 {
236 	register struct ct_data *ct = (struct ct_data *) h->cl_private;
237 	register XDR *xdrs = &(ct->ct_xdrs);
238 	struct rpc_msg reply_msg;
239 	u_long x_id;
240 	u_int32_t *msg_x_id = (u_int32_t *)(ct->ct_mcall);	/* yuk */
241 	register bool_t shipnow;
242 	int refreshes = 2;
243 
244 	if (!ct->ct_waitset) {
245 		ct->ct_wait = timeout;
246 	}
247 
248 	shipnow =
249 	    (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
250 	    && timeout.tv_usec == 0) ? FALSE : TRUE;
251 
252 call_again:
253 	xdrs->x_op = XDR_ENCODE;
254 	ct->ct_error.re_status = RPC_SUCCESS;
255 	x_id = ntohl(--(*msg_x_id));
256 	if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
257 	    (! XDR_PUTLONG(xdrs, (long *)&proc)) ||
258 	    (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
259 	    (! (*xdr_args)(xdrs, args_ptr))) {
260 		if (ct->ct_error.re_status == RPC_SUCCESS)
261 			ct->ct_error.re_status = RPC_CANTENCODEARGS;
262 		(void)xdrrec_endofrecord(xdrs, TRUE);
263 		return (ct->ct_error.re_status);
264 	}
265 	if (! xdrrec_endofrecord(xdrs, shipnow))
266 		return (ct->ct_error.re_status = RPC_CANTSEND);
267 	if (! shipnow)
268 		return (RPC_SUCCESS);
269 	/*
270 	 * Hack to provide rpc-based message passing
271 	 */
272 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
273 		return(ct->ct_error.re_status = RPC_TIMEDOUT);
274 	}
275 
276 
277 	/*
278 	 * Keep receiving until we get a valid transaction id
279 	 */
280 	xdrs->x_op = XDR_DECODE;
281 	while (TRUE) {
282 		reply_msg.acpted_rply.ar_verf = _null_auth;
283 		reply_msg.acpted_rply.ar_results.where = NULL;
284 		reply_msg.acpted_rply.ar_results.proc = xdr_void;
285 		if (! xdrrec_skiprecord(xdrs))
286 			return (ct->ct_error.re_status);
287 		/* now decode and validate the response header */
288 		if (! xdr_replymsg(xdrs, &reply_msg)) {
289 			if (ct->ct_error.re_status == RPC_SUCCESS)
290 				continue;
291 			return (ct->ct_error.re_status);
292 		}
293 		if (reply_msg.rm_xid == x_id)
294 			break;
295 	}
296 
297 	/*
298 	 * process header
299 	 */
300 	_seterr_reply(&reply_msg, &(ct->ct_error));
301 	if (ct->ct_error.re_status == RPC_SUCCESS) {
302 		if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
303 			ct->ct_error.re_status = RPC_AUTHERROR;
304 			ct->ct_error.re_why = AUTH_INVALIDRESP;
305 		} else if (! (*xdr_results)(xdrs, results_ptr)) {
306 			if (ct->ct_error.re_status == RPC_SUCCESS)
307 				ct->ct_error.re_status = RPC_CANTDECODERES;
308 		}
309 		/* free verifier ... */
310 		if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
311 			xdrs->x_op = XDR_FREE;
312 			(void)xdr_opaque_auth(xdrs, &(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))
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 	default:
372 		return (FALSE);
373 	}
374 	return (TRUE);
375 }
376 
377 
378 static void
379 clnttcp_destroy(h)
380 	CLIENT *h;
381 {
382 	register struct ct_data *ct =
383 	    (struct ct_data *) h->cl_private;
384 
385 	if (ct->ct_closeit) {
386 		(void)close(ct->ct_sock);
387 	}
388 	XDR_DESTROY(&(ct->ct_xdrs));
389 	mem_free((caddr_t)ct, sizeof(struct ct_data));
390 	mem_free((caddr_t)h, sizeof(CLIENT));
391 }
392 
393 /*
394  * Interface between xdr serializer and tcp connection.
395  * Behaves like the system calls, read & write, but keeps some error state
396  * around for the rpc level.
397  */
398 static int
399 readtcp(ct, buf, len)
400 	register struct ct_data *ct;
401 	caddr_t buf;
402 	register int len;
403 {
404 	fd_set *fds, readfds;
405 	struct timeval start, after, duration, delta, tmp;
406 	int r, save_errno;
407 
408 	if (len == 0)
409 		return (0);
410 
411 	if (ct->ct_sock+1 > FD_SETSIZE) {
412 		int bytes = howmany(ct->ct_sock+1, NFDBITS) * sizeof(fd_mask);
413 		fds = (fd_set *)malloc(bytes);
414 		if (fds == NULL)
415 			return (-1);
416 		memset(fds, 0, bytes);
417 	} else {
418 		fds = &readfds;
419 		FD_ZERO(fds);
420 	}
421 
422 	gettimeofday(&start, NULL);
423 	delta = ct->ct_wait;
424 	while (TRUE) {
425 		/* XXX we know the other bits are still clear */
426 		FD_SET(ct->ct_sock, fds);
427 		r = select(ct->ct_sock+1, fds, NULL, NULL, &delta);
428 		save_errno = errno;
429 
430 		gettimeofday(&after, NULL);
431 		timersub(&start, &after, &duration);
432 		timersub(&ct->ct_wait, &duration, &tmp);
433 		delta = tmp;
434 		if (delta.tv_sec < 0 || !timerisset(&delta))
435 			r = 0;
436 
437 		switch (r) {
438 		case 0:
439 			ct->ct_error.re_status = RPC_TIMEDOUT;
440 			if (fds != &readfds)
441 				free(fds);
442 			return (-1);
443 		case -1:
444 			if (errno == EINTR)
445 				continue;
446 			ct->ct_error.re_status = RPC_CANTRECV;
447 			ct->ct_error.re_errno = save_errno;
448 			if (fds != &readfds)
449 				free(fds);
450 			return (-1);
451 		}
452 		break;
453 	}
454 	if (fds != &readfds)
455 		free(fds);
456 
457 	switch (len = read(ct->ct_sock, buf, len)) {
458 	case 0:
459 		/* premature eof */
460 		ct->ct_error.re_errno = ECONNRESET;
461 		ct->ct_error.re_status = RPC_CANTRECV;
462 		len = -1;  /* it's really an error */
463 		break;
464 	case -1:
465 		ct->ct_error.re_errno = errno;
466 		ct->ct_error.re_status = RPC_CANTRECV;
467 		break;
468 	}
469 	return (len);
470 }
471 
472 static int
473 writetcp(ct, buf, len)
474 	struct ct_data *ct;
475 	caddr_t buf;
476 	int len;
477 {
478 	register int i, cnt;
479 
480 	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
481 		if ((i = write(ct->ct_sock, buf, cnt)) == -1) {
482 			ct->ct_error.re_errno = errno;
483 			ct->ct_error.re_status = RPC_CANTSEND;
484 			return (-1);
485 		}
486 	}
487 	return (len);
488 }
489