xref: /freebsd/crypto/krb5/src/lib/rpc/clnt_tcp.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* @(#)clnt_tcp.c	2.2 88/08/01 4.0 RPCSRC */
2 /*
3  * Copyright (c) 2010, Oracle America, Inc.
4  *
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *
18  *     * Neither the name of the "Oracle America, Inc." nor the names of
19  *       its contributors may be used to endorse or promote products
20  *       derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
23  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 #if !defined(lint) && defined(SCCSIDS)
35 static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";
36 #endif
37 
38 /*
39  * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
40  *
41  * TCP based RPC supports 'batched calls'.
42  * A sequence of calls may be batched-up in a send buffer.  The rpc call
43  * return immediately to the client even though the call was not necessarily
44  * sent.  The batching occurs if the results' xdr routine is NULL (0) AND
45  * the rpc timeout value is zero (see clnt.h, rpc).
46  *
47  * Clients should NOT casually batch calls that in fact return results; that is,
48  * the server side should be aware that a call is batched and not produce any
49  * return message.  Batched calls that produce many result messages can
50  * deadlock (netlock) the client and the server....
51  *
52  * Now go hang yourself.
53  */
54 
55 #include <stdio.h>
56 #include <unistd.h>
57 #include <gssrpc/rpc.h>
58 #include <sys/socket.h>
59 #include <netdb.h>
60 #include <errno.h>
61 #include <string.h>
62 #include <gssrpc/pmap_clnt.h>
63 /* FD_ZERO may need memset declaration (e.g., Solaris 9) */
64 #include <string.h>
65 #include <port-sockets.h>
66 
67 #define MCALL_MSG_SIZE 24
68 
69 #ifndef GETSOCKNAME_ARG3_TYPE
70 #define GETSOCKNAME_ARG3_TYPE int
71 #endif
72 
73 static enum clnt_stat	clnttcp_call(CLIENT *, rpcproc_t, xdrproc_t, void *,
74 				     xdrproc_t, void *, struct timeval);
75 static void		clnttcp_abort(CLIENT *);
76 static void		clnttcp_geterr(CLIENT *, struct rpc_err *);
77 static bool_t		clnttcp_freeres(CLIENT *, xdrproc_t, void *);
78 static bool_t           clnttcp_control(CLIENT *, int, void *);
79 static void		clnttcp_destroy(CLIENT *);
80 
81 static struct clnt_ops tcp_ops = {
82 	clnttcp_call,
83 	clnttcp_abort,
84 	clnttcp_geterr,
85 	clnttcp_freeres,
86 	clnttcp_destroy,
87 	clnttcp_control
88 };
89 
90 struct ct_data {
91 	int		ct_sock;
92 	bool_t		ct_closeit;
93 	struct timeval	ct_wait;
94 	bool_t          ct_waitset;       /* wait set by clnt_control? */
95 	struct sockaddr_in ct_addr;
96 	struct rpc_err	ct_error;
97 	union {
98 	  char		ct_mcall[MCALL_MSG_SIZE];	/* marshalled callmsg */
99 	  uint32_t   ct_mcalli;
100 	} ct_u;
101 	u_int		ct_mpos;			/* pos after marshal */
102 	XDR		ct_xdrs;
103 };
104 
105 static int	readtcp(char *, caddr_t, int);
106 static int	writetcp(char *, caddr_t, int);
107 
108 
109 /*
110  * Create a client handle for a tcp/ip connection.
111  * If *sockp<0, *sockp is set to a newly created TCP socket and it is
112  * connected to raddr.  If *sockp non-negative then
113  * raddr is ignored.  The rpc/tcp package does buffering
114  * similar to stdio, so the client must pick send and receive buffer sizes,];
115  * 0 => use the default.
116  * If raddr->sin_port is 0, then a binder on the remote machine is
117  * consulted for the right port number.
118  * NB: *sockp is copied into a private area.
119  * NB: It is the clients responsibility to close *sockp.
120  * NB: The rpch->cl_auth is set null authentication.  Caller may wish to set this
121  * something more useful.
122  */
123 CLIENT *
clnttcp_create(struct sockaddr_in * raddr,rpcprog_t prog,rpcvers_t vers,SOCKET * sockp,u_int sendsz,u_int recvsz)124 clnttcp_create(
125 	struct sockaddr_in *raddr,
126 	rpcprog_t prog,
127 	rpcvers_t vers,
128         SOCKET *sockp,
129 	u_int sendsz,
130 	u_int recvsz)
131 {
132 	CLIENT *h;
133 	struct ct_data *ct = 0;
134 	struct timeval now;
135 	struct rpc_msg call_msg;
136 
137 	h  = (CLIENT *)mem_alloc(sizeof(*h));
138 	if (h == NULL) {
139 		(void)fprintf(stderr, "clnttcp_create: out of memory\n");
140 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
141 		rpc_createerr.cf_error.re_errno = errno;
142 		goto fooy;
143 	}
144 	ct = (struct ct_data *)mem_alloc(sizeof(*ct));
145 	if (ct == NULL) {
146 		(void)fprintf(stderr, "clnttcp_create: out of memory\n");
147 		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
148 		rpc_createerr.cf_error.re_errno = errno;
149 		goto fooy;
150 	}
151 
152 	/*
153 	 * If no port number given ask the pmap for one
154 	 */
155 	if (raddr != NULL && raddr->sin_port == 0) {
156 		u_short port;
157 		if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) {
158 			mem_free((caddr_t)ct, sizeof(struct ct_data));
159 			mem_free((caddr_t)h, sizeof(CLIENT));
160 			return ((CLIENT *)NULL);
161 		}
162 		raddr->sin_port = htons(port);
163 	}
164 
165 	/*
166 	 * If no socket given, open one
167 	 */
168 	if (*sockp < 0) {
169 		*sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
170 		(void)bindresvport_sa(*sockp, NULL);
171 		if (*sockp < 0 || raddr == NULL ||
172 		    connect(*sockp, (struct sockaddr *)raddr,
173 			    sizeof(*raddr)) < 0) {
174 			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
175 			rpc_createerr.cf_error.re_errno = errno;
176                         (void)closesocket(*sockp);
177 			goto fooy;
178 		}
179 		ct->ct_closeit = TRUE;
180 	} else {
181 		ct->ct_closeit = FALSE;
182 	}
183 
184 	/*
185 	 * Set up private data struct
186 	 */
187 	ct->ct_sock = *sockp;
188 	ct->ct_wait.tv_usec = 0;
189 	ct->ct_waitset = FALSE;
190 	if (raddr == NULL) {
191 	    /* Get the remote address from the socket, if it's IPv4. */
192 	    struct sockaddr_in sin;
193 	    socklen_t len = sizeof(sin);
194 	    int ret = getpeername(ct->ct_sock, (struct sockaddr *)&sin, &len);
195 	    if (ret == 0 && len == sizeof(sin) && sin.sin_family == AF_INET)
196 		ct->ct_addr = sin;
197 	    else
198 		memset(&ct->ct_addr, 0, sizeof(ct->ct_addr));
199 	} else
200 	    ct->ct_addr = *raddr;
201 
202 	/*
203 	 * Initialize call message
204 	 */
205 	(void)gettimeofday(&now, (struct timezone *)0);
206 	call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
207 	call_msg.rm_direction = CALL;
208 	call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
209 	call_msg.rm_call.cb_prog = prog;
210 	call_msg.rm_call.cb_vers = vers;
211 
212 	/*
213 	 * pre-serialize the staic part of the call msg and stash it away
214 	 */
215 	xdrmem_create(&(ct->ct_xdrs), ct->ct_u.ct_mcall, MCALL_MSG_SIZE,
216 	    XDR_ENCODE);
217 	if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
218 		if (ct->ct_closeit)
219                         (void)closesocket(*sockp);
220 		goto fooy;
221 	}
222 	ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
223 	XDR_DESTROY(&(ct->ct_xdrs));
224 
225 	/*
226 	 * Create a client handle which uses xdrrec for serialization
227 	 * and authnone for authentication.
228 	 */
229 	xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
230 	    (caddr_t)ct, readtcp, writetcp);
231 	h->cl_ops = &tcp_ops;
232 	h->cl_private = (caddr_t) ct;
233 	h->cl_auth = authnone_create();
234 	return (h);
235 
236 fooy:
237 	/*
238 	 * Something goofed, free stuff and barf
239 	 */
240 	mem_free((caddr_t)ct, sizeof(struct ct_data));
241 	mem_free((caddr_t)h, sizeof(CLIENT));
242 	return ((CLIENT *)NULL);
243 }
244 
245 static enum clnt_stat
clnttcp_call(CLIENT * h,rpcproc_t proc,xdrproc_t xdr_args,void * args_ptr,xdrproc_t xdr_results,void * results_ptr,struct timeval timeout)246 clnttcp_call(
247 	CLIENT *h,
248 	rpcproc_t proc,
249 	xdrproc_t xdr_args,
250 	void * args_ptr,
251 	xdrproc_t xdr_results,
252 	void * results_ptr,
253 	struct timeval timeout)
254 {
255 	struct ct_data *ct = h->cl_private;
256 	XDR *xdrs = &ct->ct_xdrs;
257 	struct rpc_msg reply_msg;
258 	uint32_t x_id;
259 	uint32_t *msg_x_id = &ct->ct_u.ct_mcalli;	/* yuk */
260 	bool_t shipnow;
261 	int refreshes = 2;
262 	long procl = proc;
263 
264 	if (!ct->ct_waitset) {
265 		ct->ct_wait = timeout;
266 	}
267 
268 	shipnow =
269 	    (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
270 	    && timeout.tv_usec == 0) ? FALSE : TRUE;
271 
272 call_again:
273 	xdrs->x_op = XDR_ENCODE;
274 	ct->ct_error.re_status = RPC_SUCCESS;
275 	x_id = ntohl(--(*msg_x_id));
276 	if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcall, ct->ct_mpos)) ||
277 	    (! XDR_PUTLONG(xdrs, &procl)) ||
278 	    (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
279 	    (! AUTH_WRAP(h->cl_auth, xdrs, xdr_args, args_ptr))) {
280 		if (ct->ct_error.re_status == RPC_SUCCESS)
281 			ct->ct_error.re_status = RPC_CANTENCODEARGS;
282 		(void)xdrrec_endofrecord(xdrs, TRUE);
283 		return (ct->ct_error.re_status);
284 	}
285 	if (! xdrrec_endofrecord(xdrs, shipnow))
286 		return (ct->ct_error.re_status = RPC_CANTSEND);
287 	if (! shipnow)
288 		return (RPC_SUCCESS);
289 	/*
290 	 * Hack to provide rpc-based message passing
291 	 */
292 	if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
293 		return(ct->ct_error.re_status = RPC_TIMEDOUT);
294 	}
295 
296 
297 	/*
298 	 * Keep receiving until we get a valid transaction id
299 	 */
300 	xdrs->x_op = XDR_DECODE;
301 	while (TRUE) {
302 		reply_msg.acpted_rply.ar_verf = gssrpc__null_auth;
303 		reply_msg.acpted_rply.ar_results.where = NULL;
304 		reply_msg.acpted_rply.ar_results.proc = xdr_void;
305 		if (! xdrrec_skiprecord(xdrs))
306 			return (ct->ct_error.re_status);
307 		/* now decode and validate the response header */
308 		if (! xdr_replymsg(xdrs, &reply_msg)) {
309 			/*
310 			 * Free some stuff allocated by xdr_replymsg()
311 			 * to avoid leaks, since it may allocate
312 			 * memory from partially successful decodes.
313 			 */
314 			enum xdr_op op = xdrs->x_op;
315 			xdrs->x_op = XDR_FREE;
316 			xdr_replymsg(xdrs, &reply_msg);
317 			xdrs->x_op = op;
318 			if (ct->ct_error.re_status == RPC_SUCCESS)
319 				continue;
320 			return (ct->ct_error.re_status);
321 		}
322 		if (reply_msg.rm_xid == x_id)
323 			break;
324 	}
325 
326 	/*
327 	 * process header
328 	 */
329 	gssrpc__seterr_reply(&reply_msg, &(ct->ct_error));
330 	if (ct->ct_error.re_status == RPC_SUCCESS) {
331 		if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
332 			ct->ct_error.re_status = RPC_AUTHERROR;
333 			ct->ct_error.re_why = AUTH_INVALIDRESP;
334 		} else if (! AUTH_UNWRAP(h->cl_auth, xdrs,
335 					 xdr_results, results_ptr)) {
336 			if (ct->ct_error.re_status == RPC_SUCCESS)
337 				ct->ct_error.re_status = RPC_CANTDECODERES;
338 		}
339 	}  /* end successful completion */
340 	else {
341 		/* maybe our credentials need to be refreshed ... */
342 		if (refreshes-- && AUTH_REFRESH(h->cl_auth, &reply_msg))
343 			goto call_again;
344 	}  /* end of unsuccessful completion */
345 	/* free verifier ... */
346 	if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
347 	    (reply_msg.acpted_rply.ar_verf.oa_base != NULL)) {
348 	    xdrs->x_op = XDR_FREE;
349 	    (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
350 	}
351 	return (ct->ct_error.re_status);
352 }
353 
354 static void
clnttcp_geterr(CLIENT * h,struct rpc_err * errp)355 clnttcp_geterr(
356 	CLIENT *h,
357 	struct rpc_err *errp)
358 {
359 	struct ct_data *ct = h->cl_private;
360 
361 	*errp = ct->ct_error;
362 }
363 
364 static bool_t
clnttcp_freeres(CLIENT * cl,xdrproc_t xdr_res,void * res_ptr)365 clnttcp_freeres(
366 	CLIENT *cl,
367 	xdrproc_t xdr_res,
368 	void * res_ptr)
369 {
370 	struct ct_data *ct = cl->cl_private;
371 	XDR *xdrs = &ct->ct_xdrs;
372 
373 	xdrs->x_op = XDR_FREE;
374 	return ((*xdr_res)(xdrs, res_ptr));
375 }
376 
377 /*ARGSUSED*/
378 static void
clnttcp_abort(CLIENT * cl)379 clnttcp_abort(CLIENT *cl)
380 {
381 }
382 
383 static bool_t
clnttcp_control(CLIENT * cl,int request,void * info)384 clnttcp_control(
385 	CLIENT *cl,
386 	int request,
387 	void *info)
388 {
389 	struct ct_data *ct = cl->cl_private;
390 	GETSOCKNAME_ARG3_TYPE len;
391 
392 	switch (request) {
393 	case CLSET_TIMEOUT:
394 		ct->ct_wait = *(struct timeval *)info;
395 		ct->ct_waitset = TRUE;
396 		break;
397 	case CLGET_TIMEOUT:
398 		*(struct timeval *)info = ct->ct_wait;
399 		break;
400 	case CLGET_SERVER_ADDR:
401 		*(struct sockaddr_in *)info = ct->ct_addr;
402 		break;
403 	case CLGET_LOCAL_ADDR:
404 		len = sizeof(struct sockaddr);
405 		if (getsockname(ct->ct_sock, (struct sockaddr*)info, &len) < 0)
406 		     return FALSE;
407 		else
408 		     return TRUE;
409 	default:
410 		return (FALSE);
411 	}
412 	return (TRUE);
413 }
414 
415 
416 static void
clnttcp_destroy(CLIENT * h)417 clnttcp_destroy(CLIENT *h)
418 {
419 	struct ct_data *ct = h->cl_private;
420 
421 	if (ct->ct_closeit)
422                 (void)closesocket(ct->ct_sock);
423 	XDR_DESTROY(&(ct->ct_xdrs));
424 	mem_free((caddr_t)ct, sizeof(struct ct_data));
425 	mem_free((caddr_t)h, sizeof(CLIENT));
426 }
427 
428 /*
429  * Interface between xdr serializer and tcp connection.
430  * Behaves like the system calls, read & write, but keeps some error state
431  * around for the rpc level.
432  */
433 static int
readtcp(char * ctptr,caddr_t buf,int len)434 readtcp(
435         char *ctptr,
436 	caddr_t buf,
437 	int len)
438 {
439   struct ct_data *ct = (void *)ctptr;
440   struct timeval tout;
441 #ifdef FD_SETSIZE
442 	fd_set mask;
443 	fd_set readfds;
444 
445 	if (len == 0)
446 		return (0);
447 	FD_ZERO(&mask);
448 	FD_SET(ct->ct_sock, &mask);
449 #else
450 	int mask = 1 << (ct->ct_sock);
451 	int readfds;
452 
453 	if (len == 0)
454 		return (0);
455 
456 #endif /* def FD_SETSIZE */
457 	while (TRUE) {
458 		readfds = mask;
459 		tout = ct->ct_wait;
460 		switch (select(gssrpc__rpc_dtablesize(), &readfds, (fd_set*)NULL, (fd_set*)NULL,
461 			       &tout)) {
462 		case 0:
463 			ct->ct_error.re_status = RPC_TIMEDOUT;
464 			return (-1);
465 
466 		case -1:
467 			if (errno == EINTR)
468 				continue;
469 			ct->ct_error.re_status = RPC_CANTRECV;
470 			ct->ct_error.re_errno = errno;
471 			return (-1);
472 		}
473 		break;
474 	}
475 	switch (len = read(ct->ct_sock, buf, (size_t) len)) {
476 
477 	case 0:
478 		/* premature eof */
479 		ct->ct_error.re_errno = ECONNRESET;
480 		ct->ct_error.re_status = RPC_CANTRECV;
481 		len = -1;  /* it's really an error */
482 		break;
483 
484 	case -1:
485 		ct->ct_error.re_errno = errno;
486 		ct->ct_error.re_status = RPC_CANTRECV;
487 		break;
488 	}
489 	return (len);
490 }
491 
492 static int
writetcp(char * ctptr,caddr_t buf,int len)493 writetcp(
494         char *ctptr,
495 	caddr_t buf,
496 	int len)
497 {
498 	struct ct_data *ct = (struct ct_data *)(void *)ctptr;
499 	int i, cnt;
500 
501 	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
502 		if ((i = write(ct->ct_sock, buf, (size_t) cnt)) == -1) {
503 			ct->ct_error.re_errno = errno;
504 			ct->ct_error.re_status = RPC_CANTSEND;
505 			return (-1);
506 		}
507 	}
508 	return (len);
509 }
510