Logo Search packages:      
Sourcecode: nfs-user-server version File versions  Download package

rpcmisc.c

/*
 * rpcmisc  Miscellaneous functions for RPC startup and shutdown.
 *          This code is partially snarfed from rpcgen -s tcp -s udp,
 *          partly written by Mark Shand, Donald Becker, and Rick 
 *          Sladkey. It was tweaked slightly by Olaf Kirch to be
 *          useable by both nfsd and mountd.
 *
 *          This software may be used for any purpose provided
 *          the above copyright notice is retained.  It is supplied
 *          as is, with no warranty expressed or implied.
 *
 *          Auth daemon code by Olaf Kirch.
 */

#include "system.h"
#include <stdio.h>
#include <stdlib.h>
#include <rpc/pmap_clnt.h> 
#include <string.h> 
#include <signal.h>
#include <sys/ioctl.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <memory.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "rpcmisc.h"
#include "logging.h"

/* Another undefined function in RPC */
extern SVCXPRT *svcfd_create(int sock, u_int ssize, u_int rsize);

static int  makesock(int port, int proto, int socksz);

#define _RPCSVC_CLOSEDOWN     120
time_t            closedown = 0;
int         _rpcpmstart = 0;
int         _rpcfdtype = 0;
int         _rpcsvcdirty = 0;
const char *      auth_daemon = 0;

#ifdef AUTH_DAEMON
static bool_t     (*tcp_rendevouser)(SVCXPRT *, struct rpc_msg *);
static bool_t     (*tcp_receiver)(SVCXPRT *, struct rpc_msg *);
static bool_t     auth_rendevouser(SVCXPRT *, struct rpc_msg *);
static bool_t     auth_receiver(SVCXPRT *, struct rpc_msg *);
static void auth_handler(int sock);
#endif

void
rpc_init(const char *name, int prog, int *verstbl, void (*dispatch)(),
                  int defport, int bufsiz)
{
      struct sockaddr_in saddr;
      SVCXPRT     *transp;
      int   sock, i, vers;
      int   asize;

      /* When started from inetd, initialize only once */
      if (_rpcpmstart)
            return;

      asize = sizeof(saddr);
      sock = 0;
      if (getsockname(0, (struct sockaddr *) &saddr, &asize) == 0) {
            int   ssize = sizeof (int);

            if (saddr.sin_family != AF_INET)
                  goto not_inetd;
            if (getsockopt(0, SOL_SOCKET, SO_TYPE, &_rpcfdtype, &ssize) < 0)
                  goto not_inetd;
            background_logging();   /* no more logging to stderr */
            closedown = time(NULL) + _RPCSVC_CLOSEDOWN;
            _rpcpmstart = 1;
      } else {
not_inetd:
            _rpcfdtype = 0;
            for (i = 0; (vers = verstbl[i]) != 0; i++)
                  pmap_unset(prog, vers);
            sock = RPC_ANYSOCK;
      }

      if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
            if (_rpcfdtype == 0 && defport != 0)
                  sock = makesock(defport, IPPROTO_UDP, bufsiz);
            transp = svcudp_create(sock);
            if (transp == NULL)
                  Dprintf(L_FATAL, "cannot create udp service.");
            for (i = 0; (vers = verstbl[i]) != 0; i++) {
                  if (!svc_register(transp, prog, vers, dispatch, IPPROTO_UDP)) {
                        Dprintf(L_FATAL,
                              "unable to register (%s, %d, udp).",
                              name, vers);
                  }
            }
      }

      if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
            if (_rpcfdtype == 0 && defport != 0)
                  sock = makesock(defport, IPPROTO_TCP, bufsiz);
            transp = svctcp_create(sock, 0, 0);
            if (transp == NULL)
                  Dprintf(L_FATAL, "cannot create tcp service.");
#ifdef AUTH_DAEMON
            tcp_rendevouser = transp->xp_ops->xp_recv;
            transp->xp_ops->xp_recv = auth_rendevouser;
#endif
            for (i = 0; (vers = verstbl[i]) != 0; i++) {
                  if (!svc_register(transp, prog, vers, dispatch, IPPROTO_TCP)) {
                        Dprintf(L_FATAL,
                              "unable to register (%s, %d, tcp).",
                              name, vers);
                  }
            }
      }

      /* We ignore SIGPIPE. SIGPIPE is being sent to a daemon when trying
       * to do a sendmsg() on a TCP socket whose peer has disconnected.
       */
      if (!_rpcpmstart) {
            struct sigaction pipeact;

            memset(&pipeact, 0, sizeof(pipeact));
            pipeact.sa_handler = SIG_IGN;
            sigaction(SIGPIPE, &pipeact, NULL);
      }
}

void
rpc_exit(int prog, int *verstbl)
{
      int   i, vers;

      if (_rpcpmstart)
            return;
      for (i = 0; (vers = verstbl[i]) != 0; i++)
            pmap_unset(prog, vers);
}

void
rpc_closedown(void)
{
      struct sockaddr_in      sin;
      static int        size = 0;
      time_t                  now = time(NULL);
      int               i, len;

      if (!_rpcpmstart || now < closedown)
            return;
      if (_rpcsvcdirty == 0) {
            if (_rpcfdtype == SOCK_DGRAM)
                  exit(0);

            /* Okay, this is a TCP socket. Check whether we're still
             * connected */
            if (size == 0) {
                  size = getdtablesize();
            }
            for (i = 0; i < size; i++) {
                  if (!FD_ISSET(i, &svc_fdset))
                        continue;
                  len = sizeof(sin);
                  if (getpeername(i, (struct sockaddr *) &sin, &len) >= 0)
                        exit(0);
            }
      }
      closedown = now + _RPCSVC_CLOSEDOWN;
}

static int
makesock(int port, int proto, int socksz)
{
      struct sockaddr_in sin;
      const char  *prot_name;
      int         s;
      int         sock_type;

      switch (proto) {
      case IPPROTO_UDP:
            sock_type = SOCK_DGRAM;
            prot_name = "UDP";
            break;
      case IPPROTO_TCP:
            sock_type = SOCK_STREAM;
            prot_name = "TCP";
            break;
      default:
            Dprintf(L_FATAL, "Invalid protocol in makesock\n");
            return -1; /* NOTREACHED */
      }

      s = socket(AF_INET, sock_type, proto);
      if (s < 0)
            Dprintf(L_FATAL, "Could not make a %s socket: %s\n",
                              prot_name, strerror(errno));

      memset((char *) &sin, 0, sizeof(sin));
      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = INADDR_ANY;
      sin.sin_port = htons(port);

#ifdef DEBUG
      {
      int   val = 1;
      if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
            Dprintf(L_ERROR, "setsockopt failed: %s\n", strerror(errno));
      }
#endif

#ifdef SO_SNDBUF
      if (socksz != 0) {
            int sblen, rblen;

            /* 1024 for rpc & transport overheads */
            sblen = rblen = 8 * (socksz + 1024);
            if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sblen, sizeof sblen) < 0 ||
                setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rblen, sizeof rblen) < 0)
                  Dprintf(L_ERROR, "setsockopt failed: %s\n", strerror(errno));
      }
#endif                        /* SO_SNDBUF */

      if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) == -1)
            Dprintf(L_FATAL, "Could not bind %s socket to %s:%d: %s\n",
                              prot_name, inet_ntoa(sin.sin_addr), 
                              ntohs(sin.sin_port),
                              strerror(errno));

      return (s);
}

#ifdef AUTH_DAEMON
static bool_t
auth_rendevouser(SVCXPRT *xprt, struct rpc_msg *rpcmsg)
{
      struct sockaddr_in      sin;
      int               sock, slen = sizeof(sin);

      do {
            sock = accept(xprt->xp_sock, (struct sockaddr *) &sin, &slen);
      } while (sock < 0 && errno == EINTR);

      if (sock >= 0) {
            xprt = svcfd_create(sock, 0, 0);
            xprt->xp_raddr   = sin;
            xprt->xp_addrlen = slen;

            /* Swap the receive handler */
            if (auth_daemon) {
                  tcp_receiver = xprt->xp_ops->xp_recv;
                  xprt->xp_ops->xp_recv = auth_receiver;
            }
      }

      /* Always return false -- there's never an rpcmsg to be processed */
      return 0;
}

static bool_t
auth_receiver(SVCXPRT *xprt, struct rpc_msg *rpcmsg)
{
      __u32       buffer[10];
      struct msghdr     msg;
      struct iovec      iov;
      int         len;

      msg.msg_name      = 0;
      msg.msg_namelen   = 0;
      msg.msg_iov = &iov;
      msg.msg_iovlen    = 1;
      msg.msg_control   = 0;
      msg.msg_controllen = 0;

      iov.iov_base      = buffer;
      iov.iov_len = sizeof(buffer);

      len = recvmsg(xprt->xp_sock, &msg, MSG_PEEK);
      if (len < 16                  /* Initial packet too small */
       || buffer[2] != htonl(2)     /* RPC version 2 */
       || buffer[3] != htonl(0)) {  /* CALL */
            auth_handler(xprt->xp_sock);
            svc_destroy(xprt);
            return (0);
      }

      /* Okay--convert this back into an ordinary TCP socket */
      xprt->xp_ops->xp_recv = tcp_receiver;
      return svc_recv(xprt, rpcmsg);
}

static void
auth_handler(int sock)
{
      int   fds[2], pid, fd;

      if (socketpair(AF_INET, SOCK_STREAM, IPPROTO_TCP, fds) < 0) {
            Dprintf(L_ERROR, "Cannot create socket pair: %m");
            return;
      }

      if ((pid = fork()) < 0) {
            Dprintf(L_ERROR, "cannot fork: %m");
            return;
      }
      if (pid == 0) {
            /* Parent: create a new transport for this socket */
            svcfd_create(fds[0], 0, 0);
            return;
      }

      /* We're the child process. Set up the server socket on fd 0,
       * and the client socket on fd 1.
       */
      if (dup2(fds[1], 0) < 0 || dup2(sock, 1) < 0) {
            Dprintf(L_ERROR, "unable to dup: %m");
            exit(1);
      }

      log_close();
      for (fd = 2; fd < OPEN_MAX; fd++)
            close(fd);

      execl(auth_daemon, auth_daemon, 0);

      log_open(auth_daemon, 0);
      Dprintf(L_ERROR, "unable to execute: %m");
      exit(1);
}
#endif

Generated by  Doxygen 1.6.0   Back to index