3 * Copyright (c) 2009, Sun Microsystems, Inc.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 * - Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * - Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * - Neither the name of Sun Microsystems, Inc. nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
30 //#include <sys/cdefs.h>
33 * svc_vc.c, Server side for Connection Oriented based RPC.
35 * Actually implements two flavors of transporter -
36 * a tcp rendezvouser (a listner and connection establisher)
37 * and a record/tcp stream.
40 //#include <pthread.h>
41 #include <reentrant.h>
42 //#include <sys/socket.h>
43 #include <sys/types.h>
44 //#include <sys/param.h>
45 //#include <sys/poll.h>
47 //#include <sys/time.h>
48 //#include <sys/uio.h>
49 //#include <netinet/in.h>
50 //#include <netinet/tcp.h>
65 #include <getpeereid.h>
68 extern rwlock_t svc_fd_lock
;
70 static SVCXPRT
*makefd_xprt(SOCKET
, u_int
, u_int
);
71 static bool_t
rendezvous_request(SVCXPRT
*, struct rpc_msg
*);
72 static enum xprt_stat
rendezvous_stat(SVCXPRT
*);
73 static void svc_vc_destroy(SVCXPRT
*);
74 static void __svc_vc_dodestroy (SVCXPRT
*);
75 static int read_vc(void *, void *, int);
76 static int write_vc(void *, void *, int);
77 static enum xprt_stat
svc_vc_stat(SVCXPRT
*);
78 static bool_t
svc_vc_recv(SVCXPRT
*, struct rpc_msg
*);
79 static bool_t
svc_vc_getargs(SVCXPRT
*, xdrproc_t
, void *);
80 static bool_t
svc_vc_freeargs(SVCXPRT
*, xdrproc_t
, void *);
81 static bool_t
svc_vc_reply(SVCXPRT
*, struct rpc_msg
*);
82 static void svc_vc_rendezvous_ops(SVCXPRT
*);
83 static void svc_vc_ops(SVCXPRT
*);
84 static bool_t
svc_vc_control(SVCXPRT
*xprt
, const u_int rq
, void *in
);
85 static bool_t
svc_vc_rendezvous_control (SVCXPRT
*xprt
, const u_int rq
,
88 struct cf_rendezvous
{ /* kept in xprt->xp_p1 for rendezvouser */
94 struct cf_conn
{ /* kept in xprt->xp_p1 for actual connection */
95 enum xprt_stat strm_stat
;
98 char verf_body
[MAX_AUTH_BYTES
];
103 struct timeval last_recv_time
;
107 * This is used to set xprt->xp_raddr in a way legacy
111 __xprt_set_raddr(SVCXPRT
*xprt
, const struct sockaddr_storage
*ss
)
113 switch (ss
->ss_family
) {
115 memcpy(&xprt
->xp_raddr
, ss
, sizeof(struct sockaddr_in6
));
116 xprt
->xp_addrlen
= sizeof (struct sockaddr_in6
);
119 memcpy(&xprt
->xp_raddr
, ss
, sizeof(struct sockaddr_in
));
120 xprt
->xp_addrlen
= sizeof (struct sockaddr_in
);
123 xprt
->xp_raddr
.sin6_family
= AF_UNSPEC
;
124 xprt
->xp_addrlen
= sizeof (struct sockaddr
);
131 * xprt = svc_vc_create(sock, send_buf_size, recv_buf_size);
133 * Creates, registers, and returns a (rpc) tcp based transporter.
134 * Once *xprt is initialized, it is registered as a transporter
135 * see (svc.h, xprt_register). This routine returns
136 * a NULL if a problem occurred.
138 * The filedescriptor passed in is expected to refer to a bound, but
139 * not yet connected socket.
141 * Since streams do buffered io similar to stdio, the caller can specify
142 * how big the send and receive buffers are via the second and third parms;
143 * 0 => use the system default.
146 svc_vc_create(fd
, sendsize
, recvsize
)
152 struct cf_rendezvous
*r
= NULL
;
153 struct __rpc_sockinfo si
;
154 struct sockaddr_storage sslocal
;
157 r
= mem_alloc(sizeof(*r
));
159 // XXX warnx("svc_vc_create: out of memory");
160 goto cleanup_svc_vc_create
;
162 if (!__rpc_fd2sockinfo(fd
, &si
))
164 r
->sendsize
= __rpc_get_t_size(si
.si_af
, si
.si_proto
, (int)sendsize
);
165 r
->recvsize
= __rpc_get_t_size(si
.si_af
, si
.si_proto
, (int)recvsize
);
166 r
->maxrec
= __svc_maxrec
;
167 xprt
= mem_alloc(sizeof(SVCXPRT
));
169 // XXX warnx("svc_vc_create: out of memory");
170 goto cleanup_svc_vc_create
;
176 xprt
->xp_verf
= _null_auth
;
177 svc_vc_rendezvous_ops(xprt
);
178 xprt
->xp_port
= (u_short
)-1; /* It is the rendezvouser */
181 slen
= sizeof (struct sockaddr_storage
);
182 if (getsockname(fd
, (struct sockaddr
*)(void *)&sslocal
, &slen
) == SOCKET_ERROR
) {
183 // XXX warnx("svc_vc_create: could not retrieve local addr");
184 goto cleanup_svc_vc_create
;
187 if (!__rpc_set_netbuf(&xprt
->xp_ltaddr
, &sslocal
, sizeof(sslocal
))) {
188 // XXX warnx("svc_vc_create: no mem for local addr");
189 goto cleanup_svc_vc_create
;
193 cleanup_svc_vc_create
:
195 mem_free(r
, sizeof(*r
));
200 * Like svtcp_create(), except the routine takes any *open* UNIX file
201 * descriptor as its first input.
204 svc_fd_create(fd
, sendsize
, recvsize
)
209 struct sockaddr_storage ss
;
215 ret
= makefd_xprt(fd
, sendsize
, recvsize
);
219 slen
= sizeof (struct sockaddr_storage
);
220 if (getsockname(fd
, (struct sockaddr
*)(void *)&ss
, &slen
) == SOCKET_ERROR
) {
221 // XXX warnx("svc_fd_create: could not retrieve local addr");
224 if (!__rpc_set_netbuf(&ret
->xp_ltaddr
, &ss
, sizeof(ss
))) {
225 // XXX warnx("svc_fd_create: no mem for local addr");
229 slen
= sizeof (struct sockaddr_storage
);
230 if (getpeername(fd
, (struct sockaddr
*)(void *)&ss
, &slen
) == SOCKET_ERROR
) {
231 // XXX warnx("svc_fd_create: could not retrieve remote addr");
234 if (!__rpc_set_netbuf(&ret
->xp_rtaddr
, &ss
, sizeof(ss
))) {
235 // XXX warnx("svc_fd_create: no mem for local addr");
239 /* Set xp_raddr for compatibility */
240 __xprt_set_raddr(ret
, &ss
);
245 if (ret
->xp_ltaddr
.buf
!= NULL
)
246 mem_free(ret
->xp_ltaddr
.buf
, rep
->xp_ltaddr
.maxlen
);
252 makefd_xprt(fd
, sendsize
, recvsize
)
260 struct __rpc_sockinfo si
;
262 assert(fd
!= SOCKET_ERROR
);
264 if (fd
>= FD_SETSIZE
) {
265 // XXX warnx("svc_vc: makefd_xprt: fd too high\n");
270 xprt
= mem_alloc(sizeof(SVCXPRT
));
272 // XXX warnx("svc_vc: makefd_xprt: out of memory");
275 memset(xprt
, 0, sizeof *xprt
);
276 cd
= mem_alloc(sizeof(struct cf_conn
));
278 // XXX warnx("svc_tcp: makefd_xprt: out of memory");
279 mem_free(xprt
, sizeof(SVCXPRT
));
283 cd
->strm_stat
= XPRT_IDLE
;
284 xdrrec_create(&(cd
->xdrs
), sendsize
, recvsize
,
285 xprt
, read_vc
, write_vc
);
287 xprt
->xp_verf
.oa_base
= cd
->verf_body
;
288 svc_vc_ops(xprt
); /* truely deals with calls */
289 xprt
->xp_port
= 0; /* this is a connection, not a rendezvouser */
291 if (__rpc_fd2sockinfo(fd
, &si
) && __rpc_sockinfo2netid(&si
, &netid
))
292 xprt
->xp_netid
= strdup(netid
);
301 rendezvous_request(xprt
, msg
)
309 struct cf_rendezvous
*r
;
311 struct sockaddr_storage addr
;
313 struct __rpc_sockinfo si
;
317 assert(xprt
!= NULL
);
320 r
= (struct cf_rendezvous
*)xprt
->xp_p1
;
323 if ((sock
= accept(xprt
->xp_fd
, (struct sockaddr
*)(void *)&addr
,
324 &len
)) == SOCKET_ERROR
) {
328 * Clean out the most idle file descriptor when we're
331 if (errno
== EMFILE
|| errno
== ENFILE
) {
332 cleanfds
= svc_fdset
;
333 __svc_clean_idle(&cleanfds
, 0, FALSE
);
339 * make a new transporter (re-uses xprt)
342 newxprt
= makefd_xprt(sock
, r
->sendsize
, r
->recvsize
);
344 if (!__rpc_set_netbuf(&newxprt
->xp_rtaddr
, &addr
, len
))
347 __xprt_set_raddr(newxprt
, &addr
);
349 if (__rpc_fd2sockinfo(sock
, &si
) && si
.si_proto
== IPPROTO_TCP
) {
351 /* XXX fvdl - is this useful? */
352 setsockopt(sock
, IPPROTO_TCP
, TCP_NODELAY
, (const char *)&len
, sizeof (len
));
355 cd
= (struct cf_conn
*)newxprt
->xp_p1
;
357 cd
->recvsize
= r
->recvsize
;
358 cd
->sendsize
= r
->sendsize
;
359 cd
->maxrec
= r
->maxrec
;
362 if (cd
->maxrec
!= 0) {
363 flags
= fcntl(sock
, F_GETFL
, 0);
366 if (fcntl(sock
, F_SETFL
, flags
| O_NONBLOCK
) == -1)
368 if (cd
->recvsize
> cd
->maxrec
)
369 cd
->recvsize
= cd
->maxrec
;
371 __xdrrec_setnonblock(&cd
->xdrs
, cd
->maxrec
);
373 cd
->nonblock
= FALSE
;
376 gettimeofday(&cd
->last_recv_time
, NULL
);
378 return (FALSE
); /* there is never an rpc msg to be processed */
382 static enum xprt_stat
383 rendezvous_stat(xprt
)
394 assert(xprt
!= NULL
);
396 xprt_unregister(xprt
);
397 __svc_vc_dodestroy(xprt
);
401 __svc_vc_dodestroy(xprt
)
405 struct cf_rendezvous
*r
;
407 cd
= (struct cf_conn
*)xprt
->xp_p1
;
409 if (xprt
->xp_fd
!= RPC_ANYFD
)
410 (void)closesocket(xprt
->xp_fd
);
411 if (xprt
->xp_port
!= 0) {
412 /* a rendezvouser socket */
413 r
= (struct cf_rendezvous
*)xprt
->xp_p1
;
414 mem_free(r
, sizeof (struct cf_rendezvous
));
417 /* an actual connection socket */
418 XDR_DESTROY(&(cd
->xdrs
));
419 mem_free(cd
, sizeof(struct cf_conn
));
421 if (xprt
->xp_rtaddr
.buf
)
422 mem_free(xprt
->xp_rtaddr
.buf
, xprt
->xp_rtaddr
.maxlen
);
423 if (xprt
->xp_ltaddr
.buf
)
424 mem_free(xprt
->xp_ltaddr
.buf
, xprt
->xp_ltaddr
.maxlen
);
428 free(xprt
->xp_netid
);
429 mem_free(xprt
, sizeof(SVCXPRT
));
434 svc_vc_control(xprt
, rq
, in
)
443 svc_vc_rendezvous_control(xprt
, rq
, in
)
448 struct cf_rendezvous
*cfp
;
450 cfp
= (struct cf_rendezvous
*)xprt
->xp_p1
;
454 case SVCGET_CONNMAXREC
:
455 *(int *)in
= cfp
->maxrec
;
457 case SVCSET_CONNMAXREC
:
458 cfp
->maxrec
= *(int *)in
;
467 * reads data from the tcp or uip connection.
468 * any error is fatal and the connection is closed.
469 * (And a read of zero bytes is a half closed stream => error.)
470 * All read operations timeout after 35 seconds. A timeout is
471 * fatal for the connection.
474 read_vc(xprtp
, buf
, len
)
481 int milliseconds
= 35 * 1000;
482 struct pollfd pollfd
;
485 xprt
= (SVCXPRT
*)xprtp
;
486 assert(xprt
!= NULL
);
490 cfp
= (struct cf_conn
*)xprt
->xp_p1
;
494 len
= recv(sock
, buf
, (size_t)len
, 0);
496 len
= read(sock
, buf
, (size_t)len
);
498 if (len
== SOCKET_ERROR
) {
499 if (WSAGetLastError() == EAGAIN
)
505 gettimeofday(&cfp
->last_recv_time
, NULL
);
512 pollfd
.events
= POLLIN
;
514 switch (poll(&pollfd
, 1, milliseconds
)) {
516 /* ReactOS: use select instead of poll */
518 struct timeval timeout
;
524 timeout
.tv_usec
= milliseconds
* 1000;
526 switch (select(0, &infd
, NULL
, NULL
, &timeout
)) {
539 } while ((pollfd
.revents
& POLLIN
) == 0);
545 if ((len
= recv(sock
, buf
, (size_t)len
, 0)) > 0) {
547 if ((len
= read(sock
, buf
, (size_t)len
)) > 0) {
549 gettimeofday(&cfp
->last_recv_time
, NULL
);
554 ((struct cf_conn
*)(xprt
->xp_p1
))->strm_stat
= XPRT_DIED
;
559 * writes data to the tcp connection.
560 * Any error is fatal and the connection is closed.
564 write_vc(xprtp
, ptr
, len
)
566 write_vc(xprtp
, buf
, len
)
579 struct timeval tv0
, tv1
;
584 xprt
= (SVCXPRT
*)xprtp
;
585 assert(xprt
!= NULL
);
587 cd
= (struct cf_conn
*)xprt
->xp_p1
;
590 gettimeofday(&tv0
, NULL
);
592 for (cnt
= len
; cnt
> 0; cnt
-= i
, buf
+= i
) {
594 i
= send(xprt
->xp_fd
, buf
, (size_t)cnt
, 0);
596 i
= write(xprt
->xp_fd
, buf
, (size_t)cnt
);
598 if (i
== SOCKET_ERROR
) {
599 if (WSAGetLastError() != EAGAIN
|| !cd
->nonblock
) {
600 cd
->strm_stat
= XPRT_DIED
;
603 if (cd
->nonblock
&& i
!= cnt
) {
605 * For non-blocking connections, do not
606 * take more than 2 seconds writing the
609 * XXX 2 is an arbitrary amount.
611 gettimeofday(&tv1
, NULL
);
612 if (tv1
.tv_sec
- tv0
.tv_sec
>= 2) {
613 cd
->strm_stat
= XPRT_DIED
;
623 static enum xprt_stat
629 assert(xprt
!= NULL
);
631 cd
= (struct cf_conn
*)(xprt
->xp_p1
);
633 if (cd
->strm_stat
== XPRT_DIED
)
635 if (! xdrrec_eof(&(cd
->xdrs
)))
636 return (XPRT_MOREREQS
);
641 svc_vc_recv(xprt
, msg
)
648 assert(xprt
!= NULL
);
651 cd
= (struct cf_conn
*)(xprt
->xp_p1
);
655 if (!__xdrrec_getrec(xdrs
, &cd
->strm_stat
, TRUE
))
659 xdrs
->x_op
= XDR_DECODE
;
660 (void)xdrrec_skiprecord(xdrs
);
661 if (xdr_callmsg(xdrs
, msg
)) {
662 cd
->x_id
= msg
->rm_xid
;
665 cd
->strm_stat
= XPRT_DIED
;
670 svc_vc_getargs(xprt
, xdr_args
, args_ptr
)
676 assert(xprt
!= NULL
);
677 /* args_ptr may be NULL */
678 return ((*xdr_args
)(&(((struct cf_conn
*)(xprt
->xp_p1
))->xdrs
),
683 svc_vc_freeargs(xprt
, xdr_args
, args_ptr
)
690 assert(xprt
!= NULL
);
691 /* args_ptr may be NULL */
693 xdrs
= &(((struct cf_conn
*)(xprt
->xp_p1
))->xdrs
);
695 xdrs
->x_op
= XDR_FREE
;
696 return ((*xdr_args
)(xdrs
, args_ptr
));
700 svc_vc_reply(xprt
, msg
)
708 assert(xprt
!= NULL
);
711 cd
= (struct cf_conn
*)(xprt
->xp_p1
);
714 xdrs
->x_op
= XDR_ENCODE
;
715 msg
->rm_xid
= cd
->x_id
;
716 rstat
= xdr_replymsg(xdrs
, msg
);
717 (void)xdrrec_endofrecord(xdrs
, TRUE
);
725 static struct xp_ops ops
;
726 static struct xp_ops2 ops2
;
727 extern mutex_t ops_lock
;
729 /* VARIABLES PROTECTED BY ops_lock: ops, ops2 */
731 mutex_lock(&ops_lock
);
732 if (ops
.xp_recv
== NULL
) {
733 ops
.xp_recv
= svc_vc_recv
;
734 ops
.xp_stat
= svc_vc_stat
;
735 ops
.xp_getargs
= svc_vc_getargs
;
736 ops
.xp_reply
= svc_vc_reply
;
737 ops
.xp_freeargs
= svc_vc_freeargs
;
738 ops
.xp_destroy
= svc_vc_destroy
;
739 ops2
.xp_control
= svc_vc_control
;
742 xprt
->xp_ops2
= &ops2
;
743 mutex_unlock(&ops_lock
);
747 svc_vc_rendezvous_ops(xprt
)
750 static struct xp_ops ops
;
751 static struct xp_ops2 ops2
;
752 extern mutex_t ops_lock
;
754 mutex_lock(&ops_lock
);
755 if (ops
.xp_recv
== NULL
) {
756 ops
.xp_recv
= rendezvous_request
;
757 ops
.xp_stat
= rendezvous_stat
;
759 (bool_t (*)(SVCXPRT
*, xdrproc_t
, void *))abort
;
761 (bool_t (*)(SVCXPRT
*, struct rpc_msg
*))abort
;
763 (bool_t (*)(SVCXPRT
*, xdrproc_t
, void *))abort
,
764 ops
.xp_destroy
= svc_vc_destroy
;
765 ops2
.xp_control
= svc_vc_rendezvous_control
;
768 xprt
->xp_ops2
= &ops2
;
769 mutex_unlock(&ops_lock
);
773 * Get the effective UID of the sending process. Used by rpcbind, keyserv
774 * and rpc.yppasswdd on AF_LOCAL.
777 __rpc_get_local_uid(SVCXPRT
*transp
, uid_t
*uid
) {
784 sock
= transp
->xp_fd
;
785 sa
= (struct sockaddr
*)transp
->xp_rtaddr
.buf
;
786 if (sa
->sa_family
== AF_UNIX
) {
787 ret
= getpeereid(sock
, &euid
, &egid
);
796 void timersub( const struct timeval
*tvp
, const struct timeval
*uvp
, struct timeval
*vvp
)
798 vvp
->tv_sec
= tvp
->tv_sec
- uvp
->tv_sec
;
799 vvp
->tv_usec
= tvp
->tv_usec
- uvp
->tv_usec
;
800 if( vvp
->tv_usec
< 0 )
803 vvp
->tv_usec
+= 1000000;
809 * Destroy xprts that have not have had any activity in 'timeout' seconds.
810 * If 'cleanblock' is true, blocking connections (the default) are also
811 * cleaned. If timeout is 0, the least active connection is picked.
814 __svc_clean_idle(fd_set
*fds
, int timeout
, bool_t cleanblock
)
817 SVCXPRT
*xprt
, *least_active
;
818 struct timeval tv
, tdiff
, tmax
;
821 gettimeofday(&tv
, NULL
);
822 tmax
.tv_sec
= tmax
.tv_usec
= 0;
824 rwlock_wrlock(&svc_fd_lock
);
825 for (i
= ncleaned
= 0; i
<= svc_maxfd
; i
++) {
826 if (FD_ISSET(i
, fds
)) {
827 xprt
= __svc_xports
[i
];
828 if (xprt
== NULL
|| xprt
->xp_ops
== NULL
||
829 xprt
->xp_ops
->xp_recv
!= svc_vc_recv
)
831 cd
= (struct cf_conn
*)xprt
->xp_p1
;
832 if (!cleanblock
&& !cd
->nonblock
)
835 timersub(&tv
, &cd
->last_recv_time
, &tdiff
);
836 if (timercmp(&tdiff
, &tmax
, >)) {
842 if (tv
.tv_sec
- cd
->last_recv_time
.tv_sec
> timeout
) {
843 __xprt_unregister_unlocked(xprt
);
844 __svc_vc_dodestroy(xprt
);
849 if (timeout
== 0 && least_active
!= NULL
) {
850 __xprt_unregister_unlocked(least_active
);
851 __svc_vc_dodestroy(least_active
);
854 rwlock_unlock(&svc_fd_lock
);
855 return ncleaned
> 0 ? TRUE
: FALSE
;