1 /* $NetBSD: svc_vc.c,v 1.22 2009/02/12 04:38:52 lukem Exp $ */
4 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
5 * unrestricted use provided that this legend is included on all tape
6 * media and as a part of the software program in whole or part. Users
7 * may copy or modify Sun RPC without charge, but are not authorized
8 * to license or distribute it to anyone else except as part of a product or
9 * program developed by the user.
11 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
12 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
13 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
15 * Sun RPC is provided with no support and without any obligation on the
16 * part of Sun Microsystems, Inc. to assist in its use, correction,
17 * modification or enhancement.
19 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
20 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
21 * OR ANY PART THEREOF.
23 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
24 * or profits or other special, indirect and consequential damages, even if
25 * Sun has been advised of the possibility of such damages.
27 * Sun Microsystems, Inc.
29 * Mountain View, California 94043
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
35 static char *sccsid
= "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";
36 static char *sccsid
= "@(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC";
38 __RCSID("$NetBSD: svc_vc.c,v 1.22 2009/02/12 04:38:52 lukem Exp $");
43 * svc_vc.c, Server side for Connection Oriented based RPC.
45 * Actually implements two flavors of transporter -
46 * a tcp rendezvouser (a listner and connection establisher)
47 * and a record/tcp stream.
50 #include "namespace.h"
51 #include "reentrant.h"
52 #include <sys/types.h>
53 #include <sys/param.h>
55 #include <sys/socket.h>
58 #include <netinet/in.h>
71 #include "rpc_internal.h"
74 __weak_alias(svc_fd_create
,_svc_fd_create
)
75 __weak_alias(svc_vc_create
,_svc_vc_create
)
79 extern rwlock_t svc_fd_lock
;
82 static SVCXPRT
*makefd_xprt
__P((int, u_int
, u_int
));
83 static bool_t rendezvous_request
__P((SVCXPRT
*, struct rpc_msg
*));
84 static enum xprt_stat rendezvous_stat
__P((SVCXPRT
*));
85 static void svc_vc_destroy
__P((SVCXPRT
*));
86 static void __svc_vc_dodestroy
__P((SVCXPRT
*));
87 static int read_vc
__P((caddr_t
, caddr_t
, int));
88 static int write_vc
__P((caddr_t
, caddr_t
, int));
89 static enum xprt_stat svc_vc_stat
__P((SVCXPRT
*));
90 static bool_t svc_vc_recv
__P((SVCXPRT
*, struct rpc_msg
*));
91 static bool_t svc_vc_getargs
__P((SVCXPRT
*, xdrproc_t
, caddr_t
));
92 static bool_t svc_vc_freeargs
__P((SVCXPRT
*, xdrproc_t
, caddr_t
));
93 static bool_t svc_vc_reply
__P((SVCXPRT
*, struct rpc_msg
*));
94 static void svc_vc_rendezvous_ops
__P((SVCXPRT
*));
95 static void svc_vc_ops
__P((SVCXPRT
*));
96 static bool_t svc_vc_control
__P((SVCXPRT
*, const u_int
, void *));
97 static bool_t svc_vc_rendezvous_control
__P((SVCXPRT
*, const u_int
,
100 struct cf_rendezvous
{ /* kept in xprt->xp_p1 for rendezvouser */
106 struct cf_conn
{ /* kept in xprt->xp_p1 for actual connection */
107 enum xprt_stat strm_stat
;
110 char verf_body
[MAX_AUTH_BYTES
];
115 struct timeval last_recv_time
;
120 * xprt = svc_vc_create(sock, send_buf_size, recv_buf_size);
122 * Creates, registers, and returns a (rpc) tcp based transporter.
123 * Once *xprt is initialized, it is registered as a transporter
124 * see (svc.h, xprt_register). This routine returns
125 * a NULL if a problem occurred.
127 * The filedescriptor passed in is expected to refer to a bound, but
128 * not yet connected socket.
130 * Since streams do buffered io similar to stdio, the caller can specify
131 * how big the send and receive buffers are via the second and third parms;
132 * 0 => use the system default.
135 svc_vc_create(fd
, sendsize
, recvsize
)
141 struct cf_rendezvous
*r
= NULL
;
142 struct __rpc_sockinfo si
;
143 struct sockaddr_storage sslocal
;
147 if (!__rpc_fd2sockinfo(fd
, &si
))
150 r
= mem_alloc(sizeof(*r
));
152 warnx("svc_vc_create: out of memory");
155 r
->sendsize
= __rpc_get_t_size(si
.si_af
, si
.si_proto
, (int)sendsize
);
156 r
->recvsize
= __rpc_get_t_size(si
.si_af
, si
.si_proto
, (int)recvsize
);
157 r
->maxrec
= __svc_maxrec
;
158 xprt
= mem_alloc(sizeof(SVCXPRT
));
160 warnx("svc_vc_create: out of memory");
161 goto cleanup_svc_vc_create
;
164 xprt
->xp_p1
= (caddr_t
)(void *)r
;
167 xprt
->xp_verf
= _null_auth
;
168 svc_vc_rendezvous_ops(xprt
);
169 xprt
->xp_port
= (u_short
)-1; /* It is the rendezvouser */
172 slen
= sizeof (struct sockaddr_storage
);
173 if (getsockname(fd
, (struct sockaddr
*)(void *)&sslocal
, &slen
) < 0) {
174 warnx("svc_vc_create: could not retrieve local addr");
175 goto cleanup_svc_vc_create
;
179 * We want to be able to check credentials on local sockets.
181 if (sslocal
.ss_family
== AF_LOCAL
)
182 if (setsockopt(fd
, 0, LOCAL_CREDS
, &one
, sizeof one
) < 0)
183 goto cleanup_svc_vc_create
;
185 xprt
->xp_ltaddr
.maxlen
= xprt
->xp_ltaddr
.len
= sslocal
.ss_len
;
186 xprt
->xp_ltaddr
.buf
= mem_alloc((size_t)sslocal
.ss_len
);
187 if (xprt
->xp_ltaddr
.buf
== NULL
) {
188 warnx("svc_vc_create: no mem for local addr");
189 goto cleanup_svc_vc_create
;
191 memcpy(xprt
->xp_ltaddr
.buf
, &sslocal
, (size_t)sslocal
.ss_len
);
193 xprt
->xp_rtaddr
.maxlen
= sizeof (struct sockaddr_storage
);
196 cleanup_svc_vc_create
:
198 mem_free(xprt
, sizeof(*xprt
));
200 mem_free(r
, sizeof(*r
));
205 * Like svtcp_create(), except the routine takes any *open* UNIX file
206 * descriptor as its first input.
209 svc_fd_create(fd
, sendsize
, recvsize
)
214 struct sockaddr_storage ss
;
218 _DIAGASSERT(fd
!= -1);
220 ret
= makefd_xprt(fd
, sendsize
, recvsize
);
224 slen
= sizeof (struct sockaddr_storage
);
225 if (getsockname(fd
, (struct sockaddr
*)(void *)&ss
, &slen
) < 0) {
226 warnx("svc_fd_create: could not retrieve local addr");
229 ret
->xp_ltaddr
.maxlen
= ret
->xp_ltaddr
.len
= ss
.ss_len
;
230 ret
->xp_ltaddr
.buf
= mem_alloc((size_t)ss
.ss_len
);
231 if (ret
->xp_ltaddr
.buf
== NULL
) {
232 warnx("svc_fd_create: no mem for local addr");
235 memcpy(ret
->xp_ltaddr
.buf
, &ss
, (size_t)ss
.ss_len
);
237 slen
= sizeof (struct sockaddr_storage
);
238 if (getpeername(fd
, (struct sockaddr
*)(void *)&ss
, &slen
) < 0) {
239 warnx("svc_fd_create: could not retrieve remote addr");
242 ret
->xp_rtaddr
.maxlen
= ret
->xp_rtaddr
.len
= ss
.ss_len
;
243 ret
->xp_rtaddr
.buf
= mem_alloc((size_t)ss
.ss_len
);
244 if (ret
->xp_rtaddr
.buf
== NULL
) {
245 warnx("svc_fd_create: no mem for local addr");
248 memcpy(ret
->xp_rtaddr
.buf
, &ss
, (size_t)ss
.ss_len
);
250 if (ss
.ss_family
== AF_INET
) {
251 ret
->xp_raddr
= *(struct sockaddr_in
*)ret
->xp_rtaddr
.buf
;
252 ret
->xp_addrlen
= sizeof (struct sockaddr_in
);
259 if (ret
->xp_ltaddr
.buf
!= NULL
)
260 mem_free(ret
->xp_ltaddr
.buf
, rep
->xp_ltaddr
.maxlen
);
266 makefd_xprt(fd
, sendsize
, recvsize
)
274 struct __rpc_sockinfo si
;
276 _DIAGASSERT(fd
!= -1);
278 xprt
= mem_alloc(sizeof(SVCXPRT
));
281 memset(xprt
, 0, sizeof *xprt
);
282 cd
= mem_alloc(sizeof(struct cf_conn
));
285 cd
->strm_stat
= XPRT_IDLE
;
286 xdrrec_create(&(cd
->xdrs
), sendsize
, recvsize
,
287 (caddr_t
)(void *)xprt
, read_vc
, write_vc
);
288 xprt
->xp_p1
= (caddr_t
)(void *)cd
;
289 xprt
->xp_verf
.oa_base
= cd
->verf_body
;
290 svc_vc_ops(xprt
); /* truely deals with calls */
291 xprt
->xp_port
= 0; /* this is a connection, not a rendezvouser */
293 if (__rpc_fd2sockinfo(fd
, &si
) && __rpc_sockinfo2netid(&si
, &netid
))
294 if ((xprt
->xp_netid
= strdup(netid
)) == NULL
)
300 warn("svc_tcp: makefd_xprt");
302 mem_free(xprt
, sizeof(SVCXPRT
));
308 rendezvous_request(xprt
, msg
)
313 struct cf_rendezvous
*r
;
315 struct sockaddr_storage addr
;
317 struct __rpc_sockinfo si
;
321 _DIAGASSERT(xprt
!= NULL
);
322 _DIAGASSERT(msg
!= NULL
);
324 r
= (struct cf_rendezvous
*)xprt
->xp_p1
;
327 if ((sock
= accept(xprt
->xp_fd
, (struct sockaddr
*)(void *)&addr
,
332 * Clean out the most idle file descriptor when we're
335 if (errno
== EMFILE
|| errno
== ENFILE
) {
336 cleanfds
= svc_fdset
;
337 if (__svc_clean_idle(&cleanfds
, 0, FALSE
))
343 * make a new transporter (re-uses xprt)
345 newxprt
= makefd_xprt(sock
, r
->sendsize
, r
->recvsize
);
348 newxprt
->xp_rtaddr
.buf
= mem_alloc(len
);
349 if (newxprt
->xp_rtaddr
.buf
== NULL
)
351 memcpy(newxprt
->xp_rtaddr
.buf
, &addr
, len
);
352 newxprt
->xp_rtaddr
.len
= len
;
354 if (addr
.ss_family
== AF_INET
) {
355 newxprt
->xp_raddr
= *(struct sockaddr_in
*)newxprt
->xp_rtaddr
.buf
;
356 newxprt
->xp_addrlen
= sizeof (struct sockaddr_in
);
359 if (__rpc_fd2sockinfo(sock
, &si
))
360 __rpc_setnodelay(sock
, &si
);
362 cd
= (struct cf_conn
*)newxprt
->xp_p1
;
364 cd
->recvsize
= r
->recvsize
;
365 cd
->sendsize
= r
->sendsize
;
366 cd
->maxrec
= r
->maxrec
;
368 if (cd
->maxrec
!= 0) {
369 flags
= fcntl(sock
, F_GETFL
, 0);
372 if (fcntl(sock
, F_SETFL
, flags
| O_NONBLOCK
) == -1)
374 if (cd
->recvsize
> (u_int
)cd
->maxrec
)
375 cd
->recvsize
= cd
->maxrec
;
377 __xdrrec_setnonblock(&cd
->xdrs
, cd
->maxrec
);
379 cd
->nonblock
= FALSE
;
381 (void)gettimeofday(&cd
->last_recv_time
, NULL
);
383 return (FALSE
); /* there is never an rpc msg to be processed */
386 return (FALSE
); /* there was an error */
390 static enum xprt_stat
391 rendezvous_stat(xprt
)
402 _DIAGASSERT(xprt
!= NULL
);
404 xprt_unregister(xprt
);
405 __svc_vc_dodestroy(xprt
);
409 __svc_vc_dodestroy(xprt
)
413 struct cf_rendezvous
*r
;
415 cd
= (struct cf_conn
*)xprt
->xp_p1
;
417 if (xprt
->xp_fd
!= RPC_ANYFD
)
418 (void)close(xprt
->xp_fd
);
419 if (xprt
->xp_port
!= 0) {
420 /* a rendezvouser socket */
421 r
= (struct cf_rendezvous
*)xprt
->xp_p1
;
422 mem_free(r
, sizeof (struct cf_rendezvous
));
425 /* an actual connection socket */
426 XDR_DESTROY(&(cd
->xdrs
));
427 mem_free(cd
, sizeof(struct cf_conn
));
429 if (xprt
->xp_rtaddr
.buf
)
430 mem_free(xprt
->xp_rtaddr
.buf
, xprt
->xp_rtaddr
.maxlen
);
431 if (xprt
->xp_ltaddr
.buf
)
432 mem_free(xprt
->xp_ltaddr
.buf
, xprt
->xp_ltaddr
.maxlen
);
436 free(xprt
->xp_netid
);
437 mem_free(xprt
, sizeof(SVCXPRT
));
442 svc_vc_control(xprt
, rq
, in
)
452 svc_vc_rendezvous_control(xprt
, rq
, in
)
457 struct cf_rendezvous
*cfp
;
459 cfp
= (struct cf_rendezvous
*)xprt
->xp_p1
;
463 case SVCGET_CONNMAXREC
:
464 *(int *)in
= cfp
->maxrec
;
466 case SVCSET_CONNMAXREC
:
467 cfp
->maxrec
= *(int *)in
;
476 * reads data from the tcp connection.
477 * any error is fatal and the connection is closed.
478 * (And a read of zero bytes is a half closed stream => error.)
479 * All read operations timeout after 35 seconds. A timeout is
480 * fatal for the connection.
483 read_vc(xprtp
, buf
, len
)
490 struct pollfd pollfd
;
498 static const struct timespec ts
= { 35, 0 };
500 xprt
= (SVCXPRT
*)(void *)xprtp
;
501 _DIAGASSERT(xprt
!= NULL
);
505 sa
= (struct sockaddr
*)xprt
->xp_rtaddr
.buf
;
506 if (sa
->sa_family
== AF_LOCAL
&& xprt
->xp_p2
== NULL
) {
507 memset(&msg
, 0, sizeof msg
);
508 crmsgsize
= CMSG_SPACE(SOCKCREDSIZE(NGROUPS
));
509 crmsg
= malloc(crmsgsize
);
512 memset(crmsg
, 0, crmsgsize
);
514 msg
.msg_control
= crmsg
;
515 msg
.msg_controllen
= crmsgsize
;
517 if (recvmsg(sock
, &msg
, 0) < 0)
520 if (msg
.msg_controllen
== 0 ||
521 (msg
.msg_flags
& MSG_CTRUNC
) != 0)
524 cmp
= CMSG_FIRSTHDR(&msg
);
525 if (cmp
->cmsg_level
!= SOL_SOCKET
||
526 cmp
->cmsg_type
!= SCM_CREDS
)
529 sc
= (struct sockcred
*)(void *)CMSG_DATA(cmp
);
531 xprt
->xp_p2
= mem_alloc(SOCKCREDSIZE(sc
->sc_ngroups
));
532 if (xprt
->xp_p2
== NULL
)
535 memcpy(xprt
->xp_p2
, sc
, SOCKCREDSIZE(sc
->sc_ngroups
));
540 cfp
= (struct cf_conn
*)xprt
->xp_p1
;
543 len
= read(sock
, buf
, (size_t)len
);
551 gettimeofday(&cfp
->last_recv_time
, NULL
);
557 pollfd
.events
= POLLIN
;
558 switch (pollts(&pollfd
, 1, &ts
, NULL
)) {
560 if (errno
== EINTR
) {
570 } while ((pollfd
.revents
& POLLIN
) == 0);
572 if ((len
= read(sock
, buf
, (size_t)len
)) > 0) {
573 gettimeofday(&cfp
->last_recv_time
, NULL
);
580 ((struct cf_conn
*)(xprt
->xp_p1
))->strm_stat
= XPRT_DIED
;
585 * writes data to the tcp connection.
586 * Any error is fatal and the connection is closed.
589 write_vc(xprtp
, buf
, len
)
597 struct timeval tv0
, tv1
;
599 xprt
= (SVCXPRT
*)(void *)xprtp
;
600 _DIAGASSERT(xprt
!= NULL
);
602 cd
= (struct cf_conn
*)xprt
->xp_p1
;
605 gettimeofday(&tv0
, NULL
);
607 for (cnt
= len
; cnt
> 0; cnt
-= i
, buf
+= i
) {
608 if ((i
= write(xprt
->xp_fd
, buf
, (size_t)cnt
)) < 0) {
609 if (errno
!= EAGAIN
|| !cd
->nonblock
) {
610 cd
->strm_stat
= XPRT_DIED
;
613 if (cd
->nonblock
&& i
!= cnt
) {
615 * For non-blocking connections, do not
616 * take more than 2 seconds writing the
619 * XXX 2 is an arbitrary amount.
621 gettimeofday(&tv1
, NULL
);
622 if (tv1
.tv_sec
- tv0
.tv_sec
>= 2) {
623 cd
->strm_stat
= XPRT_DIED
;
632 static enum xprt_stat
638 _DIAGASSERT(xprt
!= NULL
);
640 cd
= (struct cf_conn
*)(xprt
->xp_p1
);
642 if (cd
->strm_stat
== XPRT_DIED
)
644 if (! xdrrec_eof(&(cd
->xdrs
)))
645 return (XPRT_MOREREQS
);
650 svc_vc_recv(xprt
, msg
)
657 _DIAGASSERT(xprt
!= NULL
);
658 _DIAGASSERT(msg
!= NULL
);
660 cd
= (struct cf_conn
*)(xprt
->xp_p1
);
664 if (!__xdrrec_getrec(xdrs
, &cd
->strm_stat
, TRUE
))
668 xdrs
->x_op
= XDR_DECODE
;
669 (void)xdrrec_skiprecord(xdrs
);
671 if (xdr_callmsg(xdrs
, msg
)) {
672 cd
->x_id
= msg
->rm_xid
;
675 cd
->strm_stat
= XPRT_DIED
;
680 svc_vc_getargs(xprt
, xdr_args
, args_ptr
)
686 _DIAGASSERT(xprt
!= NULL
);
687 /* args_ptr may be NULL */
689 return ((*xdr_args
)(&(((struct cf_conn
*)(xprt
->xp_p1
))->xdrs
),
694 svc_vc_freeargs(xprt
, xdr_args
, args_ptr
)
701 _DIAGASSERT(xprt
!= NULL
);
702 /* args_ptr may be NULL */
704 xdrs
= &(((struct cf_conn
*)(xprt
->xp_p1
))->xdrs
);
706 xdrs
->x_op
= XDR_FREE
;
707 return ((*xdr_args
)(xdrs
, args_ptr
));
711 svc_vc_reply(xprt
, msg
)
719 _DIAGASSERT(xprt
!= NULL
);
720 _DIAGASSERT(msg
!= NULL
);
722 cd
= (struct cf_conn
*)(xprt
->xp_p1
);
725 xdrs
->x_op
= XDR_ENCODE
;
726 msg
->rm_xid
= cd
->x_id
;
727 rstat
= xdr_replymsg(xdrs
, msg
);
728 (void)xdrrec_endofrecord(xdrs
, TRUE
);
736 static struct xp_ops ops
;
737 static struct xp_ops2 ops2
;
739 extern mutex_t ops_lock
;
742 /* VARIABLES PROTECTED BY ops_lock: ops, ops2 */
744 mutex_lock(&ops_lock
);
745 if (ops
.xp_recv
== NULL
) {
746 ops
.xp_recv
= svc_vc_recv
;
747 ops
.xp_stat
= svc_vc_stat
;
748 ops
.xp_getargs
= svc_vc_getargs
;
749 ops
.xp_reply
= svc_vc_reply
;
750 ops
.xp_freeargs
= svc_vc_freeargs
;
751 ops
.xp_destroy
= svc_vc_destroy
;
752 ops2
.xp_control
= svc_vc_control
;
755 xprt
->xp_ops2
= &ops2
;
756 mutex_unlock(&ops_lock
);
760 svc_vc_rendezvous_ops(xprt
)
763 static struct xp_ops ops
;
764 static struct xp_ops2 ops2
;
766 extern mutex_t ops_lock
;
768 /* XXXGCC vax compiler unhappy otherwise */
770 extern void abort(void);
773 mutex_lock(&ops_lock
);
774 if (ops
.xp_recv
== NULL
) {
775 ops
.xp_recv
= rendezvous_request
;
776 ops
.xp_stat
= rendezvous_stat
;
778 (bool_t (*) __P((SVCXPRT
*, xdrproc_t
, caddr_t
)))abort
;
780 (bool_t (*) __P((SVCXPRT
*, struct rpc_msg
*)))abort
;
782 (bool_t (*) __P((SVCXPRT
*, xdrproc_t
, caddr_t
)))abort
;
783 ops
.xp_destroy
= svc_vc_destroy
;
784 ops2
.xp_control
= svc_vc_rendezvous_control
;
787 xprt
->xp_ops2
= &ops2
;
788 mutex_unlock(&ops_lock
);
792 * Destroy xprts that have not have had any activity in 'timeout' seconds.
793 * If 'cleanblock' is true, blocking connections (the default) are also
794 * cleaned. If timeout is 0, the least active connection is picked.
797 __svc_clean_idle(fd_set
*fds
, int timeout
, bool_t cleanblock
)
800 SVCXPRT
*xprt
, *least_active
;
801 struct timeval tv
, tdiff
, tmax
;
804 gettimeofday(&tv
, NULL
);
805 tmax
.tv_sec
= tmax
.tv_usec
= 0;
807 rwlock_wrlock(&svc_fd_lock
);
808 for (i
= ncleaned
= 0; i
<= svc_maxfd
; i
++) {
809 if (FD_ISSET(i
, fds
)) {
810 xprt
= __svc_xports
[i
];
811 if (xprt
== NULL
|| xprt
->xp_ops
== NULL
||
812 xprt
->xp_ops
->xp_recv
!= svc_vc_recv
)
814 cd
= (struct cf_conn
*)xprt
->xp_p1
;
815 if (!cleanblock
&& !cd
->nonblock
)
818 timersub(&tv
, &cd
->last_recv_time
, &tdiff
);
819 if (timercmp(&tdiff
, &tmax
, >)) {
825 if (tv
.tv_sec
- cd
->last_recv_time
.tv_sec
> timeout
) {
826 __xprt_unregister_unlocked(xprt
);
827 __svc_vc_dodestroy(xprt
);
832 if (timeout
== 0 && least_active
!= NULL
) {
833 __xprt_unregister_unlocked(least_active
);
834 __svc_vc_dodestroy(least_active
);
837 rwlock_unlock(&svc_fd_lock
);
838 return ncleaned
> 0 ? TRUE
: FALSE
;