xref: /illumos-gate/usr/src/ucblib/librpcsoc/svc_tcp.c (revision dd72704bd9e794056c558153663c739e2012d721)
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 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 /*
41  * svc_tcp.c, Server side for TCP/IP based RPC.
42  *
43  * Actually implements two flavors of transporter -
44  * a tcp rendezvouser (a listner and connection establisher)
45  * and a record/tcp stream.
46  */
47 
48 #include <rpc/rpc.h>
49 #include <sys/socket.h>
50 #include <sys/time.h>
51 #include <errno.h>
52 #include <syslog.h>
53 #include <malloc.h>
54 #include <stdio.h>
55 
56 extern bool_t abort();
57 extern int errno;
58 extern SVCXPRT *svc_xprt_alloc();
59 extern void svc_xprt_free();
60 extern int _socket(int, int, int);
61 extern int _bind(int, const struct sockaddr *, int);
62 extern int _getsockname(int, struct sockaddr *, int *);
63 extern int _listen(int, int);
64 extern int _accept(int, struct sockaddr *, int *);
65 extern int bindresvport(int, struct sockaddr_in *);
66 
67 static struct xp_ops *svctcp_ops();
68 static struct xp_ops *svctcp_rendezvous_ops();
69 
70 static int readtcp(), writetcp();
71 static SVCXPRT *makefd_xprt();
72 
73 struct tcp_rendezvous { /* kept in xprt->xp_p1 */
74 	u_int sendsize;
75 	u_int recvsize;
76 };
77 
78 struct tcp_conn {  /* kept in xprt->xp_p1 */
79 	enum xprt_stat strm_stat;
80 	uint32_t x_id;
81 	XDR xdrs;
82 	char verf_body[MAX_AUTH_BYTES];
83 };
84 
85 /*
86  * Usage:
87  *	xprt = svctcp_create(sock, send_buf_size, recv_buf_size);
88  *
89  * Creates, registers, and returns a (rpc) tcp based transporter.
90  * Once *xprt is initialized, it is registered as a transporter
91  * see (svc.h, xprt_register).  This routine returns
92  * a NULL if a problem occurred.
93  *
94  * If sock<0 then a socket is created, else sock is used.
95  * If the socket, sock is not bound to a port then svctcp_create
96  * binds it to an arbitrary port.  The routine then starts a tcp
97  * listener on the socket's associated port.  In any (successful) case,
98  * xprt->xp_sock is the registered socket number and xprt->xp_port is the
99  * associated port number.
100  *
101  * Since tcp streams do buffered io similar to stdio, the caller can specify
102  * how big the send and receive buffers are via the second and third parms;
103  * 0 => use the system default.
104  */
105 SVCXPRT *
106 svctcp_create(sock, sendsize, recvsize)
107 	register int sock;
108 	u_int sendsize;
109 	u_int recvsize;
110 {
111 	bool_t madesock = FALSE;
112 	register SVCXPRT *xprt;
113 	register struct tcp_rendezvous *r;
114 	struct sockaddr_in addr;
115 	int len = sizeof (struct sockaddr_in);
116 
117 	if (sock == RPC_ANYSOCK) {
118 		if ((sock = _socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
119 			(void) syslog(LOG_ERR, "svctcp_create - tcp",
120 				" socket creation problem: %m");
121 			return ((SVCXPRT *)NULL);
122 		}
123 		madesock = TRUE;
124 	}
125 	memset((char *)&addr, 0, sizeof (addr));
126 	addr.sin_family = AF_INET;
127 	if (bindresvport(sock, &addr)) {
128 		addr.sin_port = 0;
129 		(void) _bind(sock, (struct sockaddr *)&addr, len);
130 	}
131 	if ((_getsockname(sock, (struct sockaddr *)&addr, &len) != 0) ||
132 	    (_listen(sock, 2) != 0)) {
133 		(void) syslog(LOG_ERR, "svctcp_create - cannot",
134 			" getsockname or listen: %m");
135 		if (madesock)
136 			(void) close(sock);
137 		return ((SVCXPRT *)NULL);
138 	}
139 	r = (struct tcp_rendezvous *)mem_alloc(sizeof (*r));
140 	if (r == NULL) {
141 		(void) syslog(LOG_ERR, "svctcp_create: out of memory");
142 		if (madesock)
143 			(void) close(sock);
144 		return (NULL);
145 	}
146 	r->sendsize = sendsize;
147 	r->recvsize = recvsize;
148 	xprt = svc_xprt_alloc();
149 	if (xprt == NULL) {
150 		(void) syslog(LOG_ERR, "svctcp_create: out of memory");
151 		mem_free((char *) r, sizeof (*r));
152 		if (madesock)
153 			(void) close(sock);
154 		return (NULL);
155 	}
156 	xprt->xp_p2 = NULL;
157 	xprt->xp_netid = NULL;
158 	xprt->xp_p1 = (caddr_t)r;
159 	xprt->xp_verf = _null_auth;
160 	xprt->xp_ops = svctcp_rendezvous_ops();
161 	xprt->xp_port = ntohs(addr.sin_port);
162 	xprt->xp_sock = sock;
163 	xprt->xp_rtaddr.buf = xprt->xp_raddr;
164 	xprt_register(xprt);
165 	return (xprt);
166 }
167 
168 /*
169  * Like svtcp_create(), except the routine takes any *open* UNIX file
170  * descriptor as its first input.
171  */
172 SVCXPRT *
173 svcfd_create(fd, sendsize, recvsize)
174 	int fd;
175 	u_int sendsize;
176 	u_int recvsize;
177 {
178 
179 	return (makefd_xprt(fd, sendsize, recvsize));
180 }
181 
182 static SVCXPRT *
183 makefd_xprt(fd, sendsize, recvsize)
184 	int fd;
185 	u_int sendsize;
186 	u_int recvsize;
187 {
188 	register SVCXPRT *xprt;
189 	register struct tcp_conn *cd;
190 
191 	xprt = svc_xprt_alloc();
192 	if (xprt == (SVCXPRT *)NULL) {
193 		(void) syslog(LOG_ERR, "svc_tcp: makefd_xprt: out of memory");
194 		goto done;
195 	}
196 	cd = (struct tcp_conn *)mem_alloc(sizeof (struct tcp_conn));
197 	if (cd == (struct tcp_conn *)NULL) {
198 		(void) syslog(LOG_ERR, "svc_tcp: makefd_xprt: out of memory");
199 		svc_xprt_free(xprt);
200 		xprt = (SVCXPRT *)NULL;
201 		goto done;
202 	}
203 	cd->strm_stat = XPRT_IDLE;
204 	xdrrec_create(&(cd->xdrs), sendsize, recvsize,
205 	    (caddr_t)xprt, readtcp, writetcp);
206 	xprt->xp_p2 = NULL;
207 	xprt->xp_netid = NULL;
208 	xprt->xp_p1 = (caddr_t)cd;
209 	xprt->xp_verf.oa_base = cd->verf_body;
210 	xprt->xp_addrlen = 0;
211 	xprt->xp_ops = svctcp_ops();  /* truely deals with calls */
212 	xprt->xp_port = 0;  /* this is a connection, not a rendezvouser */
213 	xprt->xp_sock = fd;
214 	/* to handle svc_getcaller() properly */
215 	xprt->xp_rtaddr.buf = xprt->xp_raddr;
216 	xprt_register(xprt);
217 	done:
218 	return (xprt);
219 }
220 
221 static bool_t
222 rendezvous_request(xprt, rpc_msg)
223 	register SVCXPRT *xprt;
224 	struct rpc_msg	*rpc_msg;
225 {
226 	int sock;
227 	struct tcp_rendezvous *r;
228 	struct sockaddr_in addr;
229 	int len;
230 
231 	r = (struct tcp_rendezvous *)xprt->xp_p1;
232 	again:
233 	len = sizeof (struct sockaddr_in);
234 	if ((sock = _accept(xprt->xp_sock, (struct sockaddr *)&addr,
235 	    &len)) < 0) {
236 		if (errno == EINTR)
237 			goto again;
238 		return (FALSE);
239 	}
240 	/*
241 	 * make a new transporter (re-uses xprt)
242 	 */
243 	xprt = makefd_xprt(sock, r->sendsize, r->recvsize);
244 
245 	memcpy((char *)&xprt->xp_raddr, (char *)&addr, len);
246 	xprt->xp_addrlen = len;
247 	return (FALSE); /* there is never an rpc msg to be processed */
248 }
249 
250 static enum xprt_stat
251 rendezvous_stat(xprt)
252 	SVCXPRT *xprt;
253 {
254 
255 	return (XPRT_IDLE);
256 }
257 
258 static void
259 svctcp_destroy(xprt)
260 	register SVCXPRT *xprt;
261 {
262 	register struct tcp_conn *cd = (struct tcp_conn *)xprt->xp_p1;
263 
264 	xprt_unregister(xprt);
265 	(void) close(xprt->xp_sock);
266 	if (xprt->xp_port != 0) {
267 		/* a rendezvouser socket */
268 		xprt->xp_port = 0;
269 	} else {
270 		/* an actual connection socket */
271 		XDR_DESTROY(&(cd->xdrs));
272 	}
273 	mem_free((caddr_t)cd, sizeof (struct tcp_conn));
274 	svc_xprt_free(xprt);
275 }
276 
277 /*
278  * All read operations timeout after 35 seconds.
279  * A timeout is fatal for the connection.
280  */
281 static struct timeval wait_per_try = { 35, 0 };
282 
283 /*
284  * reads data from the tcp conection.
285  * any error is fatal and the connection is closed.
286  * (And a read of zero bytes is a half closed stream => error.)
287  */
288 static int
289 readtcp(xprt, buf, len)
290 	register SVCXPRT *xprt;
291 	caddr_t buf;
292 	register int len;
293 {
294 	register int sock = xprt->xp_sock;
295 	fd_set mask;
296 	fd_set readfds;
297 
298 	FD_ZERO(&mask);
299 	FD_SET(sock, &mask);
300 	do {
301 		readfds = mask;
302 		if (select(__rpc_dtbsize(), &readfds, NULL, NULL,
303 			&wait_per_try) <= 0) {
304 			if (errno == EINTR) {
305 				continue;
306 			}
307 			goto fatal_err;
308 		}
309 	} while (!FD_ISSET(sock, &readfds));
310 	if ((len = read(sock, buf, len)) > 0) {
311 		return (len);
312 	}
313 fatal_err:
314 	((struct tcp_conn *)(xprt->xp_p1))->strm_stat = XPRT_DIED;
315 	return (-1);
316 }
317 
318 /*
319  * writes data to the tcp connection.
320  * Any error is fatal and the connection is closed.
321  */
322 static int
323 writetcp(xprt, buf, len)
324 	register SVCXPRT *xprt;
325 	caddr_t buf;
326 	int len;
327 {
328 	register int i, cnt;
329 
330 	for (cnt = len; cnt > 0; cnt -= i, buf += i) {
331 		if ((i = write(xprt->xp_sock, buf, cnt)) < 0) {
332 			((struct tcp_conn *)(xprt->xp_p1))->strm_stat =
333 			    XPRT_DIED;
334 			return (-1);
335 		}
336 	}
337 	return (len);
338 }
339 
340 static enum xprt_stat
341 svctcp_stat(xprt)
342 	SVCXPRT *xprt;
343 {
344 	register struct tcp_conn *cd =
345 	    (struct tcp_conn *)(xprt->xp_p1);
346 
347 	if (cd->strm_stat == XPRT_DIED)
348 		return (XPRT_DIED);
349 	if (! xdrrec_eof(&(cd->xdrs)))
350 		return (XPRT_MOREREQS);
351 	return (XPRT_IDLE);
352 }
353 
354 static bool_t
355 svctcp_recv(xprt, msg)
356 	SVCXPRT *xprt;
357 	register struct rpc_msg *msg;
358 {
359 	register struct tcp_conn *cd =
360 	    (struct tcp_conn *)(xprt->xp_p1);
361 	register XDR *xdrs = &(cd->xdrs);
362 
363 	xdrs->x_op = XDR_DECODE;
364 	(void) xdrrec_skiprecord(xdrs);
365 	if (xdr_callmsg(xdrs, msg)) {
366 		cd->x_id = msg->rm_xid;
367 		return (TRUE);
368 	}
369 	return (FALSE);
370 }
371 
372 static bool_t
373 svctcp_getargs(xprt, xdr_args, args_ptr)
374 	SVCXPRT *xprt;
375 	xdrproc_t xdr_args;
376 	caddr_t args_ptr;
377 {
378 
379 	return ((*xdr_args)(&(((struct tcp_conn *)(xprt->xp_p1))->xdrs),
380 		args_ptr));
381 }
382 
383 static bool_t
384 svctcp_freeargs(xprt, xdr_args, args_ptr)
385 	SVCXPRT *xprt;
386 	xdrproc_t xdr_args;
387 	caddr_t args_ptr;
388 {
389 	register XDR *xdrs =
390 	    &(((struct tcp_conn *)(xprt->xp_p1))->xdrs);
391 
392 	xdrs->x_op = XDR_FREE;
393 	return ((*xdr_args)(xdrs, args_ptr));
394 }
395 
396 static bool_t
397 svctcp_reply(xprt, msg)
398 	SVCXPRT *xprt;
399 	register struct rpc_msg *msg;
400 {
401 	register struct tcp_conn *cd =
402 	    (struct tcp_conn *)(xprt->xp_p1);
403 	register XDR *xdrs = &(cd->xdrs);
404 	register bool_t stat;
405 
406 	xdrs->x_op = XDR_ENCODE;
407 	msg->rm_xid = cd->x_id;
408 	stat = xdr_replymsg(xdrs, msg);
409 	(void) xdrrec_endofrecord(xdrs, TRUE);
410 	return (stat);
411 }
412 
413 
414 static struct xp_ops *
415 svctcp_ops()
416 {
417 	static struct xp_ops ops;
418 
419 	if (ops.xp_recv == NULL) {
420 		ops.xp_recv = svctcp_recv;
421 		ops.xp_stat = svctcp_stat;
422 		ops.xp_getargs = svctcp_getargs;
423 		ops.xp_reply = svctcp_reply;
424 		ops.xp_freeargs = svctcp_freeargs;
425 		ops.xp_destroy = svctcp_destroy;
426 	}
427 	return (&ops);
428 }
429 
430 
431 static struct xp_ops *
432 svctcp_rendezvous_ops()
433 {
434 	static struct xp_ops ops;
435 
436 	if (ops.xp_recv == NULL) {
437 		ops.xp_recv = rendezvous_request;
438 		ops.xp_stat = rendezvous_stat;
439 		ops.xp_getargs = abort;
440 		ops.xp_reply = abort;
441 		ops.xp_freeargs = abort,
442 		ops.xp_destroy = svctcp_destroy;
443 	}
444 	return (&ops);
445 }
446