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