Blame sunrpc/svc_udp.c

Packit 6c4009
/*
Packit 6c4009
 * svc_udp.c,
Packit 6c4009
 * Server side for UDP/IP based RPC.  (Does some caching in the hopes of
Packit 6c4009
 * achieving execute-at-most-once semantics.)
Packit 6c4009
 *
Packit 6c4009
 * Copyright (C) 2012-2018 Free Software Foundation, Inc.
Packit 6c4009
 * This file is part of the GNU C Library.
Packit 6c4009
 *
Packit 6c4009
 * The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
 * modify it under the terms of the GNU Lesser General Public
Packit 6c4009
 * License as published by the Free Software Foundation; either
Packit 6c4009
 * version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
 *
Packit 6c4009
 * The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
 * Lesser General Public License for more details.
Packit 6c4009
 *
Packit 6c4009
 * You should have received a copy of the GNU Lesser General Public
Packit 6c4009
 * License along with the GNU C Library; if not, see
Packit 6c4009
 * <http://www.gnu.org/licenses/>.
Packit 6c4009
 *
Packit 6c4009
 * Copyright (c) 2010, Oracle America, Inc.
Packit 6c4009
 *
Packit 6c4009
 * Redistribution and use in source and binary forms, with or without
Packit 6c4009
 * modification, are permitted provided that the following conditions are
Packit 6c4009
 * met:
Packit 6c4009
 *
Packit 6c4009
 *     * Redistributions of source code must retain the above copyright
Packit 6c4009
 *       notice, this list of conditions and the following disclaimer.
Packit 6c4009
 *     * Redistributions in binary form must reproduce the above
Packit 6c4009
 *       copyright notice, this list of conditions and the following
Packit 6c4009
 *       disclaimer in the documentation and/or other materials
Packit 6c4009
 *       provided with the distribution.
Packit 6c4009
 *     * Neither the name of the "Oracle America, Inc." nor the names of its
Packit 6c4009
 *       contributors may be used to endorse or promote products derived
Packit 6c4009
 *       from this software without specific prior written permission.
Packit 6c4009
 *
Packit 6c4009
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit 6c4009
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit 6c4009
 *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
Packit 6c4009
 *   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
Packit 6c4009
 *   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
Packit 6c4009
 *   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
Packit 6c4009
 *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
Packit 6c4009
 *   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
Packit 6c4009
 *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
Packit 6c4009
 *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
Packit 6c4009
 *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
Packit 6c4009
 *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <rpc/rpc.h>
Packit 6c4009
#include <sys/socket.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
Packit 6c4009
#ifdef IP_PKTINFO
Packit 6c4009
#include <sys/uio.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#include <wchar.h>
Packit 6c4009
#include <libio/iolibio.h>
Packit 6c4009
#include <shlib-compat.h>
Packit 6c4009
Packit 6c4009
#define rpc_buffer(xprt) ((xprt)->xp_p1)
Packit 6c4009
#ifndef MAX
Packit 6c4009
#define MAX(a, b)     ((a > b) ? a : b)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
static bool_t svcudp_recv (SVCXPRT *, struct rpc_msg *);
Packit 6c4009
static bool_t svcudp_reply (SVCXPRT *, struct rpc_msg *);
Packit 6c4009
static enum xprt_stat svcudp_stat (SVCXPRT *);
Packit 6c4009
static bool_t svcudp_getargs (SVCXPRT *, xdrproc_t, caddr_t);
Packit 6c4009
static bool_t svcudp_freeargs (SVCXPRT *, xdrproc_t, caddr_t);
Packit 6c4009
static void svcudp_destroy (SVCXPRT *);
Packit 6c4009
Packit 6c4009
static const struct xp_ops svcudp_op =
Packit 6c4009
{
Packit 6c4009
  svcudp_recv,
Packit 6c4009
  svcudp_stat,
Packit 6c4009
  svcudp_getargs,
Packit 6c4009
  svcudp_reply,
Packit 6c4009
  svcudp_freeargs,
Packit 6c4009
  svcudp_destroy
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static int cache_get (SVCXPRT *, struct rpc_msg *, char **replyp,
Packit 6c4009
		      u_long *replylenp);
Packit 6c4009
static void cache_set (SVCXPRT *xprt, u_long replylen);
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * kept in xprt->xp_p2
Packit 6c4009
 */
Packit 6c4009
struct svcudp_data
Packit 6c4009
  {
Packit 6c4009
    u_int su_iosz;		/* byte size of send.recv buffer */
Packit 6c4009
    u_long su_xid;		/* transaction id */
Packit 6c4009
    XDR su_xdrs;		/* XDR handle */
Packit 6c4009
    char su_verfbody[MAX_AUTH_BYTES];	/* verifier body */
Packit 6c4009
    char *su_cache;		/* cached data, NULL if no cache */
Packit 6c4009
  };
Packit 6c4009
#define	su_data(xprt)	((struct svcudp_data *)(xprt->xp_p2))
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Usage:
Packit 6c4009
 *      xprt = svcudp_create(sock);
Packit 6c4009
 *
Packit 6c4009
 * If sock<0 then a socket is created, else sock is used.
Packit 6c4009
 * If the socket, sock is not bound to a port then svcudp_create
Packit 6c4009
 * binds it to an arbitrary port.  In any (successful) case,
Packit 6c4009
 * xprt->xp_sock is the registered socket number and xprt->xp_port is the
Packit 6c4009
 * associated port number.
Packit 6c4009
 * Once *xprt is initialized, it is registered as a transporter;
Packit 6c4009
 * see (svc.h, xprt_register).
Packit 6c4009
 * The routines returns NULL if a problem occurred.
Packit 6c4009
 */
Packit 6c4009
SVCXPRT *
Packit 6c4009
svcudp_bufcreate (int sock, u_int sendsz, u_int recvsz)
Packit 6c4009
{
Packit 6c4009
  bool_t madesock = FALSE;
Packit 6c4009
  SVCXPRT *xprt;
Packit 6c4009
  struct svcudp_data *su;
Packit 6c4009
  struct sockaddr_in addr;
Packit 6c4009
  socklen_t len = sizeof (struct sockaddr_in);
Packit 6c4009
  int pad;
Packit 6c4009
  void *buf;
Packit 6c4009
Packit 6c4009
  if (sock == RPC_ANYSOCK)
Packit 6c4009
    {
Packit 6c4009
      if ((sock = __socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
Packit 6c4009
	{
Packit 6c4009
	  perror (_("svcudp_create: socket creation problem"));
Packit 6c4009
	  return (SVCXPRT *) NULL;
Packit 6c4009
	}
Packit 6c4009
      madesock = TRUE;
Packit 6c4009
    }
Packit 6c4009
  memset ((char *) &addr, 0, sizeof (addr));
Packit 6c4009
  addr.sin_family = AF_INET;
Packit 6c4009
  if (bindresvport (sock, &addr))
Packit 6c4009
    {
Packit 6c4009
      addr.sin_port = 0;
Packit 6c4009
      (void) __bind (sock, (struct sockaddr *) &addr, len);
Packit 6c4009
    }
Packit 6c4009
  if (__getsockname (sock, (struct sockaddr *) &addr, &len) != 0)
Packit 6c4009
    {
Packit 6c4009
      perror (_("svcudp_create - cannot getsockname"));
Packit 6c4009
      if (madesock)
Packit 6c4009
	(void) __close (sock);
Packit 6c4009
      return (SVCXPRT *) NULL;
Packit 6c4009
    }
Packit 6c4009
  xprt = (SVCXPRT *) mem_alloc (sizeof (SVCXPRT));
Packit 6c4009
  su = (struct svcudp_data *) mem_alloc (sizeof (*su));
Packit 6c4009
  buf = mem_alloc (((MAX (sendsz, recvsz) + 3) / 4) * 4);
Packit 6c4009
  if (xprt == NULL || su == NULL || buf == NULL)
Packit 6c4009
    {
Packit 6c4009
      (void) __fxprintf (NULL, "%s: %s",
Packit 6c4009
			 "svcudp_create",  _("out of memory\n"));
Packit 6c4009
      mem_free (xprt, sizeof (SVCXPRT));
Packit 6c4009
      mem_free (su, sizeof (*su));
Packit 6c4009
      mem_free (buf, ((MAX (sendsz, recvsz) + 3) / 4) * 4);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
  su->su_iosz = ((MAX (sendsz, recvsz) + 3) / 4) * 4;
Packit 6c4009
  rpc_buffer (xprt) = buf;
Packit 6c4009
  xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_DECODE);
Packit 6c4009
  su->su_cache = NULL;
Packit 6c4009
  xprt->xp_p2 = (caddr_t) su;
Packit 6c4009
  xprt->xp_verf.oa_base = su->su_verfbody;
Packit 6c4009
  xprt->xp_ops = &svcudp_op;
Packit 6c4009
  xprt->xp_port = ntohs (addr.sin_port);
Packit 6c4009
  xprt->xp_sock = sock;
Packit 6c4009
Packit 6c4009
#ifdef IP_PKTINFO
Packit 6c4009
  if ((sizeof (struct iovec) + sizeof (struct msghdr)
Packit 6c4009
       + sizeof(struct cmsghdr) + sizeof (struct in_pktinfo))
Packit 6c4009
      > sizeof (xprt->xp_pad))
Packit 6c4009
    {
Packit 6c4009
      (void) __fxprintf (NULL,"%s", _("\
Packit 6c4009
svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
  pad = 1;
Packit 6c4009
  if (__setsockopt (sock, SOL_IP, IP_PKTINFO, (void *) &pad,
Packit 6c4009
		    sizeof (pad)) == 0)
Packit 6c4009
    /* Set the padding to all 1s. */
Packit 6c4009
    pad = 0xff;
Packit 6c4009
  else
Packit 6c4009
#endif
Packit 6c4009
    /* Clear the padding. */
Packit 6c4009
    pad = 0;
Packit 6c4009
  memset (&xprt->xp_pad [0], pad, sizeof (xprt->xp_pad));
Packit 6c4009
Packit 6c4009
  xprt_register (xprt);
Packit 6c4009
  return xprt;
Packit 6c4009
}
Packit 6c4009
#ifdef EXPORT_RPC_SYMBOLS
Packit 6c4009
libc_hidden_def (svcudp_bufcreate)
Packit 6c4009
#else
Packit 6c4009
libc_hidden_nolink_sunrpc (svcudp_bufcreate, GLIBC_2_0)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
SVCXPRT *
Packit 6c4009
svcudp_create (int sock)
Packit 6c4009
{
Packit 6c4009
  return svcudp_bufcreate (sock, UDPMSGSIZE, UDPMSGSIZE);
Packit 6c4009
}
Packit 6c4009
#ifdef EXPORT_RPC_SYMBOLS
Packit 6c4009
libc_hidden_def (svcudp_create)
Packit 6c4009
#else
Packit 6c4009
libc_hidden_nolink_sunrpc (svcudp_create, GLIBC_2_0)
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
static enum xprt_stat
Packit 6c4009
svcudp_stat (SVCXPRT *xprt)
Packit 6c4009
{
Packit 6c4009
Packit 6c4009
  return XPRT_IDLE;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static bool_t
Packit 6c4009
svcudp_recv (SVCXPRT *xprt, struct rpc_msg *msg)
Packit 6c4009
{
Packit 6c4009
  struct svcudp_data *su = su_data (xprt);
Packit 6c4009
  XDR *xdrs = &(su->su_xdrs);
Packit 6c4009
  int rlen;
Packit 6c4009
  char *reply;
Packit 6c4009
  u_long replylen;
Packit 6c4009
  socklen_t len;
Packit 6c4009
Packit 6c4009
  /* It is very tricky when you have IP aliases. We want to make sure
Packit 6c4009
     that we are sending the packet from the IP address where the
Packit 6c4009
     incoming packet is addressed to. H.J. */
Packit 6c4009
#ifdef IP_PKTINFO
Packit 6c4009
  struct iovec *iovp;
Packit 6c4009
  struct msghdr *mesgp;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
again:
Packit 6c4009
  /* FIXME -- should xp_addrlen be a size_t?  */
Packit 6c4009
  len = (socklen_t) sizeof(struct sockaddr_in);
Packit 6c4009
#ifdef IP_PKTINFO
Packit 6c4009
  iovp = (struct iovec *) &xprt->xp_pad [0];
Packit 6c4009
  mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
Packit 6c4009
  if (mesgp->msg_iovlen)
Packit 6c4009
    {
Packit 6c4009
      iovp->iov_base = rpc_buffer (xprt);
Packit 6c4009
      iovp->iov_len = su->su_iosz;
Packit 6c4009
      mesgp->msg_iov = iovp;
Packit 6c4009
      mesgp->msg_iovlen = 1;
Packit 6c4009
      mesgp->msg_name = &(xprt->xp_raddr);
Packit 6c4009
      mesgp->msg_namelen = len;
Packit 6c4009
      mesgp->msg_control = &xprt->xp_pad [sizeof (struct iovec)
Packit 6c4009
					  + sizeof (struct msghdr)];
Packit 6c4009
      mesgp->msg_controllen = sizeof(xprt->xp_pad)
Packit 6c4009
			      - sizeof (struct iovec) - sizeof (struct msghdr);
Packit 6c4009
      rlen = __recvmsg (xprt->xp_sock, mesgp, 0);
Packit 6c4009
      if (rlen >= 0)
Packit 6c4009
	{
Packit 6c4009
	  struct cmsghdr *cmsg;
Packit 6c4009
	  len = mesgp->msg_namelen;
Packit 6c4009
	  cmsg = CMSG_FIRSTHDR (mesgp);
Packit 6c4009
	  if (cmsg == NULL
Packit 6c4009
	      || CMSG_NXTHDR (mesgp, cmsg) != NULL
Packit 6c4009
	      || cmsg->cmsg_level != SOL_IP
Packit 6c4009
	      || cmsg->cmsg_type != IP_PKTINFO
Packit 6c4009
	      || cmsg->cmsg_len < (sizeof (struct cmsghdr)
Packit 6c4009
				   + sizeof (struct in_pktinfo)))
Packit 6c4009
	    {
Packit 6c4009
	      /* Not a simple IP_PKTINFO, ignore it.  */
Packit 6c4009
	      mesgp->msg_control = NULL;
Packit 6c4009
	      mesgp->msg_controllen = 0;
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      /* It was a simple IP_PKTIFO as we expected, discard the
Packit 6c4009
		 interface field.  */
Packit 6c4009
	      struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
Packit 6c4009
	      pkti->ipi_ifindex = 0;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
#endif
Packit 6c4009
    rlen = __recvfrom (xprt->xp_sock, rpc_buffer (xprt),
Packit 6c4009
		       (int) su->su_iosz, 0,
Packit 6c4009
		       (struct sockaddr *) &(xprt->xp_raddr), &len;;
Packit 6c4009
  xprt->xp_addrlen = len;
Packit 6c4009
  if (rlen == -1)
Packit 6c4009
    {
Packit 6c4009
      if (errno == EINTR)
Packit 6c4009
	goto again;
Packit 6c4009
      __svc_accept_failed ();
Packit 6c4009
    }
Packit 6c4009
  if (rlen < 16)		/* < 4 32-bit ints? */
Packit 6c4009
    return FALSE;
Packit 6c4009
  xdrs->x_op = XDR_DECODE;
Packit 6c4009
  XDR_SETPOS (xdrs, 0);
Packit 6c4009
  if (!xdr_callmsg (xdrs, msg))
Packit 6c4009
    return FALSE;
Packit 6c4009
  su->su_xid = msg->rm_xid;
Packit 6c4009
  if (su->su_cache != NULL)
Packit 6c4009
    {
Packit 6c4009
      if (cache_get (xprt, msg, &reply, &replylen))
Packit 6c4009
	{
Packit 6c4009
#ifdef IP_PKTINFO
Packit 6c4009
	  if (mesgp->msg_iovlen)
Packit 6c4009
	    {
Packit 6c4009
	      iovp->iov_base = reply;
Packit 6c4009
	      iovp->iov_len = replylen;
Packit 6c4009
	      (void) __sendmsg (xprt->xp_sock, mesgp, 0);
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
#endif
Packit 6c4009
	    (void) __sendto (xprt->xp_sock, reply, (int) replylen, 0,
Packit 6c4009
			     (struct sockaddr *) &xprt->xp_raddr, len);
Packit 6c4009
	  return TRUE;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  return TRUE;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static bool_t
Packit 6c4009
svcudp_reply (SVCXPRT *xprt, struct rpc_msg *msg)
Packit 6c4009
{
Packit 6c4009
  struct svcudp_data *su = su_data (xprt);
Packit 6c4009
  XDR *xdrs = &(su->su_xdrs);
Packit 6c4009
  int slen, sent;
Packit 6c4009
  bool_t stat = FALSE;
Packit 6c4009
#ifdef IP_PKTINFO
Packit 6c4009
  struct iovec *iovp;
Packit 6c4009
  struct msghdr *mesgp;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  xdrs->x_op = XDR_ENCODE;
Packit 6c4009
  XDR_SETPOS (xdrs, 0);
Packit 6c4009
  msg->rm_xid = su->su_xid;
Packit 6c4009
  if (xdr_replymsg (xdrs, msg))
Packit 6c4009
    {
Packit 6c4009
      slen = (int) XDR_GETPOS (xdrs);
Packit 6c4009
#ifdef IP_PKTINFO
Packit 6c4009
      mesgp = (struct msghdr *) &xprt->xp_pad [sizeof (struct iovec)];
Packit 6c4009
      if (mesgp->msg_iovlen)
Packit 6c4009
	{
Packit 6c4009
	  iovp = (struct iovec *) &xprt->xp_pad [0];
Packit 6c4009
	  iovp->iov_base = rpc_buffer (xprt);
Packit 6c4009
	  iovp->iov_len = slen;
Packit 6c4009
	  sent = __sendmsg (xprt->xp_sock, mesgp, 0);
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
#endif
Packit 6c4009
	sent = __sendto (xprt->xp_sock, rpc_buffer (xprt), slen, 0,
Packit 6c4009
			 (struct sockaddr *) &(xprt->xp_raddr),
Packit 6c4009
			 xprt->xp_addrlen);
Packit 6c4009
      if (sent == slen)
Packit 6c4009
	{
Packit 6c4009
	  stat = TRUE;
Packit 6c4009
	  if (su->su_cache && slen >= 0)
Packit 6c4009
	    {
Packit 6c4009
	      cache_set (xprt, (u_long) slen);
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  return stat;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static bool_t
Packit 6c4009
svcudp_getargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
Packit 6c4009
{
Packit 6c4009
Packit 6c4009
  return (*xdr_args) (&(su_data (xprt)->su_xdrs), args_ptr);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static bool_t
Packit 6c4009
svcudp_freeargs (SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
Packit 6c4009
{
Packit 6c4009
  XDR *xdrs = &(su_data (xprt)->su_xdrs);
Packit 6c4009
Packit 6c4009
  xdrs->x_op = XDR_FREE;
Packit 6c4009
  return (*xdr_args) (xdrs, args_ptr);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
svcudp_destroy (SVCXPRT *xprt)
Packit 6c4009
{
Packit 6c4009
  struct svcudp_data *su = su_data (xprt);
Packit 6c4009
Packit 6c4009
  xprt_unregister (xprt);
Packit 6c4009
  (void) __close (xprt->xp_sock);
Packit 6c4009
  XDR_DESTROY (&(su->su_xdrs));
Packit 6c4009
  mem_free (rpc_buffer (xprt), su->su_iosz);
Packit 6c4009
  mem_free ((caddr_t) su, sizeof (struct svcudp_data));
Packit 6c4009
  mem_free ((caddr_t) xprt, sizeof (SVCXPRT));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/***********this could be a separate file*********************/
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Fifo cache for udp server
Packit 6c4009
 * Copies pointers to reply buffers into fifo cache
Packit 6c4009
 * Buffers are sent again if retransmissions are detected.
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
#define SPARSENESS 4		/* 75% sparse */
Packit 6c4009
Packit 6c4009
#define CACHE_PERROR(msg)	\
Packit 6c4009
	(void) __fxprintf(NULL, "%s\n", msg)
Packit 6c4009
Packit 6c4009
#define ALLOC(type, size)	\
Packit 6c4009
	(type *) mem_alloc((unsigned) (sizeof(type) * (size)))
Packit 6c4009
Packit 6c4009
#define CALLOC(type, size)	\
Packit 6c4009
  (type *) calloc (sizeof (type), size)
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * An entry in the cache
Packit 6c4009
 */
Packit 6c4009
typedef struct cache_node *cache_ptr;
Packit 6c4009
struct cache_node
Packit 6c4009
  {
Packit 6c4009
    /*
Packit 6c4009
     * Index into cache is xid, proc, vers, prog and address
Packit 6c4009
     */
Packit 6c4009
    u_long cache_xid;
Packit 6c4009
    u_long cache_proc;
Packit 6c4009
    u_long cache_vers;
Packit 6c4009
    u_long cache_prog;
Packit 6c4009
    struct sockaddr_in cache_addr;
Packit 6c4009
    /*
Packit 6c4009
     * The cached reply and length
Packit 6c4009
     */
Packit 6c4009
    char *cache_reply;
Packit 6c4009
    u_long cache_replylen;
Packit 6c4009
    /*
Packit 6c4009
     * Next node on the list, if there is a collision
Packit 6c4009
     */
Packit 6c4009
    cache_ptr cache_next;
Packit 6c4009
  };
Packit 6c4009
Packit 6c4009
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * The entire cache
Packit 6c4009
 */
Packit 6c4009
struct udp_cache
Packit 6c4009
  {
Packit 6c4009
    u_long uc_size;		/* size of cache */
Packit 6c4009
    cache_ptr *uc_entries;	/* hash table of entries in cache */
Packit 6c4009
    cache_ptr *uc_fifo;		/* fifo list of entries in cache */
Packit 6c4009
    u_long uc_nextvictim;	/* points to next victim in fifo list */
Packit 6c4009
    u_long uc_prog;		/* saved program number */
Packit 6c4009
    u_long uc_vers;		/* saved version number */
Packit 6c4009
    u_long uc_proc;		/* saved procedure number */
Packit 6c4009
    struct sockaddr_in uc_addr;	/* saved caller's address */
Packit 6c4009
  };
Packit 6c4009
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * the hashing function
Packit 6c4009
 */
Packit 6c4009
#define CACHE_LOC(transp, xid)	\
Packit 6c4009
 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
Packit 6c4009
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Enable use of the cache.
Packit 6c4009
 * Note: there is no disable.
Packit 6c4009
 */
Packit 6c4009
int
Packit 6c4009
svcudp_enablecache (SVCXPRT *transp, u_long size)
Packit 6c4009
{
Packit 6c4009
  struct svcudp_data *su = su_data (transp);
Packit 6c4009
  struct udp_cache *uc;
Packit 6c4009
Packit 6c4009
  if (su->su_cache != NULL)
Packit 6c4009
    {
Packit 6c4009
      CACHE_PERROR (_("enablecache: cache already enabled"));
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
  uc = ALLOC (struct udp_cache, 1);
Packit 6c4009
  if (uc == NULL)
Packit 6c4009
    {
Packit 6c4009
      CACHE_PERROR (_("enablecache: could not allocate cache"));
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
  uc->uc_size = size;
Packit 6c4009
  uc->uc_nextvictim = 0;
Packit 6c4009
  uc->uc_entries = CALLOC (cache_ptr, size * SPARSENESS);
Packit 6c4009
  if (uc->uc_entries == NULL)
Packit 6c4009
    {
Packit 6c4009
      mem_free (uc, sizeof (struct udp_cache));
Packit 6c4009
      CACHE_PERROR (_("enablecache: could not allocate cache data"));
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
  uc->uc_fifo = CALLOC (cache_ptr, size);
Packit 6c4009
  if (uc->uc_fifo == NULL)
Packit 6c4009
    {
Packit 6c4009
      mem_free (uc->uc_entries, size * SPARSENESS);
Packit 6c4009
      mem_free (uc, sizeof (struct udp_cache));
Packit 6c4009
      CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
  su->su_cache = (char *) uc;
Packit 6c4009
  return 1;
Packit 6c4009
}
Packit 6c4009
libc_hidden_nolink_sunrpc (svcudp_enablecache, GLIBC_2_0)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Set an entry in the cache
Packit 6c4009
 */
Packit 6c4009
static void
Packit 6c4009
cache_set (SVCXPRT *xprt, u_long replylen)
Packit 6c4009
{
Packit 6c4009
  cache_ptr victim;
Packit 6c4009
  cache_ptr *vicp;
Packit 6c4009
  struct svcudp_data *su = su_data (xprt);
Packit 6c4009
  struct udp_cache *uc = (struct udp_cache *) su->su_cache;
Packit 6c4009
  u_int loc;
Packit 6c4009
  char *newbuf;
Packit 6c4009
Packit 6c4009
  /*
Packit 6c4009
   * Find space for the new entry, either by
Packit 6c4009
   * reusing an old entry, or by mallocing a new one
Packit 6c4009
   */
Packit 6c4009
  victim = uc->uc_fifo[uc->uc_nextvictim];
Packit 6c4009
  if (victim != NULL)
Packit 6c4009
    {
Packit 6c4009
      loc = CACHE_LOC (xprt, victim->cache_xid);
Packit 6c4009
      for (vicp = &uc->uc_entries[loc];
Packit 6c4009
	   *vicp != NULL && *vicp != victim;
Packit 6c4009
	   vicp = &(*vicp)->cache_next)
Packit 6c4009
	;
Packit 6c4009
      if (*vicp == NULL)
Packit 6c4009
	{
Packit 6c4009
	  CACHE_PERROR (_("cache_set: victim not found"));
Packit 6c4009
	  return;
Packit 6c4009
	}
Packit 6c4009
      *vicp = victim->cache_next;	/* remote from cache */
Packit 6c4009
      newbuf = victim->cache_reply;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      victim = ALLOC (struct cache_node, 1);
Packit 6c4009
      if (victim == NULL)
Packit 6c4009
	{
Packit 6c4009
	  CACHE_PERROR (_("cache_set: victim alloc failed"));
Packit 6c4009
	  return;
Packit 6c4009
	}
Packit 6c4009
      newbuf = mem_alloc (su->su_iosz);
Packit 6c4009
      if (newbuf == NULL)
Packit 6c4009
	{
Packit 6c4009
	  mem_free (victim, sizeof (struct cache_node));
Packit 6c4009
	  CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
Packit 6c4009
	  return;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /*
Packit 6c4009
   * Store it away
Packit 6c4009
   */
Packit 6c4009
  victim->cache_replylen = replylen;
Packit 6c4009
  victim->cache_reply = rpc_buffer (xprt);
Packit 6c4009
  rpc_buffer (xprt) = newbuf;
Packit 6c4009
  xdrmem_create (&(su->su_xdrs), rpc_buffer (xprt), su->su_iosz, XDR_ENCODE);
Packit 6c4009
  victim->cache_xid = su->su_xid;
Packit 6c4009
  victim->cache_proc = uc->uc_proc;
Packit 6c4009
  victim->cache_vers = uc->uc_vers;
Packit 6c4009
  victim->cache_prog = uc->uc_prog;
Packit 6c4009
  victim->cache_addr = uc->uc_addr;
Packit 6c4009
  loc = CACHE_LOC (xprt, victim->cache_xid);
Packit 6c4009
  victim->cache_next = uc->uc_entries[loc];
Packit 6c4009
  uc->uc_entries[loc] = victim;
Packit 6c4009
  uc->uc_fifo[uc->uc_nextvictim++] = victim;
Packit 6c4009
  uc->uc_nextvictim %= uc->uc_size;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Try to get an entry from the cache
Packit 6c4009
 * return 1 if found, 0 if not found
Packit 6c4009
 */
Packit 6c4009
static int
Packit 6c4009
cache_get (SVCXPRT *xprt, struct rpc_msg *msg, char **replyp,
Packit 6c4009
	   u_long *replylenp)
Packit 6c4009
{
Packit 6c4009
  u_int loc;
Packit 6c4009
  cache_ptr ent;
Packit 6c4009
  struct svcudp_data *su = su_data (xprt);
Packit 6c4009
  struct udp_cache *uc = (struct udp_cache *) su->su_cache;
Packit 6c4009
Packit 6c4009
#define EQADDR(a1, a2)	(memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
Packit 6c4009
Packit 6c4009
  loc = CACHE_LOC (xprt, su->su_xid);
Packit 6c4009
  for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next)
Packit 6c4009
    {
Packit 6c4009
      if (ent->cache_xid == su->su_xid &&
Packit 6c4009
	  ent->cache_proc == uc->uc_proc &&
Packit 6c4009
	  ent->cache_vers == uc->uc_vers &&
Packit 6c4009
	  ent->cache_prog == uc->uc_prog &&
Packit 6c4009
	  EQADDR (ent->cache_addr, uc->uc_addr))
Packit 6c4009
	{
Packit 6c4009
	  *replyp = ent->cache_reply;
Packit 6c4009
	  *replylenp = ent->cache_replylen;
Packit 6c4009
	  return 1;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  /*
Packit 6c4009
   * Failed to find entry
Packit 6c4009
   * Remember a few things so we can do a set later
Packit 6c4009
   */
Packit 6c4009
  uc->uc_proc = msg->rm_call.cb_proc;
Packit 6c4009
  uc->uc_vers = msg->rm_call.cb_vers;
Packit 6c4009
  uc->uc_prog = msg->rm_call.cb_prog;
Packit 6c4009
  memcpy (&uc->uc_addr, &xprt->xp_raddr, sizeof (uc->uc_addr));
Packit 6c4009
  return 0;
Packit 6c4009
}