510 lines
14 KiB
C
510 lines
14 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
//%% (c) Copyright 1993, 1994 Hewlett-Packard Company
|
|
//%% (c) Copyright 1993, 1994 International Business Machines Corp.
|
|
//%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
|
|
//%% (c) Copyright 1993, 1994 Novell, Inc.
|
|
//%% $TOG: mp_rpc_server.C /main/11 1999/08/30 11:03:00 mgreess $
|
|
/*
|
|
*
|
|
* @(#)mp_rpc_server.C 1.46 94/11/17
|
|
*
|
|
* Copyright (c) 1990 by Sun Microsystems, Inc.
|
|
*/
|
|
#include "tt_options.h"
|
|
|
|
#include <stdio.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#include <errno.h>
|
|
|
|
#include "mp_rpc_server.h"
|
|
#include "util/tt_port.h"
|
|
#include "util/tt_gettext.h"
|
|
#include "util/tt_global_env.h"
|
|
#include "mp/mp_mp.h"
|
|
#include "mp/mp_rpc.h"
|
|
|
|
#if defined(OPT_TLI)
|
|
#include <netdir.h>
|
|
static int gettransient(int, netconfig *, netbuf *);
|
|
#if defined(OPT_BUG_SUNOS_5)
|
|
extern "C" { char * nc_sperror(); }
|
|
#endif
|
|
#else
|
|
#include <rpc/pmap_clnt.h>
|
|
#include <netinet/tcp.h>
|
|
static int gettransient(int,int,int *);
|
|
#endif /* OPT_TLI */
|
|
|
|
#if defined(OPT_BUG_AIX)
|
|
typedef void (*SERVICE_FN_TYPE)();
|
|
#else
|
|
typedef void (*SERVICE_FN_TYPE)(struct svc_req *, SVCXPRT*);
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Constructs an rpc server for the given program, version and socket.
|
|
*/
|
|
_Tt_rpc_server::
|
|
_Tt_rpc_server(int program, int version, int Rsocket, _Tt_auth &auth)
|
|
{
|
|
_version = version;
|
|
_socket = Rsocket;
|
|
_program = program;
|
|
_auth = auth;
|
|
_rpc_fd = 0;
|
|
_transp = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Destroys an rpc server. Unsets the program,version mapping in the
|
|
* portmapper.
|
|
*/
|
|
_Tt_rpc_server::
|
|
~_Tt_rpc_server()
|
|
{
|
|
#ifndef OPT_TLI
|
|
/*
|
|
* pmap_unset(_program, _version);
|
|
*/
|
|
#else
|
|
for (int version = _version; version >= 1; version--) {
|
|
rpcb_unset(_program, version, (netconfig *)0);
|
|
}
|
|
#endif // OPT_TLI
|
|
}
|
|
|
|
|
|
/*
|
|
* Initializes an rpc server with a service function. If _program is set
|
|
* to -1 then an unused program number is obtained using the gettransient
|
|
* function. If _socket is anything other than RPC_ANYSOCK then it will
|
|
* be used to create the rpc transport using svfd_create and the rpc
|
|
* numbers will not be registered with the portmapper.
|
|
*/
|
|
int _Tt_rpc_server::
|
|
init(void (*service_fn)(struct svc_req *, SVCXPRT *))
|
|
{
|
|
char *bufopt = (char *)0;
|
|
|
|
#ifndef OPT_TLI
|
|
|
|
bufopt = getenv("TT_BUFSIZE");
|
|
unsigned int buffersize = (bufopt != (char *)0) ? atoi(bufopt) : 32000;
|
|
|
|
if (_socket != RPC_ANYSOCK) {
|
|
_transp = svcfd_create(_socket, buffersize, buffersize);
|
|
if (_transp == (SVCXPRT *)0) {
|
|
return(0);
|
|
}
|
|
if (!svc_register(_transp, _program, _version,
|
|
(SERVICE_FN_TYPE)service_fn, 0))
|
|
{
|
|
_tt_syslog(0, LOG_ERR, "svc_register(): %m");
|
|
return(0);
|
|
}
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
if (_program == -1) {
|
|
if (! (_program =
|
|
gettransient(IPPROTO_TCP, _version, &_socket))) {
|
|
return(0);
|
|
}
|
|
} else {
|
|
_socket = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (_socket < 0) {
|
|
_tt_syslog(0, LOG_ERR,
|
|
"_Tt_rpc_server::init(): socket(): %m");
|
|
return 0;
|
|
}
|
|
}
|
|
int optval = 1;
|
|
if (setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY,
|
|
(char *)&optval, sizeof(int)) == -1) {
|
|
_tt_syslog(0, LOG_ERR, "setsockopt(TCP_NODELAY): %m");
|
|
}
|
|
if (setsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char *)&buffersize,
|
|
sizeof(int)) == -1) {
|
|
_tt_syslog(0, LOG_ERR, "setsockopt(SO_RCVBUF): %m");
|
|
}
|
|
if (setsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize,
|
|
sizeof(int)) == -1) {
|
|
_tt_syslog(0, LOG_ERR, "setsockopt(SO_SNDBUF): %m");
|
|
}
|
|
_transp = svctcp_create(_socket, buffersize, buffersize);
|
|
|
|
if (_transp == (SVCXPRT *)0) {
|
|
return(0);
|
|
}
|
|
|
|
#if defined(OPT_TIRPC)
|
|
/* JET: HACK WARNING 7/1/18
|
|
*
|
|
* With earlier versions of RPC and TIRPC it seems that
|
|
* svctcp_create() calles listen() on the socket (as seen by
|
|
* debugger and strace). This is the expected behavior in TT.
|
|
*
|
|
* However, with newer systems (ArchLinux 5/18+ and similar
|
|
* bleeding edge versions of SuSE's equivalent: Tumbleweed),
|
|
* this behavior seems to have changed.
|
|
*
|
|
* ttsession goes into an infinite loop trying to accept() a
|
|
* connection in the TIRPC library. It appears listen() is no
|
|
* longer called on the socket via svctcp_create(). The hack
|
|
* below, always causes listen() to be called on the socket.
|
|
* We do not care if it fails, or is called twice on the same
|
|
* socket.
|
|
*/
|
|
listen(_socket, 5);
|
|
#endif
|
|
|
|
if ( !svc_register(_transp, _program, _version,
|
|
(SERVICE_FN_TYPE)service_fn, IPPROTO_TCP)
|
|
|| !svc_register(_transp, _program, 1,
|
|
(SERVICE_FN_TYPE)service_fn, 0))
|
|
{
|
|
_tt_syslog(0, LOG_ERR, "svc_register(): %m");
|
|
return(0);
|
|
}
|
|
#else
|
|
netconfig *nconf;
|
|
void *handlep;
|
|
t_info tinfo;
|
|
int fd;
|
|
|
|
|
|
if ((handlep = setnetconfig()) == (void *)0) {
|
|
_tt_syslog(0, LOG_ERR, "setnetconfig(): %s", nc_sperror());
|
|
return(0);
|
|
}
|
|
|
|
// Find a connection-oriented transport.
|
|
while (nconf = getnetconfig(handlep)) {
|
|
if ((nconf->nc_semantics == NC_TPI_COTS) ||
|
|
(nconf->nc_semantics == NC_TPI_COTS_ORD)) {
|
|
|
|
// Make sure this netconfig maps to an address
|
|
if (0 == strcmp(nconf->nc_protofmly, NC_INET))
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we failed to find a suitable transport, exit.
|
|
if (nconf == (netconfig *)0) {
|
|
endnetconfig(handlep);
|
|
_tt_syslog(0, LOG_ERR,
|
|
catgets(_ttcatd, 2, 3,
|
|
"No connection-oriented transport"));
|
|
return(0);
|
|
}
|
|
|
|
fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
|
|
if (fd == -1) {
|
|
_tt_syslog(0, LOG_ERR,
|
|
"_Tt_rpc_server::init(): t_open(): %s",
|
|
t_strerror( t_errno ) );
|
|
endnetconfig(handlep);
|
|
return 0;
|
|
}
|
|
// No longer need to try to set NODELAY here as TIRPC does it for us
|
|
// tinfo.tsdu can be negative, but that's not a valid buf size.
|
|
u_int buf_size = 0;
|
|
if (tinfo.tsdu > 0) {
|
|
buf_size = (u_int)tinfo.tsdu;
|
|
}
|
|
_transp = svc_tli_create(fd, nconf, (struct t_bind *)0,
|
|
buf_size, buf_size);
|
|
if (_transp == (SVCXPRT *)0) {
|
|
_tt_syslog(0, LOG_ERR, "svc_tli_create(): 0");
|
|
(void)t_close(fd);
|
|
return(0);
|
|
}
|
|
if (_program == -1 &&
|
|
(! (_program = gettransient(_version, nconf,
|
|
&_transp->xp_ltaddr)))) {
|
|
_tt_syslog(0, LOG_ERR, "gettransient(): 0");
|
|
return(0);
|
|
}
|
|
for (int version = _version; version >= 1; version--) {
|
|
if (!svc_reg(_transp, _program, version,
|
|
(SERVICE_FN_TYPE)service_fn, nconf)) {
|
|
_tt_syslog(0, LOG_ERR, "svc_reg(,,%d): 0", version);
|
|
return(0);
|
|
}
|
|
}
|
|
// it is important to not call endnetconfig until one is done
|
|
// using nconf as endnetconfig frees the nconf storage.
|
|
(void)endnetconfig(handlep);
|
|
#endif /* OPT_TLI */
|
|
// now figure out what fd the rpc package is using
|
|
for (int i=0; i < FD_SETSIZE; i++) {
|
|
if (FD_ISSET(i, &svc_fdset)) {
|
|
_rpc_fd = i;
|
|
}
|
|
}
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Runs an rpc server. If a non-negative timeout is given then this
|
|
* function will return if the timeout expired before any rpc requests
|
|
* came in. The values returned are: -1 for error, 0 for timeout, 1
|
|
* for when timeout is 0 and an rpc request was serviced.
|
|
*/
|
|
_Tt_rpcsrv_err _Tt_rpc_server::
|
|
run_until(int *stop, int timeout, _Tt_int_rec_list_ptr &efds)
|
|
{
|
|
fd_set readfds;
|
|
timeval tmout;
|
|
int fd;
|
|
int done = 0;
|
|
int select_stat;
|
|
_Tt_rpcsrv_err status = _TT_RPCSRV_OK;
|
|
|
|
tmout.tv_sec = timeout;
|
|
tmout.tv_usec = 0;
|
|
_Tt_int_rec_list_cursor efds_c(efds);
|
|
do {
|
|
// Add our fd's to a copy of the rpc fdset.
|
|
readfds = svc_fdset;
|
|
efds_c.reset();
|
|
while (efds_c.next()) {
|
|
fd = efds_c->val;
|
|
// NOTE that it is crucially important that the bit
|
|
// for fd 0 not be set. fd 0 (stdin) is always set
|
|
// to /dev/null, which is always active.
|
|
// The reason fd 0 is in efds at all is that
|
|
// _Tt_self_procid uses it as a dummy entry
|
|
// for ttsession itself, which doesn\'t need a
|
|
// signalling channel.
|
|
|
|
// I haven\'t verified this, but I bet it\'s possible
|
|
// for negative entries to be in the efds list too,
|
|
// representing signalling channels that were found
|
|
// active on a previous pass but are not yet cleared
|
|
// out.
|
|
if (fd > 0) {
|
|
FD_SET(fd, &readfds);
|
|
}
|
|
}
|
|
|
|
// Drop the global mutex around any polling or RPC calls.
|
|
|
|
_tt_global->drop_mutex();
|
|
|
|
select_stat =
|
|
select(FD_SETSIZE,&readfds, 0, 0,
|
|
(timeout >= 0) ? &tmout : (timeval *)0);
|
|
|
|
_tt_global->grab_mutex();
|
|
|
|
switch (select_stat) {
|
|
case -1:
|
|
return(_TT_RPCSRV_ERR);
|
|
case 0:
|
|
return(_TT_RPCSRV_TMOUT);
|
|
default:
|
|
// check for exception fds
|
|
efds_c.reset();
|
|
while (efds_c.next()) {
|
|
fd = efds_c->val;
|
|
if (fd < 0) continue; // -1 => not valid fd
|
|
if (FD_ISSET(fd, &readfds)) {
|
|
efds_c->val = (0 - fd);
|
|
status = _TT_RPCSRV_FDERR;
|
|
done = 1;
|
|
}
|
|
|
|
// Clear our fd from the fdset so
|
|
// svc_getreqset() won't get confused (bug
|
|
// 2000972).
|
|
|
|
FD_CLR(fd, &readfds);
|
|
}
|
|
svc_getreqset(&readfds);
|
|
}
|
|
} while ((! done) && ((stop == 0) || (! *stop)));
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns an unused transient program number. Definition taken out of
|
|
* the RPC manual.
|
|
*/
|
|
#ifdef OPT_TLI
|
|
static int
|
|
gettransient(int vers, netconfig *nconf, netbuf *address)
|
|
#else
|
|
static int
|
|
gettransient(int proto, int vers, int *sockp)
|
|
#endif /* OPT_TLI */
|
|
{
|
|
int prognum;
|
|
|
|
#ifndef OPT_TLI
|
|
int found;
|
|
int s;
|
|
#if defined(__linux__) || defined(CSRG_BASED)
|
|
socklen_t len;
|
|
#else
|
|
int len;
|
|
#endif
|
|
int socktype;
|
|
sockaddr_in addr;
|
|
sockaddr_in tport;
|
|
sockaddr_in uport;
|
|
|
|
switch (proto) {
|
|
case IPPROTO_UDP:
|
|
socktype = SOCK_DGRAM;
|
|
break;
|
|
case IPPROTO_TCP:
|
|
socktype = SOCK_STREAM;
|
|
break;
|
|
default:
|
|
return(0);
|
|
}
|
|
if (*sockp == RPC_ANYSOCK) {
|
|
s = socket(AF_INET, socktype, 0);
|
|
if (s < 0) {
|
|
_tt_syslog(0, LOG_ERR, "gettransient(): socket(): %m");
|
|
return 0;
|
|
}
|
|
*sockp = s;
|
|
} else {
|
|
s = *sockp;
|
|
}
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
addr.sin_port = htons(0);
|
|
addr.sin_family = AF_INET;
|
|
len = sizeof(addr);
|
|
if(bind(s, (sockaddr *)&addr, len) == -1) {
|
|
_tt_syslog(0, LOG_ERR, "bind(): %m");
|
|
return(0);
|
|
}
|
|
#if defined (_AIX) && (OSMAJORVERSION==4) && (OSMINORVERSION==2)
|
|
if (getsockname(s, (sockaddr *)&addr, (size_t *)&len) < 0) {
|
|
#else
|
|
if (getsockname(s, (sockaddr *)&addr, &len) < 0) {
|
|
#endif
|
|
_tt_syslog(0, LOG_ERR, "getsockname(): %m");
|
|
return(0);
|
|
}
|
|
|
|
int optval = 0;
|
|
#if !defined(__linux__)
|
|
if (setsockopt(s, SOL_SOCKET, SO_USELOOPBACK,
|
|
(char *)&optval, sizeof(optval)) == -1) {
|
|
}
|
|
#endif
|
|
#endif /* !OPT_TLI */
|
|
|
|
|
|
// Search for a transient rpc number in the range 0x40000000 -
|
|
// 0x5fffffff by starting in the middle of the range searching
|
|
// up and then searching down if that fails. The reason for
|
|
// this is to make it less likely for other programs to grab
|
|
// this transient number (since pmap_getport doesn't complain
|
|
// if you try to grab a number for udp and we have it grabbed
|
|
// for tcp).
|
|
|
|
// JET - this is way too many pnums to search, and causes what
|
|
// appears to be an infinite loop (though it isn't) if the
|
|
// user is running an rpcbind in secure mode and not using
|
|
// libtirpc - a common error. The end result is staring at
|
|
// the dthello welcome screen. So - rather than search this
|
|
// immense space, we will only search from start to +-50
|
|
// before bailing. If a hundred attmepts to get a transient
|
|
// fail, I don't see that doing approximately 537 million
|
|
// attempts are worth it :)
|
|
#define MAX_TRANS_RANGE 50
|
|
|
|
// search up in the range 0x4fffffff to 0x4fffffff + MAX_TRANS_RANGE
|
|
for (prognum = 0x4fffffff; prognum <= (0x4fffffff + MAX_TRANS_RANGE); prognum++) {
|
|
/* XXX: pmap_set allows the same prognum for different */
|
|
/* protocols so we hack around that by attemptint to */
|
|
/* set both tcp and udp. */
|
|
|
|
#ifndef OPT_TLI
|
|
found = (!pmap_getport(&uport, prognum, vers,
|
|
IPPROTO_UDP) &&
|
|
!pmap_getport(&tport, prognum, vers, proto));
|
|
if (found &&
|
|
(found = pmap_set(prognum,
|
|
vers,
|
|
proto,
|
|
ntohs(addr.sin_port))) &&
|
|
(vers==1 || (found = pmap_set(prognum,
|
|
1,
|
|
proto,
|
|
ntohs(addr.sin_port))))) {
|
|
return(prognum);
|
|
}
|
|
#else
|
|
if (rpcb_set(prognum, vers, nconf, address) &&
|
|
(vers==1 || rpcb_set(prognum, 1, nconf, address))) {
|
|
return(prognum);
|
|
}
|
|
#endif /* !OPT_TLI */
|
|
}
|
|
|
|
// search down in the range 0x4ffffffe - 0x40000000
|
|
for (prognum = 0x4ffffffe; prognum >= (0x4ffffffe - MAX_TRANS_RANGE); prognum--) {
|
|
/* XXX: pmap_set allows the same prognum for different */
|
|
/* protocols so we hack around that by attemptint to */
|
|
/* set both tcp and udp. */
|
|
#ifndef OPT_TLI
|
|
found = (!pmap_getport(&uport, prognum, vers,
|
|
IPPROTO_UDP) &&
|
|
!pmap_getport(&tport, prognum, vers, proto));
|
|
if (found &&
|
|
(found = pmap_set(prognum,
|
|
vers,
|
|
proto,
|
|
ntohs(addr.sin_port)))) {
|
|
return(prognum);
|
|
}
|
|
#else
|
|
if (rpcb_set(prognum, vers, nconf, address)) {
|
|
return(prognum);
|
|
}
|
|
#endif /* !OPT_TLI */
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
|