xref: /openbsd/lib/libc/rpc/svc_tcp.c (revision f2dfb0a4)
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: svc_tcp.c,v 1.18 1998/05/22 04:23:01 deraadt Exp $";
32 #endif /* LIBC_SCCS and not lint */
33 
34 /*
35  * svc_tcp.c, Server side for TCP/IP based RPC.
36  *
37  * Copyright (C) 1984, Sun Microsystems, Inc.
38  *
39  * Actually implements two flavors of transporter -
40  * a tcp rendezvouser (a listner and connection establisher)
41  * and a record/tcp stream.
42  */
43 
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <rpc/rpc.h>
49 #include <sys/socket.h>
50 #include <errno.h>
51 
52 #include <netinet/in_systm.h>
53 #include <netinet/in.h>
54 #include <netinet/ip.h>
55 #include <netinet/ip_var.h>
56 
57 /*
58  * Ops vector for TCP/IP based rpc service handle
59  */
60 static bool_t		svctcp_recv();
61 static enum xprt_stat	svctcp_stat();
62 static bool_t		svctcp_getargs();
63 static bool_t		svctcp_reply();
64 static bool_t		svctcp_freeargs();
65 static void		svctcp_destroy();
66 
67 static struct xp_ops svctcp_op = {
68 	svctcp_recv,
69 	svctcp_stat,
70 	svctcp_getargs,
71 	svctcp_reply,
72 	svctcp_freeargs,
73 	svctcp_destroy
74 };
75 
76 /*
77  * Ops vector for TCP/IP rendezvous handler
78  */
79 static bool_t		rendezvous_request();
80 static enum xprt_stat	rendezvous_stat();
81 
82 static struct xp_ops svctcp_rendezvous_op = {
83 	rendezvous_request,
84 	rendezvous_stat,
85 	(bool_t (*)())abort,
86 	(bool_t (*)())abort,
87 	(bool_t (*)())abort,
88 	svctcp_destroy
89 };
90 
91 static int readtcp(), writetcp();
92 static SVCXPRT *makefd_xprt();
93 
94 struct tcp_rendezvous { /* kept in xprt->xp_p1 */
95 	u_int sendsize;
96 	u_int recvsize;
97 };
98 
99 struct tcp_conn {  /* kept in xprt->xp_p1 */
100 	enum xprt_stat strm_stat;
101 	u_long x_id;
102 	XDR xdrs;
103 	char verf_body[MAX_AUTH_BYTES];
104 };
105 
106 /*
107  * Usage:
108  *	xprt = svctcp_create(sock, send_buf_size, recv_buf_size);
109  *
110  * Creates, registers, and returns a (rpc) tcp based transporter.
111  * Once *xprt is initialized, it is registered as a transporter
112  * see (svc.h, xprt_register).  This routine returns
113  * a NULL if a problem occurred.
114  *
115  * If sock<0 then a socket is created, else sock is used.
116  * If the socket, sock is not bound to a port then svctcp_create
117  * binds it to an arbitrary port.  The routine then starts a tcp
118  * listener on the socket's associated port.  In any (successful) case,
119  * xprt->xp_sock is the registered socket number and xprt->xp_port is the
120  * associated port number.
121  *
122  * Since tcp streams do buffered io similar to stdio, the caller can specify
123  * how big the send and receive buffers are via the second and third parms;
124  * 0 => use the system default.
125  */
126 SVCXPRT *
127 svctcp_create(sock, sendsize, recvsize)
128 	register int sock;
129 	u_int sendsize;
130 	u_int recvsize;
131 {
132 	bool_t madesock = FALSE;
133 	register SVCXPRT *xprt;
134 	register struct tcp_rendezvous *r;
135 	struct sockaddr_in addr;
136 	int len = sizeof(struct sockaddr_in);
137 
138 	if (sock == RPC_ANYSOCK) {
139 		if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
140 			perror("svctcp_.c - udp socket creation problem");
141 			return ((SVCXPRT *)NULL);
142 		}
143 		madesock = TRUE;
144 	}
145 	memset(&addr, 0, sizeof (addr));
146 	addr.sin_len = sizeof(struct sockaddr_in);
147 	addr.sin_family = AF_INET;
148 	if (bindresvport(sock, &addr)) {
149 		addr.sin_port = 0;
150 		(void)bind(sock, (struct sockaddr *)&addr, len);
151 	}
152 	if ((getsockname(sock, (struct sockaddr *)&addr, &len) != 0)  ||
153 	    (listen(sock, 2) != 0)) {
154 		perror("svctcp_.c - cannot getsockname or listen");
155 		if (madesock)
156 			(void)close(sock);
157 		return ((SVCXPRT *)NULL);
158 	}
159 	r = (struct tcp_rendezvous *)mem_alloc(sizeof(*r));
160 	if (r == NULL) {
161 		(void)fprintf(stderr, "svctcp_create: out of memory\n");
162 		if (madesock)
163 			(void)close(sock);
164 		return (NULL);
165 	}
166 	r->sendsize = sendsize;
167 	r->recvsize = recvsize;
168 	xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
169 	if (xprt == NULL) {
170 		(void)fprintf(stderr, "svctcp_create: out of memory\n");
171 		if (madesock)
172 			(void)close(sock);
173 		free(r);
174 		return (NULL);
175 	}
176 	xprt->xp_p2 = NULL;
177 	xprt->xp_p1 = (caddr_t)r;
178 	xprt->xp_verf = _null_auth;
179 	xprt->xp_ops = &svctcp_rendezvous_op;
180 	xprt->xp_port = ntohs(addr.sin_port);
181 	xprt->xp_sock = sock;
182 	xprt_register(xprt);
183 	return (xprt);
184 }
185 
186 /*
187  * Like svtcp_create(), except the routine takes any *open* UNIX file
188  * descriptor as its first input.
189  */
190 SVCXPRT *
191 svcfd_create(fd, sendsize, recvsize)
192 	int fd;
193 	u_int sendsize;
194 	u_int recvsize;
195 {
196 
197 	return (makefd_xprt(fd, sendsize, recvsize));
198 }
199 
200 static SVCXPRT *
201 makefd_xprt(fd, sendsize, recvsize)
202 	int fd;
203 	u_int sendsize;
204 	u_int recvsize;
205 {
206 	register SVCXPRT *xprt;
207 	register struct tcp_conn *cd;
208 
209 	xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
210 	if (xprt == (SVCXPRT *)NULL) {
211 		(void) fprintf(stderr, "svc_tcp: makefd_xprt: out of memory\n");
212 		goto done;
213 	}
214 	cd = (struct tcp_conn *)mem_alloc(sizeof(struct tcp_conn));
215 	if (cd == (struct tcp_conn *)NULL) {
216 		(void) fprintf(stderr, "svc_tcp: makefd_xprt: out of memory\n");
217 		mem_free((char *) xprt, sizeof(SVCXPRT));
218 		xprt = (SVCXPRT *)NULL;
219 		goto done;
220 	}
221 	cd->strm_stat = XPRT_IDLE;
222 	xdrrec_create(&(cd->xdrs), sendsize, recvsize,
223 	    (caddr_t)xprt, readtcp, writetcp);
224 	xprt->xp_p2 = NULL;
225 	xprt->xp_p1 = (caddr_t)cd;
226 	xprt->xp_verf.oa_base = cd->verf_body;
227 	xprt->xp_addrlen = 0;
228 	xprt->xp_ops = &svctcp_op;  /* truely deals with calls */
229 	xprt->xp_port = 0;  /* this is a connection, not a rendezvouser */
230 	xprt->xp_sock = fd;
231 	xprt_register(xprt);
232     done:
233 	return (xprt);
234 }
235 
236 static bool_t
237 rendezvous_request(xprt)
238 	register SVCXPRT *xprt;
239 {
240 	int sock;
241 	struct tcp_rendezvous *r;
242 	struct sockaddr_in addr;
243 	int len;
244 
245 	r = (struct tcp_rendezvous *)xprt->xp_p1;
246     again:
247 	len = sizeof(struct sockaddr_in);
248 	if ((sock = accept(xprt->xp_sock, (struct sockaddr *)&addr,
249 	    &len)) < 0) {
250 		if (errno == EINTR)
251 			goto again;
252 	       return (FALSE);
253 	}
254 
255 #ifdef IP_OPTIONS
256 	{
257 		struct ipoption opts;
258 		int optsize = sizeof(opts), i;
259 
260 		if (!getsockopt(sock, IPPROTO_IP, IP_OPTIONS, (char *)&opts,
261 		    &optsize) && optsize != 0) {
262 			for (i = 0; (char *)&opts.ipopt_list[i] - (char *)&opts <
263 			    optsize; ) {
264 				u_char c = (u_char)opts.ipopt_list[i];
265 				if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
266 					close(sock);
267 					return (FALSE);
268 				}
269 				if (c == IPOPT_EOL)
270 					break;
271 				i += (c == IPOPT_NOP) ? 1 :
272 				    (u_char)opts.ipopt_list[i+1];
273 			}
274 		}
275 	}
276 #endif
277 
278 	/*
279 	 * XXX careful for ftp bounce attacks. If discovered, close the
280 	 * socket and look for another connection.
281 	 */
282 	if (addr.sin_port == htons(20)) {
283 		close(sock);
284 		return (FALSE);
285 	}
286 
287 	/*
288 	 * make a new transporter (re-uses xprt)
289 	 */
290 	xprt = makefd_xprt(sock, r->sendsize, r->recvsize);
291 	xprt->xp_raddr = addr;
292 	xprt->xp_addrlen = len;
293 	return (FALSE); /* there is never an rpc msg to be processed */
294 }
295 
296 static enum xprt_stat
297 rendezvous_stat()
298 {
299 
300 	return (XPRT_IDLE);
301 }
302 
303 static void
304 svctcp_destroy(xprt)
305 	register SVCXPRT *xprt;
306 {
307 	register struct tcp_conn *cd = (struct tcp_conn *)xprt->xp_p1;
308 
309 	xprt_unregister(xprt);
310 	if (xprt->xp_sock != -1)
311 		(void)close(xprt->xp_sock);
312 	xprt->xp_sock = -1;
313 	if (xprt->xp_port != 0) {
314 		/* a rendezvouser socket */
315 		xprt->xp_port = 0;
316 	} else {
317 		/* an actual connection socket */
318 		XDR_DESTROY(&(cd->xdrs));
319 	}
320 	mem_free((caddr_t)cd, sizeof(struct tcp_conn));
321 	mem_free((caddr_t)xprt, sizeof(SVCXPRT));
322 }
323 
324 /*
325  * All read operations timeout after 35 seconds.
326  * A timeout is fatal for the connection.
327  */
328 static struct timeval wait_per_try = { 35, 0 };
329 
330 /*
331  * reads data from the tcp conection.
332  * any error is fatal and the connection is closed.
333  * (And a read of zero bytes is a half closed stream => error.)
334  */
335 static int
336 readtcp(xprt, buf, len)
337 	register SVCXPRT *xprt;
338 	caddr_t buf;
339 	register int len;
340 {
341 	register int sock = xprt->xp_sock;
342 	struct timeval start, delta;
343 	struct timeval tmp1, tmp2;
344 	fd_set *fds = NULL;
345 	int prevbytes = 0, bytes;
346 	extern int __svc_fdsetsize;
347 	extern fd_set *__svc_fdset;
348 
349 	delta = wait_per_try;
350 	gettimeofday(&start, NULL);
351 	do {
352 		bytes = howmany(__svc_fdsetsize, NFDBITS) * sizeof(fd_mask);
353 		if (bytes != prevbytes) {
354 			if (fds)
355 				free(fds);
356 			fds = (fd_set *)malloc(bytes);
357 			prevbytes = bytes;
358 		}
359 		if (fds == NULL)
360 			goto fatal_err;
361 		memcpy(fds, __svc_fdset, bytes);
362 
363 		FD_SET(sock, fds);
364 		switch (select(svc_maxfd+1, fds, NULL, NULL, &delta)) {
365 		case -1:
366 			if (errno != EINTR)
367 				goto fatal_err;
368 			gettimeofday(&tmp1, NULL);
369 			timersub(&tmp1, &start, &tmp2);
370 			timersub(&wait_per_try, &tmp2, &tmp1);
371 			if (tmp1.tv_sec < 0 || !timerisset(&tmp1))
372 				goto fatal_err;
373 			delta = tmp1;
374 			continue;
375 		case 0:
376 			goto fatal_err;
377 		default:
378 			if (!FD_ISSET(sock, fds)) {
379 				svc_getreqset2(fds, svc_maxfd+1);
380 				gettimeofday(&tmp1, NULL);
381 				timersub(&tmp1, &start, &tmp2);
382 				timersub(&wait_per_try, &tmp2, &tmp1);
383 				if (tmp1.tv_sec < 0 || !timerisset(&tmp1))
384 					goto fatal_err;
385 				delta = tmp1;
386 				continue;
387 			}
388 		}
389 	} while (!FD_ISSET(sock, fds));
390 	if ((len = read(sock, buf, len)) > 0) {
391 		if (fds)
392 			free(fds);
393 		return (len);
394 	}
395 fatal_err:
396 	((struct tcp_conn *)(xprt->xp_p1))->strm_stat = XPRT_DIED;
397 	if (fds)
398 		free(fds);
399 	return (-1);
400 }
401 
402 /*
403  * writes data to the tcp connection.
404  * Any error is fatal and the connection is closed.
405  */
406 static int
407 writetcp(xprt, buf, len)
408 	register SVCXPRT *xprt;
409 	caddr_t buf;
410 	int len;
411 {
412 	register int i, cnt;
413 
414 	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
415 		if ((i = write(xprt->xp_sock, buf, cnt)) < 0) {
416 			((struct tcp_conn *)(xprt->xp_p1))->strm_stat =
417 			    XPRT_DIED;
418 			return (-1);
419 		}
420 	}
421 	return (len);
422 }
423 
424 static enum xprt_stat
425 svctcp_stat(xprt)
426 	SVCXPRT *xprt;
427 {
428 	register struct tcp_conn *cd =
429 	    (struct tcp_conn *)(xprt->xp_p1);
430 
431 	if (cd->strm_stat == XPRT_DIED)
432 		return (XPRT_DIED);
433 	if (! xdrrec_eof(&(cd->xdrs)))
434 		return (XPRT_MOREREQS);
435 	return (XPRT_IDLE);
436 }
437 
438 static bool_t
439 svctcp_recv(xprt, msg)
440 	SVCXPRT *xprt;
441 	register struct rpc_msg *msg;
442 {
443 	register struct tcp_conn *cd =
444 	    (struct tcp_conn *)(xprt->xp_p1);
445 	register XDR *xdrs = &(cd->xdrs);
446 
447 	xdrs->x_op = XDR_DECODE;
448 	(void)xdrrec_skiprecord(xdrs);
449 	if (xdr_callmsg(xdrs, msg)) {
450 		cd->x_id = msg->rm_xid;
451 		return (TRUE);
452 	}
453 	cd->strm_stat = XPRT_DIED;	/* XXX */
454 	return (FALSE);
455 }
456 
457 static bool_t
458 svctcp_getargs(xprt, xdr_args, args_ptr)
459 	SVCXPRT *xprt;
460 	xdrproc_t xdr_args;
461 	caddr_t args_ptr;
462 {
463 
464 	return ((*xdr_args)(&(((struct tcp_conn *)(xprt->xp_p1))->xdrs), args_ptr));
465 }
466 
467 static bool_t
468 svctcp_freeargs(xprt, xdr_args, args_ptr)
469 	SVCXPRT *xprt;
470 	xdrproc_t xdr_args;
471 	caddr_t args_ptr;
472 {
473 	register XDR *xdrs =
474 	    &(((struct tcp_conn *)(xprt->xp_p1))->xdrs);
475 
476 	xdrs->x_op = XDR_FREE;
477 	return ((*xdr_args)(xdrs, args_ptr));
478 }
479 
480 static bool_t
481 svctcp_reply(xprt, msg)
482 	SVCXPRT *xprt;
483 	register struct rpc_msg *msg;
484 {
485 	register struct tcp_conn *cd =
486 	    (struct tcp_conn *)(xprt->xp_p1);
487 	register XDR *xdrs = &(cd->xdrs);
488 	register bool_t stat;
489 
490 	xdrs->x_op = XDR_ENCODE;
491 	msg->rm_xid = cd->x_id;
492 	stat = xdr_replymsg(xdrs, msg);
493 	(void)xdrrec_endofrecord(xdrs, TRUE);
494 	return (stat);
495 }
496