xref: /openbsd-src/lib/libc/rpc/clnt_tcp.c (revision 8445c53715e7030056b779e8ab40efb7820981f2)
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.18 2001/09/15 13:51:00 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(CLIENT *, u_long, xdrproc_t, caddr_t,
69 			    xdrproc_t, caddr_t, struct timeval);
70 static void		clnttcp_abort(CLIENT *);
71 static void		clnttcp_geterr(CLIENT *, struct rpc_err *);
72 static bool_t		clnttcp_freeres(CLIENT *, xdrproc_t, caddr_t);
73 static bool_t           clnttcp_control(CLIENT *, u_int, void *);
74 static void		clnttcp_destroy(CLIENT *);
75 
76 static struct clnt_ops tcp_ops = {
77 	clnttcp_call,
78 	clnttcp_abort,
79 	clnttcp_geterr,
80 	clnttcp_freeres,
81 	clnttcp_destroy,
82 	clnttcp_control
83 };
84 
85 struct ct_data {
86 	int		ct_sock;
87 	bool_t		ct_closeit;
88 	struct timeval	ct_wait;
89 	bool_t          ct_waitset;       /* wait set by clnt_control? */
90 	struct sockaddr_in ct_addr;
91 	struct rpc_err	ct_error;
92 	char		ct_mcall[MCALL_MSG_SIZE];	/* marshalled callmsg */
93 	u_int		ct_mpos;			/* pos after marshal */
94 	XDR		ct_xdrs;
95 };
96 
97 /*
98  * Create a client handle for a tcp/ip connection.
99  * If *sockp<0, *sockp is set to a newly created TCP socket and it is
100  * connected to raddr.  If *sockp non-negative then
101  * raddr is ignored.  The rpc/tcp package does buffering
102  * similar to stdio, so the client must pick send and receive buffer sizes,];
103  * 0 => use the default.
104  * If raddr->sin_port is 0, then a binder on the remote machine is
105  * consulted for the right port number.
106  * NB: *sockp is copied into a private area.
107  * NB: It is the clients responsibility to close *sockp.
108  * NB: The rpch->cl_auth is set null authentication.  Caller may wish to set this
109  * something more useful.
110  */
111 CLIENT *
112 clnttcp_create(raddr, prog, vers, sockp, sendsz, recvsz)
113 	struct sockaddr_in *raddr;
114 	u_long prog;
115 	u_long vers;
116 	int *sockp;
117 	u_int sendsz;
118 	u_int recvsz;
119 {
120 	CLIENT *h;
121 	struct ct_data *ct = NULL;
122 	struct timeval now;
123 	struct rpc_msg call_msg;
124 
125 	h  = (CLIENT *)mem_alloc(sizeof(*h));
126 	if (h == NULL) {
127 		(void)fprintf(stderr, "clnttcp_create: out of memory\n");
128 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
129 		rpc_createerr.cf_error.re_errno = errno;
130 		goto fooy;
131 	}
132 	ct = (struct ct_data *)mem_alloc(sizeof(*ct));
133 	if (ct == NULL) {
134 		(void)fprintf(stderr, "clnttcp_create: out of memory\n");
135 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
136 		rpc_createerr.cf_error.re_errno = errno;
137 		goto fooy;
138 	}
139 
140 	/*
141 	 * If no port number given ask the pmap for one
142 	 */
143 	if (raddr->sin_port == 0) {
144 		u_short port;
145 		if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 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 		(void)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 			if (*sockp != -1)
165 				(void)close(*sockp);
166 			goto fooy;
167 		}
168 		ct->ct_closeit = TRUE;
169 	} else {
170 		ct->ct_closeit = FALSE;
171 	}
172 
173 	/*
174 	 * Set up private data struct
175 	 */
176 	ct->ct_sock = *sockp;
177 	ct->ct_wait.tv_usec = 0;
178 	ct->ct_waitset = FALSE;
179 	ct->ct_addr = *raddr;
180 
181 	/*
182 	 * Initialize call message
183 	 */
184 	(void)gettimeofday(&now, (struct timezone *)0);
185 	call_msg.rm_xid = arc4random();
186 	call_msg.rm_direction = CALL;
187 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
188 	call_msg.rm_call.cb_prog = prog;
189 	call_msg.rm_call.cb_vers = vers;
190 
191 	/*
192 	 * pre-serialize the staic part of the call msg and stash it away
193 	 */
194 	xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
195 	    XDR_ENCODE);
196 	if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
197 		if (ct->ct_closeit) {
198 			(void)close(*sockp);
199 		}
200 		goto fooy;
201 	}
202 	ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
203 	XDR_DESTROY(&(ct->ct_xdrs));
204 
205 	/*
206 	 * Create a client handle which uses xdrrec for serialization
207 	 * and authnone for authentication.
208 	 */
209 	xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
210 	    (caddr_t)ct, readtcp, writetcp);
211 	h->cl_ops = &tcp_ops;
212 	h->cl_private = (caddr_t) ct;
213 	h->cl_auth = authnone_create();
214 	return (h);
215 
216 fooy:
217 	/*
218 	 * Something goofed, free stuff and barf
219 	 */
220 	if (ct)
221 		mem_free((caddr_t)ct, sizeof(struct ct_data));
222 	if (h)
223 		mem_free((caddr_t)h, sizeof(CLIENT));
224 	return ((CLIENT *)NULL);
225 }
226 
227 static enum clnt_stat
228 clnttcp_call(h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
229 	CLIENT *h;
230 	u_long proc;
231 	xdrproc_t xdr_args;
232 	caddr_t args_ptr;
233 	xdrproc_t xdr_results;
234 	caddr_t results_ptr;
235 	struct timeval timeout;
236 {
237 	struct ct_data *ct = (struct ct_data *) h->cl_private;
238 	XDR *xdrs = &(ct->ct_xdrs);
239 	struct rpc_msg reply_msg;
240 	u_long x_id;
241 	u_int32_t *msg_x_id = (u_int32_t *)(ct->ct_mcall);	/* yuk */
242 	bool_t shipnow;
243 	int refreshes = 2;
244 
245 	if (!ct->ct_waitset) {
246 		ct->ct_wait = timeout;
247 	}
248 
249 	shipnow =
250 	    (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
251 	    && timeout.tv_usec == 0) ? FALSE : TRUE;
252 
253 call_again:
254 	xdrs->x_op = XDR_ENCODE;
255 	ct->ct_error.re_status = RPC_SUCCESS;
256 	x_id = ntohl(--(*msg_x_id));
257 	if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
258 	    (! XDR_PUTLONG(xdrs, (long *)&proc)) ||
259 	    (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
260 	    (! (*xdr_args)(xdrs, args_ptr))) {
261 		if (ct->ct_error.re_status == RPC_SUCCESS)
262 			ct->ct_error.re_status = RPC_CANTENCODEARGS;
263 		(void)xdrrec_endofrecord(xdrs, TRUE);
264 		return (ct->ct_error.re_status);
265 	}
266 	if (! xdrrec_endofrecord(xdrs, shipnow))
267 		return (ct->ct_error.re_status = RPC_CANTSEND);
268 	if (! shipnow)
269 		return (RPC_SUCCESS);
270 	/*
271 	 * Hack to provide rpc-based message passing
272 	 */
273 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
274 		return(ct->ct_error.re_status = RPC_TIMEDOUT);
275 	}
276 
277 
278 	/*
279 	 * Keep receiving until we get a valid transaction id
280 	 */
281 	xdrs->x_op = XDR_DECODE;
282 	while (TRUE) {
283 		reply_msg.acpted_rply.ar_verf = _null_auth;
284 		reply_msg.acpted_rply.ar_results.where = NULL;
285 		reply_msg.acpted_rply.ar_results.proc = xdr_void;
286 		if (! xdrrec_skiprecord(xdrs))
287 			return (ct->ct_error.re_status);
288 		/* now decode and validate the response header */
289 		if (! xdr_replymsg(xdrs, &reply_msg)) {
290 			if (ct->ct_error.re_status == RPC_SUCCESS)
291 				continue;
292 			return (ct->ct_error.re_status);
293 		}
294 		if (reply_msg.rm_xid == x_id)
295 			break;
296 	}
297 
298 	/*
299 	 * process header
300 	 */
301 	_seterr_reply(&reply_msg, &(ct->ct_error));
302 	if (ct->ct_error.re_status == RPC_SUCCESS) {
303 		if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
304 			ct->ct_error.re_status = RPC_AUTHERROR;
305 			ct->ct_error.re_why = AUTH_INVALIDRESP;
306 		} else if (! (*xdr_results)(xdrs, results_ptr)) {
307 			if (ct->ct_error.re_status == RPC_SUCCESS)
308 				ct->ct_error.re_status = RPC_CANTDECODERES;
309 		}
310 		/* free verifier ... */
311 		if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
312 			xdrs->x_op = XDR_FREE;
313 			(void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
314 		}
315 	}  /* end successful completion */
316 	else {
317 		/* maybe our credentials need to be refreshed ... */
318 		if (refreshes-- && AUTH_REFRESH(h->cl_auth))
319 			goto call_again;
320 	}  /* end of unsuccessful completion */
321 	return (ct->ct_error.re_status);
322 }
323 
324 static void
325 clnttcp_geterr(h, errp)
326 	CLIENT *h;
327 	struct rpc_err *errp;
328 {
329 	struct ct_data *ct =
330 	    (struct ct_data *) h->cl_private;
331 
332 	*errp = ct->ct_error;
333 }
334 
335 static bool_t
336 clnttcp_freeres(cl, xdr_res, res_ptr)
337 	CLIENT *cl;
338 	xdrproc_t xdr_res;
339 	caddr_t res_ptr;
340 {
341 	struct ct_data *ct = (struct ct_data *)cl->cl_private;
342 	XDR *xdrs = &(ct->ct_xdrs);
343 
344 	xdrs->x_op = XDR_FREE;
345 	return ((*xdr_res)(xdrs, res_ptr));
346 }
347 
348 static void
349 clnttcp_abort(CLIENT *clnt)
350 {
351 }
352 
353 static bool_t
354 clnttcp_control(cl, request, info)
355 	CLIENT *cl;
356 	u_int request;
357 	void *info;
358 {
359 	struct ct_data *ct = (struct ct_data *)cl->cl_private;
360 
361 	switch (request) {
362 	case CLSET_TIMEOUT:
363 		ct->ct_wait = *(struct timeval *)info;
364 		ct->ct_waitset = TRUE;
365 		break;
366 	case CLGET_TIMEOUT:
367 		*(struct timeval *)info = ct->ct_wait;
368 		break;
369 	case CLGET_SERVER_ADDR:
370 		*(struct sockaddr_in *)info = ct->ct_addr;
371 		break;
372 	default:
373 		return (FALSE);
374 	}
375 	return (TRUE);
376 }
377 
378 
379 static void
380 clnttcp_destroy(h)
381 	CLIENT *h;
382 {
383 	struct ct_data *ct =
384 	    (struct ct_data *) h->cl_private;
385 
386 	if (ct->ct_closeit) {
387 		(void)close(ct->ct_sock);
388 	}
389 	XDR_DESTROY(&(ct->ct_xdrs));
390 	mem_free((caddr_t)ct, sizeof(struct ct_data));
391 	mem_free((caddr_t)h, sizeof(CLIENT));
392 }
393 
394 /*
395  * Interface between xdr serializer and tcp connection.
396  * Behaves like the system calls, read & write, but keeps some error state
397  * around for the rpc level.
398  */
399 static int
400 readtcp(ct, buf, len)
401 	struct ct_data *ct;
402 	caddr_t buf;
403 	int len;
404 {
405 	fd_set *fds, readfds;
406 	struct timeval start, after, duration, delta, tmp;
407 	int r, save_errno;
408 
409 	if (len == 0)
410 		return (0);
411 
412 	if (ct->ct_sock+1 > FD_SETSIZE) {
413 		int bytes = howmany(ct->ct_sock+1, NFDBITS) * sizeof(fd_mask);
414 		fds = (fd_set *)malloc(bytes);
415 		if (fds == NULL)
416 			return (-1);
417 		memset(fds, 0, bytes);
418 	} else {
419 		fds = &readfds;
420 		FD_ZERO(fds);
421 	}
422 
423 	gettimeofday(&start, NULL);
424 	delta = ct->ct_wait;
425 	while (TRUE) {
426 		/* XXX we know the other bits are still clear */
427 		FD_SET(ct->ct_sock, fds);
428 		r = select(ct->ct_sock+1, fds, NULL, NULL, &delta);
429 		save_errno = errno;
430 
431 		gettimeofday(&after, NULL);
432 		timersub(&start, &after, &duration);
433 		timersub(&ct->ct_wait, &duration, &tmp);
434 		delta = tmp;
435 		if (delta.tv_sec < 0 || !timerisset(&delta))
436 			r = 0;
437 
438 		switch (r) {
439 		case 0:
440 			ct->ct_error.re_status = RPC_TIMEDOUT;
441 			if (fds != &readfds)
442 				free(fds);
443 			return (-1);
444 		case -1:
445 			if (errno == EINTR)
446 				continue;
447 			ct->ct_error.re_status = RPC_CANTRECV;
448 			ct->ct_error.re_errno = save_errno;
449 			if (fds != &readfds)
450 				free(fds);
451 			return (-1);
452 		}
453 		break;
454 	}
455 	if (fds != &readfds)
456 		free(fds);
457 
458 	switch (len = read(ct->ct_sock, buf, len)) {
459 	case 0:
460 		/* premature eof */
461 		ct->ct_error.re_errno = ECONNRESET;
462 		ct->ct_error.re_status = RPC_CANTRECV;
463 		len = -1;  /* it's really an error */
464 		break;
465 	case -1:
466 		ct->ct_error.re_errno = errno;
467 		ct->ct_error.re_status = RPC_CANTRECV;
468 		break;
469 	}
470 	return (len);
471 }
472 
473 static int
474 writetcp(ct, buf, len)
475 	struct ct_data *ct;
476 	caddr_t buf;
477 	int len;
478 {
479 	int i, cnt;
480 
481 	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
482 		if ((i = write(ct->ct_sock, buf, cnt)) == -1) {
483 			ct->ct_error.re_errno = errno;
484 			ct->ct_error.re_status = RPC_CANTSEND;
485 			return (-1);
486 		}
487 	}
488 	return (len);
489 }
490