1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
  27 /* All Rights Reserved */
  28 /*
  29  * Portions of this source code were derived from Berkeley
  30  * 4.3 BSD under license from the Regents of the University of
  31  * California.
  32  */
  33 
  34 /*
  35  * svc_dg.c, Server side for connectionless RPC.
  36  *
  37  * Does some caching in the hopes of achieving execute-at-most-once semantics.
  38  */
  39 
  40 #include "mt.h"
  41 #include "rpc_mt.h"
  42 #include <stdio.h>
  43 #include <sys/types.h>
  44 #include <sys/sysmacros.h>
  45 #include <rpc/rpc.h>
  46 #include <rpcsvc/svc_dg_priv.h>
  47 #include <errno.h>
  48 #include <syslog.h>
  49 #include <stdlib.h>
  50 #include <string.h>
  51 #include <ucred.h>
  52 #include <unistd.h>
  53 #include <sys/socket.h>
  54 #include <netinet/in.h>
  55 #include <arpa/inet.h>
  56 #ifdef RPC_CACHE_DEBUG
  57 #include <netconfig.h>
  58 #include <netdir.h>
  59 #endif
  60 
  61 #ifndef MAX
  62 #define MAX(a, b)       (((a) > (b)) ? (a) : (b))
  63 #endif
  64 
  65 static struct xp_ops *svc_dg_ops();
  66 static void cache_set();
  67 static int cache_get();
  68 
  69 #define rpc_buffer(xprt) ((xprt)->xp_p1)
  70 
  71 /*
  72  * Usage:
  73  *      xprt = svc_dg_create(sock, sendsize, recvsize);
  74  * Does other connectionless specific initializations.
  75  * Once *xprt is initialized, it is registered.
  76  * see (svc.h, xprt_register). If recvsize or sendsize are 0 suitable
  77  * system defaults are chosen.
  78  * The routines returns NULL if a problem occurred.
  79  */
  80 static const char svc_dg_str[] = "svc_dg_create: %s";
  81 static const char svc_dg_err1[] = "could not get transport information";
  82 static const char svc_dg_err2[] = " transport does not support data transfer";
  83 static const char svc_dg_err3[] =
  84                 "fd > FD_SETSIZE; Use rpc_control(RPC_SVC_USE_POLLFD,...);";
  85 static const char __no_mem_str[] = "out of memory";
  86 
  87 /* Structure used to initialize SVC_XP_AUTH(xprt).svc_ah_ops. */
  88 extern struct svc_auth_ops svc_auth_any_ops;
  89 extern int __rpc_get_ltaddr(struct netbuf *, struct netbuf *);
  90 
  91 void
  92 svc_dg_xprtfree(SVCXPRT *xprt)
  93 {
  94 /* LINTED pointer alignment */
  95         SVCXPRT_EXT             *xt = xprt ? SVCEXT(xprt) : NULL;
  96 /* LINTED pointer alignment */
  97         struct svc_dg_data      *su = xprt ? get_svc_dg_data(xprt) : NULL;
  98 
  99         if (xprt == NULL)
 100                 return;
 101         if (xprt->xp_netid)
 102                 free(xprt->xp_netid);
 103         if (xprt->xp_tp)
 104                 free(xprt->xp_tp);
 105         if (xt->parent == NULL)
 106                 if (xprt->xp_ltaddr.buf)
 107                         free(xprt->xp_ltaddr.buf);
 108         if (xprt->xp_rtaddr.buf)
 109                 free(xprt->xp_rtaddr.buf);
 110         if (su != NULL) {
 111                 XDR_DESTROY(&(su->su_xdrs));
 112                 free(su);
 113         }
 114         if (rpc_buffer(xprt))
 115                 free(rpc_buffer(xprt));
 116         svc_xprt_free(xprt);
 117 }
 118 
 119 SVCXPRT *
 120 svc_dg_create_private(int fd, uint_t sendsize, uint_t recvsize)
 121 {
 122         SVCXPRT *xprt;
 123         struct svc_dg_data *su = NULL;
 124         struct t_info tinfo;
 125         size_t ucred_sz = ucred_size();
 126 
 127         if (RPC_FD_NOTIN_FDSET(fd)) {
 128                 errno = EBADF;
 129                 t_errno = TBADF;
 130                 syslog(LOG_ERR, svc_dg_str, svc_dg_err3);
 131                 return (NULL);
 132         }
 133 
 134         if (t_getinfo(fd, &tinfo) == -1) {
 135                 syslog(LOG_ERR, svc_dg_str, svc_dg_err1);
 136                 return (NULL);
 137         }
 138         /*
 139          * Find the receive and the send size
 140          */
 141         sendsize = __rpc_get_t_size((int)sendsize, tinfo.tsdu);
 142         recvsize = __rpc_get_t_size((int)recvsize, tinfo.tsdu);
 143         if ((sendsize == 0) || (recvsize == 0)) {
 144                 syslog(LOG_ERR, svc_dg_str, svc_dg_err2);
 145                 return (NULL);
 146         }
 147 
 148         if ((xprt = svc_xprt_alloc()) == NULL)
 149                 goto freedata;
 150 /* LINTED pointer alignment */
 151         svc_flags(xprt) |= SVC_DGRAM;
 152 
 153         su = malloc(sizeof (*su) + ucred_sz);
 154         if (su == NULL)
 155                 goto freedata;
 156         su->su_iosz = ((MAX(sendsize, recvsize) + 3) / 4) * 4;
 157         if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL)
 158                 goto freedata;
 159         xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz,
 160             XDR_DECODE);
 161         su->su_cache = NULL;
 162         xprt->xp_fd = fd;
 163         xprt->xp_p2 = (caddr_t)su;
 164         xprt->xp_verf.oa_base = su->su_verfbody;
 165         xprt->xp_ops = svc_dg_ops();
 166 
 167         su->su_tudata.addr.maxlen =  0; /* Fill in later */
 168 
 169         su->su_tudata.udata.buf = (char *)rpc_buffer(xprt);
 170         su->su_tudata.opt.buf = (char *)su->opts;
 171         su->su_tudata.udata.maxlen = su->su_iosz;
 172         su->su_tudata.opt.maxlen = MAX_OPT_WORDS * sizeof (int) + ucred_sz;
 173 /* LINTED pointer alignment */
 174         SVC_XP_AUTH(xprt).svc_ah_ops = svc_auth_any_ops;
 175 /* LINTED pointer alignment */
 176         SVC_XP_AUTH(xprt).svc_ah_private = NULL;
 177         return (xprt);
 178 freedata:
 179         (void) syslog(LOG_ERR, svc_dg_str, __no_mem_str);
 180         if (xprt)
 181                 svc_dg_xprtfree(xprt);
 182         return (NULL);
 183 }
 184 
 185 SVCXPRT *
 186 svc_dg_create(const int fd, const uint_t sendsize, const uint_t recvsize)
 187 {
 188         SVCXPRT *xprt;
 189 
 190         if ((xprt = svc_dg_create_private(fd, sendsize, recvsize)) != NULL)
 191                 xprt_register(xprt);
 192         return (xprt);
 193 }
 194 
 195 SVCXPRT *
 196 svc_dg_xprtcopy(SVCXPRT *parent)
 197 {
 198         SVCXPRT                 *xprt;
 199         struct svc_dg_data      *su;
 200         size_t                  ucred_sz = ucred_size();
 201 
 202         if ((xprt = svc_xprt_alloc()) == NULL)
 203                 return (NULL);
 204 
 205 /* LINTED pointer alignment */
 206         SVCEXT(xprt)->parent = parent;
 207 /* LINTED pointer alignment */
 208         SVCEXT(xprt)->flags = SVCEXT(parent)->flags;
 209 
 210         xprt->xp_fd = parent->xp_fd;
 211         xprt->xp_port = parent->xp_port;
 212         xprt->xp_ops = svc_dg_ops();
 213         if (parent->xp_tp) {
 214                 xprt->xp_tp = (char *)strdup(parent->xp_tp);
 215                 if (xprt->xp_tp == NULL) {
 216                         syslog(LOG_ERR, "svc_dg_xprtcopy: strdup failed");
 217                         svc_dg_xprtfree(xprt);
 218                         return (NULL);
 219                 }
 220         }
 221         if (parent->xp_netid) {
 222                 xprt->xp_netid = (char *)strdup(parent->xp_netid);
 223                 if (xprt->xp_netid == NULL) {
 224                         syslog(LOG_ERR, "svc_dg_xprtcopy: strdup failed");
 225                         if (parent->xp_tp)
 226                                 free(parent->xp_tp);
 227                         svc_dg_xprtfree(xprt);
 228                         return (NULL);
 229                 }
 230         }
 231         xprt->xp_ltaddr = parent->xp_ltaddr;      /* shared with parent */
 232 
 233         xprt->xp_rtaddr = parent->xp_rtaddr;
 234         xprt->xp_rtaddr.buf = malloc(xprt->xp_rtaddr.maxlen);
 235         if (xprt->xp_rtaddr.buf == NULL) {
 236                 svc_dg_xprtfree(xprt);
 237                 return (NULL);
 238         }
 239         (void) memcpy(xprt->xp_rtaddr.buf, parent->xp_rtaddr.buf,
 240             xprt->xp_rtaddr.maxlen);
 241         xprt->xp_type = parent->xp_type;
 242 
 243         if ((su = malloc(sizeof (struct svc_dg_data) + ucred_sz)) == NULL) {
 244                 svc_dg_xprtfree(xprt);
 245                 return (NULL);
 246         }
 247 /* LINTED pointer alignment */
 248         su->su_iosz = get_svc_dg_data(parent)->su_iosz;
 249         if ((rpc_buffer(xprt) = malloc(su->su_iosz)) == NULL) {
 250                 svc_dg_xprtfree(xprt);
 251                 free(su);
 252                 return (NULL);
 253         }
 254         xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz,
 255             XDR_DECODE);
 256         su->su_cache = NULL;
 257         su->su_tudata.addr.maxlen =  0; /* Fill in later */
 258         su->su_tudata.udata.buf = (char *)rpc_buffer(xprt);
 259         su->su_tudata.opt.buf = (char *)su->opts;
 260         su->su_tudata.udata.maxlen = su->su_iosz;
 261         su->su_tudata.opt.maxlen = MAX_OPT_WORDS * sizeof (int) + ucred_sz;
 262         xprt->xp_p2 = (caddr_t)su;   /* get_svc_dg_data(xprt) = su */
 263         xprt->xp_verf.oa_base = su->su_verfbody;
 264 
 265         return (xprt);
 266 }
 267 
 268 /*ARGSUSED*/
 269 static enum xprt_stat
 270 svc_dg_stat(SVCXPRT *xprt)
 271 {
 272         return (XPRT_IDLE);
 273 }
 274 
 275 /*
 276  * Find the SCM_UCRED in src and place a pointer to that option alone in dest.
 277  * Note that these two 'netbuf' structures might be the same one, so the code
 278  * has to be careful about referring to src after changing dest.
 279  */
 280 static void
 281 extract_cred(const struct netbuf *src, struct netbuf *dest)
 282 {
 283         char *cp = src->buf;
 284         unsigned int len = src->len;
 285         const struct T_opthdr *opt;
 286         unsigned int olen;
 287 
 288         while (len >= sizeof (*opt)) {
 289                 /* LINTED: pointer alignment */
 290                 opt = (const struct T_opthdr *)cp;
 291                 olen = opt->len;
 292                 if (olen > len || olen < sizeof (*opt) ||
 293                     !IS_P2ALIGNED(olen, sizeof (t_uscalar_t)))
 294                         break;
 295                 if (opt->level == SOL_SOCKET && opt->name == SCM_UCRED) {
 296                         dest->buf = cp;
 297                         dest->len = olen;
 298                         return;
 299                 }
 300                 cp += olen;
 301                 len -= olen;
 302         }
 303         dest->len = 0;
 304 }
 305 
 306 /*
 307  * This routine extracts the destination IP address of the inbound RPC packet
 308  * and sets that as source IP address for the outbound response.
 309  */
 310 static void
 311 set_src_addr(SVCXPRT *xprt, struct netbuf *opt)
 312 {
 313         struct netbuf *nbufp, *ltaddr;
 314         struct T_opthdr *opthdr;
 315         in_pktinfo_t *pktinfo;
 316         struct sockaddr_in *sock = (struct sockaddr_in *)NULL;
 317 
 318         /* extract dest IP of inbound packet */
 319         /* LINTED pointer alignment */
 320         nbufp = (struct netbuf *)xprt->xp_p2;
 321         ltaddr = &xprt->xp_ltaddr;
 322         if (__rpc_get_ltaddr(nbufp, ltaddr) != 0)
 323                 return;
 324 
 325         /* do nothing for non-IPv4 packet */
 326         /* LINTED pointer alignment */
 327         sock = (struct sockaddr_in *)ltaddr->buf;
 328         if (sock->sin_family != AF_INET)
 329                 return;
 330 
 331         /* set desired option header */
 332         opthdr = (struct T_opthdr *)memalign(sizeof (int),
 333             sizeof (struct T_opthdr) + sizeof (in_pktinfo_t));
 334         if (opthdr == NULL)
 335                 return;
 336         opthdr->len = sizeof (struct T_opthdr) + sizeof (in_pktinfo_t);
 337         opthdr->level = IPPROTO_IP;
 338         opthdr->name = IP_PKTINFO;
 339 
 340         /*
 341          * 1. set source IP of outbound packet
 342          * 2. value '0' for index means IP layer uses this as source address
 343          */
 344         pktinfo = (in_pktinfo_t *)(opthdr + 1);
 345         (void) memset(pktinfo, 0, sizeof (in_pktinfo_t));
 346         pktinfo->ipi_spec_dst.s_addr = sock->sin_addr.s_addr;
 347         pktinfo->ipi_ifindex = 0;
 348 
 349         /* copy data into ancillary buffer */
 350         if (opthdr->len + opt->len <= opt->maxlen) {
 351                 (void) memcpy((void *)(opt->buf+opt->len), (const void *)opthdr,
 352                     opthdr->len);
 353                 opt->len += opthdr->len;
 354         }
 355         free(opthdr);
 356 }
 357 
 358 static bool_t
 359 svc_dg_recv(SVCXPRT *xprt, struct rpc_msg *msg)
 360 {
 361 /* LINTED pointer alignment */
 362         struct svc_dg_data *su = get_svc_dg_data(xprt);
 363         XDR *xdrs = &(su->su_xdrs);
 364         struct t_unitdata *tu_data = &(su->su_tudata);
 365         int moreflag;
 366         struct netbuf *nbufp;
 367         struct netconfig *nconf;
 368 
 369         /* XXX: tudata should have been made a part of the server handle */
 370         if (tu_data->addr.maxlen == 0)
 371                 tu_data->addr = xprt->xp_rtaddr;
 372 again:
 373         tu_data->addr.len = 0;
 374         tu_data->opt.len  = 0;
 375         tu_data->udata.len  = 0;
 376 
 377         moreflag = 0;
 378         if (t_rcvudata(xprt->xp_fd, tu_data, &moreflag) == -1) {
 379 #ifdef RPC_DEBUG
 380                 syslog(LOG_ERR, "svc_dg_recv: t_rcvudata t_errno=%d errno=%d\n",
 381                     t_errno, errno);
 382 #endif
 383                 if (t_errno == TLOOK) {
 384                         int lookres;
 385 
 386                         lookres = t_look(xprt->xp_fd);
 387                         if ((lookres & T_UDERR) &&
 388                             (t_rcvuderr(xprt->xp_fd,
 389                                     (struct t_uderr *)0) < 0)) {
 390                                 /*EMPTY*/
 391 #ifdef RPC_DEBUG
 392                                 syslog(LOG_ERR,
 393                                 "svc_dg_recv: t_rcvuderr t_errno = %d\n",
 394                                         t_errno);
 395 #endif
 396                         }
 397                         if (lookres & T_DATA)
 398                                 goto again;
 399                 } else if ((errno == EINTR) && (t_errno == TSYSERR))
 400                         goto again;
 401                 else {
 402                         return (FALSE);
 403                 }
 404         }
 405 
 406         if ((moreflag) ||
 407             (tu_data->udata.len < 4 * (uint_t)sizeof (uint32_t))) {
 408                 /*
 409                  * If moreflag is set, drop that data packet. Something wrong
 410                  */
 411                 return (FALSE);
 412         }
 413         su->optbuf = tu_data->opt;
 414         xprt->xp_rtaddr.len = tu_data->addr.len;
 415         xdrs->x_op = XDR_DECODE;
 416         XDR_SETPOS(xdrs, 0);
 417         if (!xdr_callmsg(xdrs, msg))
 418                 return (FALSE);
 419         su->su_xid = msg->rm_xid;
 420         if (su->su_cache != NULL) {
 421                 char *reply;
 422                 uint32_t replylen;
 423 
 424                 if (cache_get(xprt, msg, &reply, &replylen)) {
 425                         /* tu_data.addr is already set */
 426                         tu_data->udata.buf = reply;
 427                         tu_data->udata.len = (uint_t)replylen;
 428                         extract_cred(&tu_data->opt, &tu_data->opt);
 429                         set_src_addr(xprt, &tu_data->opt);
 430                         (void) t_sndudata(xprt->xp_fd, tu_data);
 431                         tu_data->udata.buf = (char *)rpc_buffer(xprt);
 432                         tu_data->opt.buf = (char *)su->opts;
 433                         return (FALSE);
 434                 }
 435         }
 436 
 437         /*
 438          * get local ip address
 439          */
 440 
 441         if ((nconf = getnetconfigent(xprt->xp_netid)) != NULL) {
 442                 if (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
 443                     strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
 444                         if (nconf->nc_semantics == NC_TPI_CLTS) {
 445                                 /* LINTED pointer cast */
 446                                 nbufp = (struct netbuf *)(xprt->xp_p2);
 447                                 if (__rpc_get_ltaddr(nbufp,
 448                                     &xprt->xp_ltaddr) < 0) {
 449                                         if (strcmp(nconf->nc_protofmly,
 450                                             NC_INET) == 0) {
 451                                                 syslog(LOG_ERR,
 452                                                     "svc_dg_recv: ip(udp), "
 453                                                     "t_errno=%d, errno=%d",
 454                                                     t_errno, errno);
 455                                         }
 456                                         if (strcmp(nconf->nc_protofmly,
 457                                             NC_INET6) == 0) {
 458                                                 syslog(LOG_ERR,
 459                                                     "svc_dg_recv: ip (udp6), "
 460                                                     "t_errno=%d, errno=%d",
 461                                                     t_errno, errno);
 462                                         }
 463                                         freenetconfigent(nconf);
 464                                         return (FALSE);
 465                                 }
 466                         }
 467                 }
 468                 freenetconfigent(nconf);
 469         }
 470         return (TRUE);
 471 }
 472 
 473 static bool_t
 474 svc_dg_reply(SVCXPRT *xprt, struct rpc_msg *msg)
 475 {
 476 /* LINTED pointer alignment */
 477         struct svc_dg_data *su = get_svc_dg_data(xprt);
 478         XDR *xdrs = &(su->su_xdrs);
 479         bool_t stat = FALSE;
 480         xdrproc_t xdr_results;
 481         caddr_t xdr_location;
 482         bool_t has_args;
 483 
 484         if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
 485             msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
 486                 has_args = TRUE;
 487                 xdr_results = msg->acpted_rply.ar_results.proc;
 488                 xdr_location = msg->acpted_rply.ar_results.where;
 489                 msg->acpted_rply.ar_results.proc = xdr_void;
 490                 msg->acpted_rply.ar_results.where = NULL;
 491         } else
 492                 has_args = FALSE;
 493 
 494         xdrs->x_op = XDR_ENCODE;
 495         XDR_SETPOS(xdrs, 0);
 496         msg->rm_xid = su->su_xid;
 497         if (xdr_replymsg(xdrs, msg) && (!has_args ||
 498 /* LINTED pointer alignment */
 499             SVCAUTH_WRAP(&SVC_XP_AUTH(xprt), xdrs, xdr_results,
 500             xdr_location))) {
 501                 int slen;
 502                 struct t_unitdata *tu_data = &(su->su_tudata);
 503 
 504                 slen = (int)XDR_GETPOS(xdrs);
 505                 tu_data->udata.len = slen;
 506                 extract_cred(&su->optbuf, &tu_data->opt);
 507                 set_src_addr(xprt, &tu_data->opt);
 508 try_again:
 509                 if (t_sndudata(xprt->xp_fd, tu_data) == 0) {
 510                         stat = TRUE;
 511                         if (su->su_cache && slen >= 0) {
 512                                 cache_set(xprt, (uint32_t)slen);
 513                         }
 514                 } else {
 515                         if (errno == EINTR)
 516                                 goto try_again;
 517 
 518                         syslog(LOG_ERR,
 519                             "svc_dg_reply: t_sndudata error t_errno=%d ",
 520                             "errno=%d\n", t_errno, errno);
 521                 }
 522                 tu_data->opt.buf = (char *)su->opts;
 523         }
 524         return (stat);
 525 }
 526 
 527 static bool_t
 528 svc_dg_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
 529 {
 530         if (svc_mt_mode != RPC_SVC_MT_NONE)
 531                 svc_args_done(xprt);
 532 /* LINTED pointer alignment */
 533         return (SVCAUTH_UNWRAP(&SVC_XP_AUTH(xprt),
 534             &(get_svc_dg_data(xprt)->su_xdrs), xdr_args, args_ptr));
 535 }
 536 
 537 static bool_t
 538 svc_dg_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
 539 {
 540 /* LINTED pointer alignment */
 541         XDR *xdrs = &(get_svc_dg_data(xprt)->su_xdrs);
 542 
 543         xdrs->x_op = XDR_FREE;
 544         return ((*xdr_args)(xdrs, args_ptr));
 545 }
 546 
 547 static void
 548 svc_dg_destroy(SVCXPRT *xprt)
 549 {
 550         (void) mutex_lock(&svc_mutex);
 551         _svc_dg_destroy_private(xprt);
 552         (void) mutex_unlock(&svc_mutex);
 553 }
 554 
 555 void
 556 _svc_dg_destroy_private(SVCXPRT *xprt)
 557 {
 558         if (svc_mt_mode != RPC_SVC_MT_NONE) {
 559 /* LINTED pointer alignment */
 560                 if (SVCEXT(xprt)->parent)
 561 /* LINTED pointer alignment */
 562                         xprt = SVCEXT(xprt)->parent;
 563 /* LINTED pointer alignment */
 564                 svc_flags(xprt) |= SVC_DEFUNCT;
 565 /* LINTED pointer alignment */
 566                 if (SVCEXT(xprt)->refcnt > 0)
 567                         return;
 568         }
 569 
 570         xprt_unregister(xprt);
 571         (void) t_close(xprt->xp_fd);
 572 
 573         if (svc_mt_mode != RPC_SVC_MT_NONE)
 574                 svc_xprt_destroy(xprt);
 575         else
 576                 svc_dg_xprtfree(xprt);
 577 }
 578 
 579 /*ARGSUSED*/
 580 static bool_t
 581 svc_dg_control(SVCXPRT *xprt, const uint_t rq, void *in)
 582 {
 583         switch (rq) {
 584         case SVCGET_XID:
 585                 if (xprt->xp_p2 == NULL)
 586                         return (FALSE);
 587                 /* LINTED pointer alignment */
 588                 *(uint32_t *)in = ((struct svc_dg_data *)(xprt->xp_p2))->su_xid;
 589                 return (TRUE);
 590         default:
 591                 return (FALSE);
 592         }
 593 }
 594 
 595 static struct xp_ops *
 596 svc_dg_ops(void)
 597 {
 598         static struct xp_ops ops;
 599         extern mutex_t ops_lock;
 600 
 601 /* VARIABLES PROTECTED BY ops_lock: ops */
 602 
 603         (void) mutex_lock(&ops_lock);
 604         if (ops.xp_recv == NULL) {
 605                 ops.xp_recv = svc_dg_recv;
 606                 ops.xp_stat = svc_dg_stat;
 607                 ops.xp_getargs = svc_dg_getargs;
 608                 ops.xp_reply = svc_dg_reply;
 609                 ops.xp_freeargs = svc_dg_freeargs;
 610                 ops.xp_destroy = svc_dg_destroy;
 611                 ops.xp_control = svc_dg_control;
 612         }
 613         (void) mutex_unlock(&ops_lock);
 614         return (&ops);
 615 }
 616 
 617 /*  The CACHING COMPONENT */
 618 
 619 /*
 620  * Could have been a separate file, but some part of it depends upon the
 621  * private structure of the client handle.
 622  *
 623  * Fifo cache for cl server
 624  * Copies pointers to reply buffers into fifo cache
 625  * Buffers are sent again if retransmissions are detected.
 626  */
 627 
 628 #define SPARSENESS 4    /* 75% sparse */
 629 
 630 /*
 631  * An entry in the cache
 632  */
 633 typedef struct cache_node *cache_ptr;
 634 struct cache_node {
 635         /*
 636          * Index into cache is xid, proc, vers, prog and address
 637          */
 638         uint32_t cache_xid;
 639         rpcproc_t cache_proc;
 640         rpcvers_t cache_vers;
 641         rpcprog_t cache_prog;
 642         struct netbuf cache_addr;
 643         /*
 644          * The cached reply and length
 645          */
 646         char *cache_reply;
 647         uint32_t cache_replylen;
 648         /*
 649          * Next node on the list, if there is a collision
 650          */
 651         cache_ptr cache_next;
 652 };
 653 
 654 /*
 655  * The entire cache
 656  */
 657 struct cl_cache {
 658         uint32_t uc_size;               /* size of cache */
 659         cache_ptr *uc_entries;  /* hash table of entries in cache */
 660         cache_ptr *uc_fifo;     /* fifo list of entries in cache */
 661         uint32_t uc_nextvictim; /* points to next victim in fifo list */
 662         rpcprog_t uc_prog;      /* saved program number */
 663         rpcvers_t uc_vers;      /* saved version number */
 664         rpcproc_t uc_proc;      /* saved procedure number */
 665 };
 666 
 667 
 668 /*
 669  * the hashing function
 670  */
 671 #define CACHE_LOC(transp, xid)  \
 672         (xid % (SPARSENESS * ((struct cl_cache *) \
 673                 get_svc_dg_data(transp)->su_cache)->uc_size))
 674 
 675 extern mutex_t  dupreq_lock;
 676 
 677 /*
 678  * Enable use of the cache. Returns 1 on success, 0 on failure.
 679  * Note: there is no disable.
 680  */
 681 static const char cache_enable_str[] = "svc_enablecache: %s %s";
 682 static const char alloc_err[] = "could not allocate cache ";
 683 static const char enable_err[] = "cache already enabled";
 684 
 685 int
 686 svc_dg_enablecache(SVCXPRT *xprt, const uint_t size)
 687 {
 688         SVCXPRT *transp;
 689         struct svc_dg_data *su;
 690         struct cl_cache *uc;
 691 
 692 /* LINTED pointer alignment */
 693         if (svc_mt_mode != RPC_SVC_MT_NONE && SVCEXT(xprt)->parent != NULL)
 694 /* LINTED pointer alignment */
 695                 transp = SVCEXT(xprt)->parent;
 696         else
 697                 transp = xprt;
 698 /* LINTED pointer alignment */
 699         su = get_svc_dg_data(transp);
 700 
 701         (void) mutex_lock(&dupreq_lock);
 702         if (su->su_cache != NULL) {
 703                 (void) syslog(LOG_ERR, cache_enable_str,
 704                     enable_err, " ");
 705                 (void) mutex_unlock(&dupreq_lock);
 706                 return (0);
 707         }
 708         uc = malloc(sizeof (struct cl_cache));
 709         if (uc == NULL) {
 710                 (void) syslog(LOG_ERR, cache_enable_str,
 711                     alloc_err, " ");
 712                 (void) mutex_unlock(&dupreq_lock);
 713                 return (0);
 714         }
 715         uc->uc_size = size;
 716         uc->uc_nextvictim = 0;
 717         uc->uc_entries = calloc(size * SPARSENESS, sizeof (cache_ptr));
 718         if (uc->uc_entries == NULL) {
 719                 (void) syslog(LOG_ERR, cache_enable_str, alloc_err, "data");
 720                 free(uc);
 721                 (void) mutex_unlock(&dupreq_lock);
 722                 return (0);
 723         }
 724         uc->uc_fifo = calloc(size, sizeof (cache_ptr));
 725         if (uc->uc_fifo == NULL) {
 726                 (void) syslog(LOG_ERR, cache_enable_str, alloc_err, "fifo");
 727                 free(uc->uc_entries);
 728                 free(uc);
 729                 (void) mutex_unlock(&dupreq_lock);
 730                 return (0);
 731         }
 732         su->su_cache = (char *)uc;
 733         (void) mutex_unlock(&dupreq_lock);
 734         return (1);
 735 }
 736 
 737 /*
 738  * Set an entry in the cache.  It assumes that the uc entry is set from
 739  * the earlier call to cache_get() for the same procedure.  This will always
 740  * happen because cache_get() is calle by svc_dg_recv and cache_set() is called
 741  * by svc_dg_reply().  All this hoopla because the right RPC parameters are
 742  * not available at svc_dg_reply time.
 743  */
 744 
 745 static const char cache_set_str[] = "cache_set: %s";
 746 static const char cache_set_err1[] = "victim not found";
 747 static const char cache_set_err2[] = "victim alloc failed";
 748 static const char cache_set_err3[] = "could not allocate new rpc buffer";
 749 
 750 static void
 751 cache_set(SVCXPRT *xprt, uint32_t replylen)
 752 {
 753         SVCXPRT *parent;
 754         cache_ptr victim;
 755         cache_ptr *vicp;
 756         struct svc_dg_data *su;
 757         struct cl_cache *uc;
 758         uint_t loc;
 759         char *newbuf, *newbuf2;
 760         int my_mallocs = 0;
 761 #ifdef RPC_CACHE_DEBUG
 762         struct netconfig *nconf;
 763         char *uaddr;
 764 #endif
 765 
 766 /* LINTED pointer alignment */
 767         if (svc_mt_mode != RPC_SVC_MT_NONE && SVCEXT(xprt)->parent != NULL)
 768 /* LINTED pointer alignment */
 769                 parent = SVCEXT(xprt)->parent;
 770         else
 771                 parent = xprt;
 772 /* LINTED pointer alignment */
 773         su = get_svc_dg_data(xprt);
 774 /* LINTED pointer alignment */
 775         uc = (struct cl_cache *)get_svc_dg_data(parent)->su_cache;
 776 
 777         (void) mutex_lock(&dupreq_lock);
 778         /*
 779          * Find space for the new entry, either by
 780          * reusing an old entry, or by mallocing a new one
 781          */
 782         victim = uc->uc_fifo[uc->uc_nextvictim];
 783         if (victim != NULL) {
 784 /* LINTED pointer alignment */
 785                 loc = CACHE_LOC(parent, victim->cache_xid);
 786                 for (vicp = &uc->uc_entries[loc];
 787                     *vicp != NULL && *vicp != victim;
 788                     vicp = &(*vicp)->cache_next)
 789                         ;
 790                 if (*vicp == NULL) {
 791                         (void) syslog(LOG_ERR, cache_set_str, cache_set_err1);
 792                         (void) mutex_unlock(&dupreq_lock);
 793                         return;
 794                 }
 795                 *vicp = victim->cache_next;  /* remove from cache */
 796                 newbuf = victim->cache_reply;
 797         } else {
 798                 victim = malloc(sizeof (struct cache_node));
 799                 if (victim == NULL) {
 800                         (void) syslog(LOG_ERR, cache_set_str, cache_set_err2);
 801                         (void) mutex_unlock(&dupreq_lock);
 802                         return;
 803                 }
 804                 newbuf = malloc(su->su_iosz);
 805                 if (newbuf == NULL) {
 806                         (void) syslog(LOG_ERR, cache_set_str, cache_set_err3);
 807                         free(victim);
 808                         (void) mutex_unlock(&dupreq_lock);
 809                         return;
 810                 }
 811                 my_mallocs = 1;
 812         }
 813 
 814         /*
 815          * Store it away
 816          */
 817 #ifdef RPC_CACHE_DEBUG
 818         if (nconf = getnetconfigent(xprt->xp_netid)) {
 819                 uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr);
 820                 freenetconfigent(nconf);
 821                 printf(
 822         "cache set for xid= %x prog=%d vers=%d proc=%d for rmtaddr=%s\n",
 823                     su->su_xid, uc->uc_prog, uc->uc_vers, uc->uc_proc, uaddr);
 824                 free(uaddr);
 825         }
 826 #endif
 827         newbuf2 = malloc(sizeof (char) * xprt->xp_rtaddr.len);
 828         if (newbuf2 == NULL) {
 829                 syslog(LOG_ERR, "cache_set : out of memory");
 830                 if (my_mallocs) {
 831                         free(victim);
 832                         free(newbuf);
 833                 }
 834                 (void) mutex_unlock(&dupreq_lock);
 835                 return;
 836         }
 837         victim->cache_replylen = replylen;
 838         victim->cache_reply = rpc_buffer(xprt);
 839         rpc_buffer(xprt) = newbuf;
 840         xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz,
 841             XDR_ENCODE);
 842         su->su_tudata.udata.buf = (char *)rpc_buffer(xprt);
 843         victim->cache_xid = su->su_xid;
 844         victim->cache_proc = uc->uc_proc;
 845         victim->cache_vers = uc->uc_vers;
 846         victim->cache_prog = uc->uc_prog;
 847         victim->cache_addr = xprt->xp_rtaddr;
 848         victim->cache_addr.buf = newbuf2;
 849         (void) memcpy(victim->cache_addr.buf, xprt->xp_rtaddr.buf,
 850             (int)xprt->xp_rtaddr.len);
 851 /* LINTED pointer alignment */
 852         loc = CACHE_LOC(parent, victim->cache_xid);
 853         victim->cache_next = uc->uc_entries[loc];
 854         uc->uc_entries[loc] = victim;
 855         uc->uc_fifo[uc->uc_nextvictim++] = victim;
 856         uc->uc_nextvictim %= uc->uc_size;
 857         (void) mutex_unlock(&dupreq_lock);
 858 }
 859 
 860 /*
 861  * Try to get an entry from the cache
 862  * return 1 if found, 0 if not found and set the stage for cache_set()
 863  */
 864 static int
 865 cache_get(SVCXPRT *xprt, struct rpc_msg *msg, char **replyp,
 866                                                         uint32_t *replylenp)
 867 {
 868         SVCXPRT *parent;
 869         uint_t loc;
 870         cache_ptr ent;
 871         struct svc_dg_data *su;
 872         struct cl_cache *uc;
 873 #ifdef RPC_CACHE_DEBUG
 874         struct netconfig *nconf;
 875         char *uaddr;
 876 #endif
 877 
 878 /* LINTED pointer alignment */
 879         if (svc_mt_mode != RPC_SVC_MT_NONE && SVCEXT(xprt)->parent != NULL)
 880 /* LINTED pointer alignment */
 881                 parent = SVCEXT(xprt)->parent;
 882         else
 883                 parent = xprt;
 884 /* LINTED pointer alignment */
 885         su = get_svc_dg_data(xprt);
 886 /* LINTED pointer alignment */
 887         uc = (struct cl_cache *)get_svc_dg_data(parent)->su_cache;
 888 
 889         (void) mutex_lock(&dupreq_lock);
 890 /* LINTED pointer alignment */
 891         loc = CACHE_LOC(parent, su->su_xid);
 892         for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
 893                 if (ent->cache_xid == su->su_xid &&
 894                     ent->cache_proc == msg->rm_call.cb_proc &&
 895                     ent->cache_vers == msg->rm_call.cb_vers &&
 896                     ent->cache_prog == msg->rm_call.cb_prog &&
 897                     ent->cache_addr.len == xprt->xp_rtaddr.len &&
 898                     (memcmp(ent->cache_addr.buf, xprt->xp_rtaddr.buf,
 899                     xprt->xp_rtaddr.len) == 0)) {
 900 #ifdef RPC_CACHE_DEBUG
 901                         if (nconf = getnetconfigent(xprt->xp_netid)) {
 902                                 uaddr = taddr2uaddr(nconf, &xprt->xp_rtaddr);
 903                                 freenetconfigent(nconf);
 904                                 printf(
 905         "cache entry found for xid=%x prog=%d vers=%d proc=%d for rmtaddr=%s\n",
 906                                     su->su_xid, msg->rm_call.cb_prog,
 907                                     msg->rm_call.cb_vers,
 908                                     msg->rm_call.cb_proc, uaddr);
 909                                 free(uaddr);
 910                         }
 911 #endif
 912                         *replyp = ent->cache_reply;
 913                         *replylenp = ent->cache_replylen;
 914                         (void) mutex_unlock(&dupreq_lock);
 915                         return (1);
 916                 }
 917         }
 918         /*
 919          * Failed to find entry
 920          * Remember a few things so we can do a set later
 921          */
 922         uc->uc_proc = msg->rm_call.cb_proc;
 923         uc->uc_vers = msg->rm_call.cb_vers;
 924         uc->uc_prog = msg->rm_call.cb_prog;
 925         (void) mutex_unlock(&dupreq_lock);
 926         return (0);
 927 }