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 (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  25  */
  26 /*
  27  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
  28  */
  29 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
  30 /* All Rights Reserved */
  31 /*
  32  * Portions of this source code were derived from Berkeley
  33  * 4.3 BSD under license from the Regents of the University of
  34  * California.
  35  */
  36 
  37 /*
  38  * svc.c, Server-side remote procedure call interface.
  39  *
  40  * There are two sets of procedures here.  The xprt routines are
  41  * for handling transport handles.  The svc routines handle the
  42  * list of service routines.
  43  *
  44  */
  45 
  46 #include "mt.h"
  47 #include "rpc_mt.h"
  48 #include <assert.h>
  49 #include <errno.h>
  50 #include <sys/types.h>
  51 #include <stropts.h>
  52 #include <sys/conf.h>
  53 #include <rpc/rpc.h>
  54 #ifdef PORTMAP
  55 #include <rpc/pmap_clnt.h>
  56 #endif
  57 #include <sys/poll.h>
  58 #include <netconfig.h>
  59 #include <syslog.h>
  60 #include <stdlib.h>
  61 #include <unistd.h>
  62 #include <string.h>
  63 #include <limits.h>
  64 
  65 extern bool_t __svc_get_door_cred();
  66 extern bool_t __rpc_get_local_cred();
  67 
  68 SVCXPRT **svc_xports;
  69 static int nsvc_xports;         /* total number of svc_xports allocated */
  70 
  71 XDR **svc_xdrs;         /* common XDR receive area */
  72 int nsvc_xdrs;          /* total number of svc_xdrs allocated */
  73 
  74 int __rpc_use_pollfd_done;      /* to unlimit the number of connections */
  75 
  76 #define NULL_SVC ((struct svc_callout *)0)
  77 #define RQCRED_SIZE     400             /* this size is excessive */
  78 
  79 /*
  80  * The services list
  81  * Each entry represents a set of procedures (an rpc program).
  82  * The dispatch routine takes request structs and runs the
  83  * appropriate procedure.
  84  */
  85 static struct svc_callout {
  86         struct svc_callout *sc_next;
  87         rpcprog_t           sc_prog;
  88         rpcvers_t           sc_vers;
  89         char               *sc_netid;
  90         void                (*sc_dispatch)();
  91 } *svc_head;
  92 extern rwlock_t svc_lock;
  93 
  94 static struct svc_callout *svc_find();
  95 int _svc_prog_dispatch();
  96 void svc_getreq_common();
  97 char *strdup();
  98 
  99 extern mutex_t  svc_door_mutex;
 100 extern cond_t   svc_door_waitcv;
 101 extern int      svc_ndoorfds;
 102 extern SVCXPRT_LIST *_svc_xprtlist;
 103 extern mutex_t xprtlist_lock;
 104 extern void __svc_rm_from_xlist();
 105 
 106 #if !defined(_LP64)
 107 extern fd_set _new_svc_fdset;
 108 #endif
 109 
 110 /*
 111  * If the allocated array of reactor is too small, this value is used as a
 112  * margin. This reduces the number of allocations.
 113  */
 114 #define USER_FD_INCREMENT 5
 115 
 116 static void add_pollfd(int fd, short events);
 117 static void remove_pollfd(int fd);
 118 static void __svc_remove_input_of_fd(int fd);
 119 
 120 /*
 121  * Data used to handle reactor:
 122  *      - one file descriptor we listen to,
 123  *      - one callback we call if the fd pops,
 124  *      - and a cookie passed as a parameter to the callback.
 125  *
 126  * The structure is an array indexed on the file descriptor. Each entry is
 127  * pointing to the first element of a double-linked list of callback.
 128  * only one callback may be associated to a couple (fd, event).
 129  */
 130 
 131 struct _svc_user_fd_head;
 132 
 133 typedef struct {
 134         struct _svc_user_fd_node *next;
 135         struct _svc_user_fd_node *previous;
 136 } _svc_user_link;
 137 
 138 typedef struct _svc_user_fd_node {
 139         _svc_user_link lnk;
 140         svc_input_id_t id;
 141         int         fd;
 142         unsigned int   events;
 143         svc_callback_t callback;
 144         void*     cookie;
 145 } _svc_user_fd_node;
 146 
 147 typedef struct _svc_user_fd_head {
 148         struct _svc_user_fd_node *list;
 149         unsigned int mask;    /* logical OR of all sub-masks */
 150 } _svc_user_fd_head;
 151 
 152 
 153 /* Array of defined reactor - indexed on file descriptor */
 154 static _svc_user_fd_head *svc_userfds = NULL;
 155 
 156 /* current size of file descriptor */
 157 static int svc_nuserfds = 0;
 158 
 159 /* Mutex to ensure MT safe operations for user fds callbacks. */
 160 static mutex_t svc_userfds_lock = DEFAULTMUTEX;
 161 
 162 
 163 /*
 164  * This structure is used to have constant time alogrithms. There is an array
 165  * of this structure as large as svc_nuserfds. When the user is registering a
 166  * new callback, the address of the created structure is stored in a cell of
 167  * this array. The address of this cell is the returned unique identifier.
 168  *
 169  * On removing, the id is given by the user, then we know if this cell is
 170  * filled or not (with free). If it is free, we return an error. Otherwise,
 171  * we can free the structure pointed by fd_node.
 172  *
 173  * On insertion, we use the linked list created by (first_free,
 174  * next_free). In this way with a constant time computation, we can give a
 175  * correct index to the user.
 176  */
 177 
 178 typedef struct _svc_management_user_fd {
 179         bool_t free;
 180         union {
 181                 svc_input_id_t next_free;
 182                 _svc_user_fd_node *fd_node;
 183         } data;
 184 } _svc_management_user_fd;
 185 
 186 /* index to the first free elem */
 187 static svc_input_id_t first_free = (svc_input_id_t)-1;
 188 /* the size of this array is the same as svc_nuserfds */
 189 static _svc_management_user_fd* user_fd_mgt_array = NULL;
 190 
 191 /* current size of user_fd_mgt_array */
 192 static int svc_nmgtuserfds = 0;
 193 
 194 
 195 /* Define some macros to access data associated to registration ids. */
 196 #define node_from_id(id) (user_fd_mgt_array[(int)id].data.fd_node)
 197 #define is_free_id(id) (user_fd_mgt_array[(int)id].free)
 198 
 199 #ifndef POLLSTANDARD
 200 #define POLLSTANDARD \
 201         (POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND| \
 202         POLLWRBAND|POLLERR|POLLHUP|POLLNVAL)
 203 #endif
 204 
 205 /*
 206  * To free an Id, we set the cell as free and insert its address in the list
 207  * of free cell.
 208  */
 209 
 210 static void
 211 _svc_free_id(const svc_input_id_t id)
 212 {
 213         assert(((int)id >= 0) && ((int)id < svc_nmgtuserfds));
 214         user_fd_mgt_array[(int)id].free = TRUE;
 215         user_fd_mgt_array[(int)id].data.next_free = first_free;
 216         first_free = id;
 217 }
 218 
 219 /*
 220  * To get a free cell, we just have to take it from the free linked list and
 221  * set the flag to "not free". This function also allocates new memory if
 222  * necessary
 223  */
 224 static svc_input_id_t
 225 _svc_attribute_new_id(_svc_user_fd_node *node)
 226 {
 227         int selected_index = (int)first_free;
 228         assert(node != NULL);
 229 
 230         if (selected_index == -1) {
 231                 /* Allocate new entries */
 232                 int L_inOldSize = svc_nmgtuserfds;
 233                 int i;
 234                 _svc_management_user_fd *tmp;
 235 
 236                 svc_nmgtuserfds += USER_FD_INCREMENT;
 237 
 238                 tmp = realloc(user_fd_mgt_array,
 239                     svc_nmgtuserfds * sizeof (_svc_management_user_fd));
 240 
 241                 if (tmp == NULL) {
 242                         syslog(LOG_ERR, "_svc_attribute_new_id: out of memory");
 243                         svc_nmgtuserfds = L_inOldSize;
 244                         errno = ENOMEM;
 245                         return ((svc_input_id_t)-1);
 246                 }
 247 
 248                 user_fd_mgt_array = tmp;
 249 
 250                 for (i = svc_nmgtuserfds - 1; i >= L_inOldSize; i--)
 251                         _svc_free_id((svc_input_id_t)i);
 252                 selected_index = (int)first_free;
 253         }
 254 
 255         node->id = (svc_input_id_t)selected_index;
 256         first_free = user_fd_mgt_array[selected_index].data.next_free;
 257 
 258         user_fd_mgt_array[selected_index].data.fd_node = node;
 259         user_fd_mgt_array[selected_index].free = FALSE;
 260 
 261         return ((svc_input_id_t)selected_index);
 262 }
 263 
 264 /*
 265  * Access to a pollfd treatment. Scan all the associated callbacks that have
 266  * at least one bit in their mask that masks a received event.
 267  *
 268  * If event POLLNVAL is received, we check that one callback processes it, if
 269  * not, then remove the file descriptor from the poll. If there is one, let
 270  * the user do the work.
 271  */
 272 void
 273 __svc_getreq_user(struct pollfd *pfd)
 274 {
 275         int fd = pfd->fd;
 276         short revents = pfd->revents;
 277         bool_t invalHandled = FALSE;
 278         _svc_user_fd_node *node;
 279 
 280         (void) mutex_lock(&svc_userfds_lock);
 281 
 282         if ((fd < 0) || (fd >= svc_nuserfds)) {
 283                 (void) mutex_unlock(&svc_userfds_lock);
 284                 return;
 285         }
 286 
 287         node = svc_userfds[fd].list;
 288 
 289         /* check if at least one mask fits */
 290         if (0 == (revents & svc_userfds[fd].mask)) {
 291                 (void) mutex_unlock(&svc_userfds_lock);
 292                 return;
 293         }
 294 
 295         while ((svc_userfds[fd].mask != 0) && (node != NULL)) {
 296                 /*
 297                  * If one of the received events maps the ones the node listens
 298                  * to
 299                  */
 300                 _svc_user_fd_node *next = node->lnk.next;
 301 
 302                 if (node->callback != NULL) {
 303                         if (node->events & revents) {
 304                                 if (revents & POLLNVAL) {
 305                                         invalHandled = TRUE;
 306                                 }
 307 
 308                                 /*
 309                                  * The lock must be released before calling the
 310                                  * user function, as this function can call
 311                                  * svc_remove_input() for example.
 312                                  */
 313                                 (void) mutex_unlock(&svc_userfds_lock);
 314                                 node->callback(node->id, node->fd,
 315                                     node->events & revents, node->cookie);
 316                                 /*
 317                                  * Do not use the node structure anymore, as it
 318                                  * could have been deallocated by the previous
 319                                  * callback.
 320                                  */
 321                                 (void) mutex_lock(&svc_userfds_lock);
 322                         }
 323                 }
 324                 node = next;
 325         }
 326 
 327         if ((revents & POLLNVAL) && !invalHandled)
 328                 __svc_remove_input_of_fd(fd);
 329         (void) mutex_unlock(&svc_userfds_lock);
 330 }
 331 
 332 
 333 /*
 334  * Check if a file descriptor is associated with a user reactor.
 335  * To do this, just check that the array indexed on fd has a non-void linked
 336  * list (ie. first element is not NULL)
 337  */
 338 bool_t
 339 __is_a_userfd(int fd)
 340 {
 341         /* Checks argument */
 342         if ((fd < 0) || (fd >= svc_nuserfds))
 343                 return (FALSE);
 344         return ((svc_userfds[fd].mask == 0x0000)? FALSE:TRUE);
 345 }
 346 
 347 /* free everything concerning user fd */
 348 /* used in svc_run.c => no static */
 349 
 350 void
 351 __destroy_userfd(void)
 352 {
 353         int one_fd;
 354         /* Clean user fd */
 355         if (svc_userfds != NULL) {
 356                 for (one_fd = 0; one_fd < svc_nuserfds; one_fd++) {
 357                         _svc_user_fd_node *node;
 358 
 359                         node = svc_userfds[one_fd].list;
 360                         while (node != NULL) {
 361                                 _svc_user_fd_node *tmp = node;
 362                                 _svc_free_id(node->id);
 363                                 node = node->lnk.next;
 364                                 free(tmp);
 365                         }
 366                 }
 367 
 368                 free(user_fd_mgt_array);
 369                 user_fd_mgt_array = NULL;
 370                 first_free = (svc_input_id_t)-1;
 371 
 372                 free(svc_userfds);
 373                 svc_userfds = NULL;
 374                 svc_nuserfds = 0;
 375         }
 376 }
 377 
 378 /*
 379  * Remove all the callback associated with a fd => useful when the fd is
 380  * closed for instance
 381  */
 382 static void
 383 __svc_remove_input_of_fd(int fd)
 384 {
 385         _svc_user_fd_node **pnode;
 386         _svc_user_fd_node *tmp;
 387 
 388         if ((fd < 0) || (fd >= svc_nuserfds))
 389                 return;
 390 
 391         pnode = &svc_userfds[fd].list;
 392         while ((tmp = *pnode) != NULL) {
 393                 *pnode = tmp->lnk.next;
 394 
 395                 _svc_free_id(tmp->id);
 396                 free(tmp);
 397         }
 398 
 399         svc_userfds[fd].mask = 0;
 400 }
 401 
 402 /*
 403  * Allow user to add an fd in the poll list. If it does not succeed, return
 404  * -1. Otherwise, return a svc_id
 405  */
 406 
 407 svc_input_id_t
 408 svc_add_input(int user_fd, unsigned int events,
 409     svc_callback_t user_callback, void *cookie)
 410 {
 411         _svc_user_fd_node *new_node;
 412 
 413         if (user_fd < 0) {
 414                 errno = EINVAL;
 415                 return ((svc_input_id_t)-1);
 416         }
 417 
 418         if ((events == 0x0000) ||
 419             (events & ~(POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND|\
 420             POLLWRBAND|POLLERR|POLLHUP|POLLNVAL))) {
 421                 errno = EINVAL;
 422                 return ((svc_input_id_t)-1);
 423         }
 424 
 425         (void) mutex_lock(&svc_userfds_lock);
 426 
 427         if ((user_fd < svc_nuserfds) &&
 428             (svc_userfds[user_fd].mask & events) != 0) {
 429                 /* Already registrated call-back */
 430                 errno = EEXIST;
 431                 (void) mutex_unlock(&svc_userfds_lock);
 432                 return ((svc_input_id_t)-1);
 433         }
 434 
 435         /* Handle memory allocation. */
 436         if (user_fd >= svc_nuserfds) {
 437                 int oldSize = svc_nuserfds;
 438                 int i;
 439                 _svc_user_fd_head *tmp;
 440 
 441                 svc_nuserfds = (user_fd + 1) + USER_FD_INCREMENT;
 442 
 443                 tmp = realloc(svc_userfds,
 444                     svc_nuserfds * sizeof (_svc_user_fd_head));
 445 
 446                 if (tmp == NULL) {
 447                         syslog(LOG_ERR, "svc_add_input: out of memory");
 448                         svc_nuserfds = oldSize;
 449                         errno = ENOMEM;
 450                         (void) mutex_unlock(&svc_userfds_lock);
 451                         return ((svc_input_id_t)-1);
 452                 }
 453 
 454                 svc_userfds = tmp;
 455 
 456                 for (i = oldSize; i < svc_nuserfds; i++) {
 457                         svc_userfds[i].list = NULL;
 458                         svc_userfds[i].mask = 0;
 459                 }
 460         }
 461 
 462         new_node = malloc(sizeof (_svc_user_fd_node));
 463         if (new_node == NULL) {
 464                 syslog(LOG_ERR, "svc_add_input: out of memory");
 465                 errno = ENOMEM;
 466                 (void) mutex_unlock(&svc_userfds_lock);
 467                 return ((svc_input_id_t)-1);
 468         }
 469 
 470         /* create a new node */
 471         new_node->fd         = user_fd;
 472         new_node->events     = events;
 473         new_node->callback   = user_callback;
 474         new_node->cookie     = cookie;
 475 
 476         if (_svc_attribute_new_id(new_node) == -1) {
 477                 (void) mutex_unlock(&svc_userfds_lock);
 478                 free(new_node);
 479                 return ((svc_input_id_t)-1);
 480         }
 481 
 482         /* Add the new element at the beginning of the list. */
 483         if (svc_userfds[user_fd].list != NULL)
 484                 svc_userfds[user_fd].list->lnk.previous = new_node;
 485         new_node->lnk.next = svc_userfds[user_fd].list;
 486         new_node->lnk.previous = NULL;
 487 
 488         svc_userfds[user_fd].list = new_node;
 489 
 490         /* refresh global mask for this file desciptor */
 491         svc_userfds[user_fd].mask |= events;
 492 
 493         /* refresh mask for the poll */
 494         add_pollfd(user_fd, (svc_userfds[user_fd].mask));
 495 
 496         (void) mutex_unlock(&svc_userfds_lock);
 497         return (new_node->id);
 498 }
 499 
 500 int
 501 svc_remove_input(svc_input_id_t id)
 502 {
 503         _svc_user_fd_node* node;
 504         _svc_user_fd_node* next;
 505         _svc_user_fd_node* previous;
 506         int fd;         /* caching optim */
 507 
 508         (void) mutex_lock(&svc_userfds_lock);
 509 
 510         /* Immediately update data for id management */
 511         if (user_fd_mgt_array == NULL || id >= svc_nmgtuserfds ||
 512             is_free_id(id)) {
 513                 errno = EINVAL;
 514                 (void) mutex_unlock(&svc_userfds_lock);
 515                 return (-1);
 516         }
 517 
 518         node = node_from_id(id);
 519         assert(node != NULL);
 520 
 521         _svc_free_id(id);
 522         next            = node->lnk.next;
 523         previous        = node->lnk.previous;
 524         fd              = node->fd; /* caching optim */
 525 
 526         /* Remove this node from the list. */
 527         if (previous != NULL) {
 528                 previous->lnk.next = next;
 529         } else {
 530                 assert(svc_userfds[fd].list == node);
 531                 svc_userfds[fd].list = next;
 532         }
 533         if (next != NULL)
 534                 next->lnk.previous = previous;
 535 
 536         /* Remove the node flags from the global mask */
 537         svc_userfds[fd].mask ^= node->events;
 538 
 539         free(node);
 540         if (svc_userfds[fd].mask == 0) {
 541                 assert(svc_userfds[fd].list == NULL);
 542                 remove_pollfd(fd);
 543         } else {
 544                 assert(svc_userfds[fd].list != NULL);
 545         }
 546         /* <=> CLEAN NEEDED TO SHRINK MEMORY USAGE */
 547 
 548         (void) mutex_unlock(&svc_userfds_lock);
 549         return (0);
 550 }
 551 
 552 /*
 553  * Provides default service-side functions for authentication flavors
 554  * that do not use all the fields in struct svc_auth_ops.
 555  */
 556 
 557 /*ARGSUSED*/
 558 static int
 559 authany_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xfunc, caddr_t xwhere)
 560 {
 561         return (*xfunc)(xdrs, xwhere);
 562 }
 563 
 564 struct svc_auth_ops svc_auth_any_ops = {
 565         authany_wrap,
 566         authany_wrap,
 567 };
 568 
 569 /*
 570  * Return pointer to server authentication structure.
 571  */
 572 SVCAUTH *
 573 __svc_get_svcauth(SVCXPRT *xprt)
 574 {
 575 /* LINTED pointer alignment */
 576         return (&SVC_XP_AUTH(xprt));
 577 }
 578 
 579 /*
 580  * A callback routine to cleanup after a procedure is executed.
 581  */
 582 void (*__proc_cleanup_cb)() = NULL;
 583 
 584 void *
 585 __svc_set_proc_cleanup_cb(void *cb)
 586 {
 587         void    *tmp = (void *)__proc_cleanup_cb;
 588 
 589         __proc_cleanup_cb = (void (*)())cb;
 590         return (tmp);
 591 }
 592 
 593 /* ***************  SVCXPRT related stuff **************** */
 594 
 595 
 596 static int pollfd_shrinking = 1;
 597 
 598 
 599 /*
 600  * Add fd to svc_pollfd
 601  */
 602 static void
 603 add_pollfd(int fd, short events)
 604 {
 605         if (fd < FD_SETSIZE) {
 606                 FD_SET(fd, &svc_fdset);
 607 #if !defined(_LP64)
 608                 FD_SET(fd, &_new_svc_fdset);
 609 #endif
 610                 svc_nfds++;
 611                 svc_nfds_set++;
 612                 if (fd >= svc_max_fd)
 613                         svc_max_fd = fd + 1;
 614         }
 615         if (fd >= svc_max_pollfd)
 616                 svc_max_pollfd = fd + 1;
 617         if (svc_max_pollfd > svc_pollfd_allocd) {
 618                 int i = svc_pollfd_allocd;
 619                 pollfd_t *tmp;
 620                 do {
 621                         svc_pollfd_allocd += POLLFD_EXTEND;
 622                 } while (svc_max_pollfd > svc_pollfd_allocd);
 623                 tmp = realloc(svc_pollfd,
 624                     sizeof (pollfd_t) * svc_pollfd_allocd);
 625                 if (tmp != NULL) {
 626                         svc_pollfd = tmp;
 627                         for (; i < svc_pollfd_allocd; i++)
 628                                 POLLFD_CLR(i, tmp);
 629                 } else {
 630                         /*
 631                          * give an error message; undo fdset setting
 632                          * above;  reset the pollfd_shrinking flag.
 633                          * because of this poll will not be done
 634                          * on these fds.
 635                          */
 636                         if (fd < FD_SETSIZE) {
 637                                 FD_CLR(fd, &svc_fdset);
 638 #if !defined(_LP64)
 639                                 FD_CLR(fd, &_new_svc_fdset);
 640 #endif
 641                                 svc_nfds--;
 642                                 svc_nfds_set--;
 643                                 if (fd == (svc_max_fd - 1))
 644                                         svc_max_fd--;
 645                         }
 646                         if (fd == (svc_max_pollfd - 1))
 647                                 svc_max_pollfd--;
 648                         pollfd_shrinking = 0;
 649                         syslog(LOG_ERR, "add_pollfd: out of memory");
 650                         _exit(1);
 651                 }
 652         }
 653         svc_pollfd[fd].fd       = fd;
 654         svc_pollfd[fd].events   = events;
 655         svc_npollfds++;
 656         svc_npollfds_set++;
 657 }
 658 
 659 /*
 660  * the fd is still active but only the bit in fdset is cleared.
 661  * do not subtract svc_nfds or svc_npollfds
 662  */
 663 void
 664 clear_pollfd(int fd)
 665 {
 666         if (fd < FD_SETSIZE && FD_ISSET(fd, &svc_fdset)) {
 667                 FD_CLR(fd, &svc_fdset);
 668 #if !defined(_LP64)
 669                 FD_CLR(fd, &_new_svc_fdset);
 670 #endif
 671                 svc_nfds_set--;
 672         }
 673         if (fd < svc_pollfd_allocd && POLLFD_ISSET(fd, svc_pollfd)) {
 674                 POLLFD_CLR(fd, svc_pollfd);
 675                 svc_npollfds_set--;
 676         }
 677 }
 678 
 679 /*
 680  * sets the bit in fdset for an active fd so that poll() is done for that
 681  */
 682 void
 683 set_pollfd(int fd, short events)
 684 {
 685         if (fd < FD_SETSIZE) {
 686                 FD_SET(fd, &svc_fdset);
 687 #if !defined(_LP64)
 688                 FD_SET(fd, &_new_svc_fdset);
 689 #endif
 690                 svc_nfds_set++;
 691         }
 692         if (fd < svc_pollfd_allocd) {
 693                 svc_pollfd[fd].fd       = fd;
 694                 svc_pollfd[fd].events   = events;
 695                 svc_npollfds_set++;
 696         }
 697 }
 698 
 699 /*
 700  * remove a svc_pollfd entry; it does not shrink the memory
 701  */
 702 static void
 703 remove_pollfd(int fd)
 704 {
 705         clear_pollfd(fd);
 706         if (fd == (svc_max_fd - 1))
 707                 svc_max_fd--;
 708         svc_nfds--;
 709         if (fd == (svc_max_pollfd - 1))
 710                 svc_max_pollfd--;
 711         svc_npollfds--;
 712 }
 713 
 714 /*
 715  * delete a svc_pollfd entry; it shrinks the memory
 716  * use remove_pollfd if you do not want to shrink
 717  */
 718 static void
 719 delete_pollfd(int fd)
 720 {
 721         remove_pollfd(fd);
 722         if (pollfd_shrinking && svc_max_pollfd <
 723             (svc_pollfd_allocd - POLLFD_SHRINK)) {
 724                 do {
 725                         svc_pollfd_allocd -= POLLFD_SHRINK;
 726                 } while (svc_max_pollfd < (svc_pollfd_allocd - POLLFD_SHRINK));
 727                 svc_pollfd = realloc(svc_pollfd,
 728                     sizeof (pollfd_t) * svc_pollfd_allocd);
 729                 if (svc_pollfd == NULL) {
 730                         syslog(LOG_ERR, "delete_pollfd: out of memory");
 731                         _exit(1);
 732                 }
 733         }
 734 }
 735 
 736 
 737 /*
 738  * Activate a transport handle.
 739  */
 740 void
 741 xprt_register(const SVCXPRT *xprt)
 742 {
 743         int fd = xprt->xp_fd;
 744 #ifdef CALLBACK
 745         extern void (*_svc_getreqset_proc)();
 746 #endif
 747 /* VARIABLES PROTECTED BY svc_fd_lock: svc_xports, svc_fdset */
 748 
 749         (void) rw_wrlock(&svc_fd_lock);
 750         if (svc_xports == NULL) {
 751                 /* allocate some small amount first */
 752                 svc_xports = calloc(FD_INCREMENT,  sizeof (SVCXPRT *));
 753                 if (svc_xports == NULL) {
 754                         syslog(LOG_ERR, "xprt_register: out of memory");
 755                         _exit(1);
 756                 }
 757                 nsvc_xports = FD_INCREMENT;
 758 
 759 #ifdef CALLBACK
 760                 /*
 761                  * XXX: This code does not keep track of the server state.
 762                  *
 763                  * This provides for callback support.  When a client
 764                  * recv's a call from another client on the server fd's,
 765                  * it calls _svc_getreqset_proc() which would return
 766                  * after serving all the server requests.  Also look under
 767                  * clnt_dg.c and clnt_vc.c  (clnt_call part of it)
 768                  */
 769                 _svc_getreqset_proc = svc_getreq_poll;
 770 #endif
 771         }
 772 
 773         while (fd >= nsvc_xports) {
 774                 SVCXPRT **tmp_xprts = svc_xports;
 775 
 776                 /* time to expand svc_xprts */
 777                 tmp_xprts = realloc(svc_xports,
 778                     sizeof (SVCXPRT *) * (nsvc_xports + FD_INCREMENT));
 779                 if (tmp_xprts == NULL) {
 780                         syslog(LOG_ERR, "xprt_register : out of memory.");
 781                         _exit(1);
 782                 }
 783 
 784                 svc_xports = tmp_xprts;
 785                 (void) memset(&svc_xports[nsvc_xports], 0,
 786                     sizeof (SVCXPRT *) * FD_INCREMENT);
 787                 nsvc_xports += FD_INCREMENT;
 788         }
 789 
 790         svc_xports[fd] = (SVCXPRT *)xprt;
 791 
 792         add_pollfd(fd, MASKVAL);
 793 
 794         if (svc_polling) {
 795                 char dummy;
 796 
 797                 /*
 798                  * This happens only in one of the MT modes.
 799                  * Wake up poller.
 800                  */
 801                 (void) write(svc_pipe[1], &dummy, sizeof (dummy));
 802         }
 803         /*
 804          * If already dispatching door based services, start
 805          * dispatching TLI based services now.
 806          */
 807         (void) mutex_lock(&svc_door_mutex);
 808         if (svc_ndoorfds > 0)
 809                 (void) cond_signal(&svc_door_waitcv);
 810         (void) mutex_unlock(&svc_door_mutex);
 811 
 812         if (svc_xdrs == NULL) {
 813                 /* allocate initial chunk */
 814                 svc_xdrs = calloc(FD_INCREMENT, sizeof (XDR *));
 815                 if (svc_xdrs != NULL)
 816                         nsvc_xdrs = FD_INCREMENT;
 817                 else {
 818                         syslog(LOG_ERR, "xprt_register : out of memory.");
 819                         _exit(1);
 820                 }
 821         }
 822         (void) rw_unlock(&svc_fd_lock);
 823 }
 824 
 825 /*
 826  * De-activate a transport handle.
 827  */
 828 void
 829 __xprt_unregister_private(const SVCXPRT *xprt, bool_t lock_not_held)
 830 {
 831         int fd = xprt->xp_fd;
 832 
 833         if (lock_not_held)
 834                 (void) rw_wrlock(&svc_fd_lock);
 835         if ((fd < nsvc_xports) && (svc_xports[fd] == xprt)) {
 836                 svc_xports[fd] = NULL;
 837                 delete_pollfd(fd);
 838         }
 839         if (lock_not_held)
 840                 (void) rw_unlock(&svc_fd_lock);
 841         __svc_rm_from_xlist(&_svc_xprtlist, xprt, &xprtlist_lock);
 842 }
 843 
 844 void
 845 xprt_unregister(const SVCXPRT *xprt)
 846 {
 847         __xprt_unregister_private(xprt, TRUE);
 848 }
 849 
 850 /* ********************** CALLOUT list related stuff ************* */
 851 
 852 /*
 853  * Add a service program to the callout list.
 854  * The dispatch routine will be called when a rpc request for this
 855  * program number comes in.
 856  */
 857 bool_t
 858 svc_reg(const SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers,
 859     void (*dispatch)(), const struct netconfig *nconf)
 860 {
 861         struct svc_callout *prev;
 862         struct svc_callout *s, **s2;
 863         struct netconfig *tnconf;
 864         char *netid = NULL;
 865         int flag = 0;
 866 
 867 /* VARIABLES PROTECTED BY svc_lock: s, prev, svc_head */
 868 
 869         if (xprt->xp_netid) {
 870                 netid = strdup(xprt->xp_netid);
 871                 flag = 1;
 872         } else if (nconf && nconf->nc_netid) {
 873                 netid = strdup(nconf->nc_netid);
 874                 flag = 1;
 875         } else if ((tnconf = __rpcfd_to_nconf(xprt->xp_fd, xprt->xp_type))
 876             != NULL) {
 877                 netid = strdup(tnconf->nc_netid);
 878                 flag = 1;
 879                 freenetconfigent(tnconf);
 880         } /* must have been created with svc_raw_create */
 881         if ((netid == NULL) && (flag == 1))
 882                 return (FALSE);
 883 
 884         (void) rw_wrlock(&svc_lock);
 885         if ((s = svc_find(prog, vers, &prev, netid)) != NULL_SVC) {
 886                 if (netid)
 887                         free(netid);
 888                 if (s->sc_dispatch == dispatch)
 889                         goto rpcb_it; /* he is registering another xptr */
 890                 (void) rw_unlock(&svc_lock);
 891                 return (FALSE);
 892         }
 893         s = malloc(sizeof (struct svc_callout));
 894         if (s == NULL) {
 895                 if (netid)
 896                         free(netid);
 897                 (void) rw_unlock(&svc_lock);
 898                 return (FALSE);
 899         }
 900 
 901         s->sc_prog = prog;
 902         s->sc_vers = vers;
 903         s->sc_dispatch = dispatch;
 904         s->sc_netid = netid;
 905         s->sc_next = NULL;
 906 
 907         /*
 908          * The ordering of transports is such that the most frequently used
 909          * one appears first.  So add the new entry to the end of the list.
 910          */
 911         for (s2 = &svc_head; *s2 != NULL; s2 = &(*s2)->sc_next)
 912                 ;
 913         *s2 = s;
 914 
 915         if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
 916                 if ((((SVCXPRT *)xprt)->xp_netid = strdup(netid)) == NULL) {
 917                         syslog(LOG_ERR, "svc_reg : strdup failed.");
 918                         free(netid);
 919                         free(s);
 920                         *s2 = NULL;
 921                         (void) rw_unlock(&svc_lock);
 922                         return (FALSE);
 923                 }
 924 
 925 rpcb_it:
 926         (void) rw_unlock(&svc_lock);
 927 
 928         /* now register the information with the local binder service */
 929         if (nconf)
 930                 return (rpcb_set(prog, vers, nconf, &xprt->xp_ltaddr));
 931         return (TRUE);
 932         /*NOTREACHED*/
 933 }
 934 
 935 /*
 936  * Remove a service program from the callout list.
 937  */
 938 void
 939 svc_unreg(const rpcprog_t prog, const rpcvers_t vers)
 940 {
 941         struct svc_callout *prev;
 942         struct svc_callout *s;
 943 
 944         /* unregister the information anyway */
 945         (void) rpcb_unset(prog, vers, NULL);
 946 
 947         (void) rw_wrlock(&svc_lock);
 948         while ((s = svc_find(prog, vers, &prev, NULL)) != NULL_SVC) {
 949                 if (prev == NULL_SVC) {
 950                         svc_head = s->sc_next;
 951                 } else {
 952                         prev->sc_next = s->sc_next;
 953                 }
 954                 s->sc_next = NULL_SVC;
 955                 if (s->sc_netid)
 956                         free(s->sc_netid);
 957                 free(s);
 958         }
 959         (void) rw_unlock(&svc_lock);
 960 }
 961 
 962 #ifdef PORTMAP
 963 /*
 964  * Add a service program to the callout list.
 965  * The dispatch routine will be called when a rpc request for this
 966  * program number comes in.
 967  * For version 2 portmappers.
 968  */
 969 bool_t
 970 svc_register(SVCXPRT *xprt, rpcprog_t prog, rpcvers_t vers,
 971     void (*dispatch)(), int protocol)
 972 {
 973         struct svc_callout *prev;
 974         struct svc_callout *s;
 975         struct netconfig *nconf;
 976         char *netid = NULL;
 977         int flag = 0;
 978 
 979         if (xprt->xp_netid) {
 980                 netid = strdup(xprt->xp_netid);
 981                 flag = 1;
 982         } else if ((ioctl(xprt->xp_fd, I_FIND, "timod") > 0) && ((nconf =
 983             __rpcfd_to_nconf(xprt->xp_fd, xprt->xp_type)) != NULL)) {
 984                 /* fill in missing netid field in SVCXPRT */
 985                 netid = strdup(nconf->nc_netid);
 986                 flag = 1;
 987                 freenetconfigent(nconf);
 988         } /* must be svc_raw_create */
 989 
 990         if ((netid == NULL) && (flag == 1))
 991                 return (FALSE);
 992 
 993         (void) rw_wrlock(&svc_lock);
 994         if ((s = svc_find(prog, vers, &prev, netid)) != NULL_SVC) {
 995                 if (netid)
 996                         free(netid);
 997                 if (s->sc_dispatch == dispatch)
 998                         goto pmap_it;  /* he is registering another xptr */
 999                 (void) rw_unlock(&svc_lock);
1000                 return (FALSE);
1001         }
1002         s = malloc(sizeof (struct svc_callout));
1003         if (s == (struct svc_callout *)0) {
1004                 if (netid)
1005                         free(netid);
1006                 (void) rw_unlock(&svc_lock);
1007                 return (FALSE);
1008         }
1009         s->sc_prog = prog;
1010         s->sc_vers = vers;
1011         s->sc_dispatch = dispatch;
1012         s->sc_netid = netid;
1013         s->sc_next = svc_head;
1014         svc_head = s;
1015 
1016         if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
1017                 if ((xprt->xp_netid = strdup(netid)) == NULL) {
1018                         syslog(LOG_ERR, "svc_register : strdup failed.");
1019                         free(netid);
1020                         svc_head = s->sc_next;
1021                         free(s);
1022                         (void) rw_unlock(&svc_lock);
1023                         return (FALSE);
1024                 }
1025 
1026 pmap_it:
1027         (void) rw_unlock(&svc_lock);
1028         /* now register the information with the local binder service */
1029         if (protocol)
1030                 return (pmap_set(prog, vers, protocol, xprt->xp_port));
1031         return (TRUE);
1032 }
1033 
1034 /*
1035  * Remove a service program from the callout list.
1036  * For version 2 portmappers.
1037  */
1038 void
1039 svc_unregister(rpcprog_t prog, rpcvers_t vers)
1040 {
1041         struct svc_callout *prev;
1042         struct svc_callout *s;
1043 
1044         (void) rw_wrlock(&svc_lock);
1045         while ((s = svc_find(prog, vers, &prev, NULL)) != NULL_SVC) {
1046                 if (prev == NULL_SVC) {
1047                         svc_head = s->sc_next;
1048                 } else {
1049                         prev->sc_next = s->sc_next;
1050                 }
1051                 s->sc_next = NULL_SVC;
1052                 if (s->sc_netid)
1053                         free(s->sc_netid);
1054                 free(s);
1055                 /* unregister the information with the local binder service */
1056                 (void) pmap_unset(prog, vers);
1057         }
1058         (void) rw_unlock(&svc_lock);
1059 }
1060 #endif /* PORTMAP */
1061 
1062 /*
1063  * Search the callout list for a program number, return the callout
1064  * struct.
1065  * Also check for transport as well.  Many routines such as svc_unreg
1066  * dont give any corresponding transport, so dont check for transport if
1067  * netid == NULL
1068  */
1069 static struct svc_callout *
1070 svc_find(rpcprog_t prog, rpcvers_t vers, struct svc_callout **prev, char *netid)
1071 {
1072         struct svc_callout *s, *p;
1073 
1074 /* WRITE LOCK HELD ON ENTRY: svc_lock */
1075 
1076 /*      assert(RW_WRITE_HELD(&svc_lock)); */
1077         p = NULL_SVC;
1078         for (s = svc_head; s != NULL_SVC; s = s->sc_next) {
1079                 if (((s->sc_prog == prog) && (s->sc_vers == vers)) &&
1080                     ((netid == NULL) || (s->sc_netid == NULL) ||
1081                     (strcmp(netid, s->sc_netid) == 0)))
1082                         break;
1083                 p = s;
1084         }
1085         *prev = p;
1086         return (s);
1087 }
1088 
1089 
1090 /* ******************* REPLY GENERATION ROUTINES  ************ */
1091 
1092 /*
1093  * Send a reply to an rpc request
1094  */
1095 bool_t
1096 svc_sendreply(const SVCXPRT *xprt, const xdrproc_t xdr_results,
1097     const caddr_t xdr_location)
1098 {
1099         struct rpc_msg rply;
1100 
1101         rply.rm_direction = REPLY;
1102         rply.rm_reply.rp_stat = MSG_ACCEPTED;
1103         rply.acpted_rply.ar_verf = xprt->xp_verf;
1104         rply.acpted_rply.ar_stat = SUCCESS;
1105         rply.acpted_rply.ar_results.where = xdr_location;
1106         rply.acpted_rply.ar_results.proc = xdr_results;
1107         return (SVC_REPLY((SVCXPRT *)xprt, &rply));
1108 }
1109 
1110 /*
1111  * No procedure error reply
1112  */
1113 void
1114 svcerr_noproc(const SVCXPRT *xprt)
1115 {
1116         struct rpc_msg rply;
1117 
1118         rply.rm_direction = REPLY;
1119         rply.rm_reply.rp_stat = MSG_ACCEPTED;
1120         rply.acpted_rply.ar_verf = xprt->xp_verf;
1121         rply.acpted_rply.ar_stat = PROC_UNAVAIL;
1122         SVC_REPLY((SVCXPRT *)xprt, &rply);
1123 }
1124 
1125 /*
1126  * Can't decode args error reply
1127  */
1128 void
1129 svcerr_decode(const SVCXPRT *xprt)
1130 {
1131         struct rpc_msg rply;
1132 
1133         rply.rm_direction = REPLY;
1134         rply.rm_reply.rp_stat = MSG_ACCEPTED;
1135         rply.acpted_rply.ar_verf = xprt->xp_verf;
1136         rply.acpted_rply.ar_stat = GARBAGE_ARGS;
1137         SVC_REPLY((SVCXPRT *)xprt, &rply);
1138 }
1139 
1140 /*
1141  * Some system error
1142  */
1143 void
1144 svcerr_systemerr(const SVCXPRT *xprt)
1145 {
1146         struct rpc_msg rply;
1147 
1148         rply.rm_direction = REPLY;
1149         rply.rm_reply.rp_stat = MSG_ACCEPTED;
1150         rply.acpted_rply.ar_verf = xprt->xp_verf;
1151         rply.acpted_rply.ar_stat = SYSTEM_ERR;
1152         SVC_REPLY((SVCXPRT *)xprt, &rply);
1153 }
1154 
1155 /*
1156  * Tell RPC package to not complain about version errors to the client.  This
1157  * is useful when revving broadcast protocols that sit on a fixed address.
1158  * There is really one (or should be only one) example of this kind of
1159  * protocol: the portmapper (or rpc binder).
1160  */
1161 void
1162 __svc_versquiet_on(const SVCXPRT *xprt)
1163 {
1164 /* LINTED pointer alignment */
1165         svc_flags(xprt) |= SVC_VERSQUIET;
1166 }
1167 
1168 void
1169 __svc_versquiet_off(const SVCXPRT *xprt)
1170 {
1171 /* LINTED pointer alignment */
1172         svc_flags(xprt) &= ~SVC_VERSQUIET;
1173 }
1174 
1175 void
1176 svc_versquiet(const SVCXPRT *xprt)
1177 {
1178         __svc_versquiet_on(xprt);
1179 }
1180 
1181 int
1182 __svc_versquiet_get(const SVCXPRT *xprt)
1183 {
1184 /* LINTED pointer alignment */
1185         return (svc_flags(xprt) & SVC_VERSQUIET);
1186 }
1187 
1188 /*
1189  * Authentication error reply
1190  */
1191 void
1192 svcerr_auth(const SVCXPRT *xprt, const enum auth_stat why)
1193 {
1194         struct rpc_msg rply;
1195 
1196         rply.rm_direction = REPLY;
1197         rply.rm_reply.rp_stat = MSG_DENIED;
1198         rply.rjcted_rply.rj_stat = AUTH_ERROR;
1199         rply.rjcted_rply.rj_why = why;
1200         SVC_REPLY((SVCXPRT *)xprt, &rply);
1201 }
1202 
1203 /*
1204  * Auth too weak error reply
1205  */
1206 void
1207 svcerr_weakauth(const SVCXPRT *xprt)
1208 {
1209         svcerr_auth(xprt, AUTH_TOOWEAK);
1210 }
1211 
1212 /*
1213  * Program unavailable error reply
1214  */
1215 void
1216 svcerr_noprog(const SVCXPRT *xprt)
1217 {
1218         struct rpc_msg rply;
1219 
1220         rply.rm_direction = REPLY;
1221         rply.rm_reply.rp_stat = MSG_ACCEPTED;
1222         rply.acpted_rply.ar_verf = xprt->xp_verf;
1223         rply.acpted_rply.ar_stat = PROG_UNAVAIL;
1224         SVC_REPLY((SVCXPRT *)xprt, &rply);
1225 }
1226 
1227 /*
1228  * Program version mismatch error reply
1229  */
1230 void
1231 svcerr_progvers(const SVCXPRT *xprt, const rpcvers_t low_vers,
1232     const rpcvers_t high_vers)
1233 {
1234         struct rpc_msg rply;
1235 
1236         rply.rm_direction = REPLY;
1237         rply.rm_reply.rp_stat = MSG_ACCEPTED;
1238         rply.acpted_rply.ar_verf = xprt->xp_verf;
1239         rply.acpted_rply.ar_stat = PROG_MISMATCH;
1240         rply.acpted_rply.ar_vers.low = low_vers;
1241         rply.acpted_rply.ar_vers.high = high_vers;
1242         SVC_REPLY((SVCXPRT *)xprt, &rply);
1243 }
1244 
1245 /* ******************* SERVER INPUT STUFF ******************* */
1246 
1247 /*
1248  * Get server side input from some transport.
1249  *
1250  * Statement of authentication parameters management:
1251  * This function owns and manages all authentication parameters, specifically
1252  * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
1253  * the "cooked" credentials (rqst->rq_clntcred).
1254  * However, this function does not know the structure of the cooked
1255  * credentials, so it make the following assumptions:
1256  *   a) the structure is contiguous (no pointers), and
1257  *   b) the cred structure size does not exceed RQCRED_SIZE bytes.
1258  * In all events, all three parameters are freed upon exit from this routine.
1259  * The storage is trivially management on the call stack in user land, but
1260  * is mallocated in kernel land.
1261  */
1262 
1263 void
1264 svc_getreq(int rdfds)
1265 {
1266         fd_set readfds;
1267 
1268         FD_ZERO(&readfds);
1269         readfds.fds_bits[0] = rdfds;
1270         svc_getreqset(&readfds);
1271 }
1272 
1273 void
1274 svc_getreqset(fd_set *readfds)
1275 {
1276         int i;
1277 
1278         for (i = 0; i < svc_max_fd; i++) {
1279                 /* fd has input waiting */
1280                 if (FD_ISSET(i, readfds))
1281                         svc_getreq_common(i);
1282         }
1283 }
1284 
1285 void
1286 svc_getreq_poll(struct pollfd *pfdp, const int pollretval)
1287 {
1288         int i;
1289         int fds_found;
1290 
1291         for (i = fds_found = 0; fds_found < pollretval; i++) {
1292                 struct pollfd *p = &pfdp[i];
1293 
1294                 if (p->revents) {
1295                         /* fd has input waiting */
1296                         fds_found++;
1297                         /*
1298                          *      We assume that this function is only called
1299                          *      via someone select()ing from svc_fdset or
1300                          *      poll()ing from svc_pollset[].  Thus it's safe
1301                          *      to handle the POLLNVAL event by simply turning
1302                          *      the corresponding bit off in svc_fdset.  The
1303                          *      svc_pollset[] array is derived from svc_fdset
1304                          *      and so will also be updated eventually.
1305                          *
1306                          *      XXX Should we do an xprt_unregister() instead?
1307                          */
1308                         /* Handle user callback */
1309                         if (__is_a_userfd(p->fd) == TRUE) {
1310                                 (void) rw_rdlock(&svc_fd_lock);
1311                                 __svc_getreq_user(p);
1312                                 (void) rw_unlock(&svc_fd_lock);
1313                         } else {
1314                                 if (p->revents & POLLNVAL) {
1315                                         (void) rw_wrlock(&svc_fd_lock);
1316                                         remove_pollfd(p->fd);        /* XXX */
1317                                         (void) rw_unlock(&svc_fd_lock);
1318                                 } else {
1319                                         svc_getreq_common(p->fd);
1320                                 }
1321                         }
1322                 }
1323         }
1324 }
1325 
1326 void
1327 svc_getreq_common(const int fd)
1328 {
1329         SVCXPRT *xprt;
1330         enum xprt_stat stat;
1331         struct rpc_msg *msg;
1332         struct svc_req *r;
1333         char *cred_area;
1334 
1335         (void) rw_rdlock(&svc_fd_lock);
1336 
1337         /* HANDLE USER CALLBACK */
1338         if (__is_a_userfd(fd) == TRUE) {
1339                 struct pollfd virtual_fd;
1340 
1341                 virtual_fd.events = virtual_fd.revents = (short)0xFFFF;
1342                 virtual_fd.fd = fd;
1343                 __svc_getreq_user(&virtual_fd);
1344                 (void) rw_unlock(&svc_fd_lock);
1345                 return;
1346         }
1347 
1348         /*
1349          * The transport associated with this fd could have been
1350          * removed from svc_timeout_nonblock_xprt_and_LRU, for instance.
1351          * This can happen if two or more fds get read events and are
1352          * passed to svc_getreq_poll/set, the first fd is seviced by
1353          * the dispatch routine and cleans up any dead transports.  If
1354          * one of the dead transports removed is the other fd that
1355          * had a read event then svc_getreq_common() will be called with no
1356          * xprt associated with the fd that had the original read event.
1357          */
1358         if ((fd >= nsvc_xports) || (xprt = svc_xports[fd]) == NULL) {
1359                 (void) rw_unlock(&svc_fd_lock);
1360                 return;
1361         }
1362         (void) rw_unlock(&svc_fd_lock);
1363 /* LINTED pointer alignment */
1364         msg = SVCEXT(xprt)->msg;
1365 /* LINTED pointer alignment */
1366         r = SVCEXT(xprt)->req;
1367 /* LINTED pointer alignment */
1368         cred_area = SVCEXT(xprt)->cred_area;
1369         msg->rm_call.cb_cred.oa_base = cred_area;
1370         msg->rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]);
1371         r->rq_clntcred = &(cred_area[2 * MAX_AUTH_BYTES]);
1372 
1373         /* receive msgs from xprtprt (support batch calls) */
1374         do {
1375                 bool_t dispatch;
1376 
1377                 if (dispatch = SVC_RECV(xprt, msg))
1378                         (void) _svc_prog_dispatch(xprt, msg, r);
1379                 /*
1380                  * Check if the xprt has been disconnected in a recursive call
1381                  * in the service dispatch routine. If so, then break
1382                  */
1383                 (void) rw_rdlock(&svc_fd_lock);
1384                 if (xprt != svc_xports[fd]) {
1385                         (void) rw_unlock(&svc_fd_lock);
1386                         break;
1387                 }
1388                 (void) rw_unlock(&svc_fd_lock);
1389 
1390                 /*
1391                  * Call cleanup procedure if set.
1392                  */
1393                 if (__proc_cleanup_cb != NULL && dispatch)
1394                         (*__proc_cleanup_cb)(xprt);
1395 
1396                 if ((stat = SVC_STAT(xprt)) == XPRT_DIED) {
1397                         SVC_DESTROY(xprt);
1398                         break;
1399                 }
1400         } while (stat == XPRT_MOREREQS);
1401 }
1402 
1403 int
1404 _svc_prog_dispatch(SVCXPRT *xprt, struct rpc_msg *msg, struct svc_req *r)
1405 {
1406         struct svc_callout *s;
1407         enum auth_stat why;
1408         int prog_found;
1409         rpcvers_t low_vers;
1410         rpcvers_t high_vers;
1411         void (*disp_fn)();
1412 
1413         r->rq_xprt = xprt;
1414         r->rq_prog = msg->rm_call.cb_prog;
1415         r->rq_vers = msg->rm_call.cb_vers;
1416         r->rq_proc = msg->rm_call.cb_proc;
1417         r->rq_cred = msg->rm_call.cb_cred;
1418 /* LINTED pointer alignment */
1419         SVC_XP_AUTH(r->rq_xprt).svc_ah_ops = svc_auth_any_ops;
1420 /* LINTED pointer alignment */
1421         SVC_XP_AUTH(r->rq_xprt).svc_ah_private = NULL;
1422 
1423         /* first authenticate the message */
1424         /* Check for null flavor and bypass these calls if possible */
1425 
1426         if (msg->rm_call.cb_cred.oa_flavor == AUTH_NULL) {
1427                 r->rq_xprt->xp_verf.oa_flavor = _null_auth.oa_flavor;
1428                 r->rq_xprt->xp_verf.oa_length = 0;
1429         } else {
1430                 bool_t no_dispatch;
1431 
1432                 if ((why = __gss_authenticate(r, msg,
1433                     &no_dispatch)) != AUTH_OK) {
1434                         svcerr_auth(xprt, why);
1435                         return (0);
1436                 }
1437                 if (no_dispatch)
1438                         return (0);
1439         }
1440         /* match message with a registered service */
1441         prog_found = FALSE;
1442         low_vers = (rpcvers_t)(0 - 1);
1443         high_vers = 0;
1444         (void) rw_rdlock(&svc_lock);
1445         for (s = svc_head; s != NULL_SVC; s = s->sc_next) {
1446                 if (s->sc_prog == r->rq_prog) {
1447                         prog_found = TRUE;
1448                         if (s->sc_vers == r->rq_vers) {
1449                                 if ((xprt->xp_netid == NULL) ||
1450                                     (s->sc_netid == NULL) ||
1451                                     (strcmp(xprt->xp_netid,
1452                                     s->sc_netid) == 0)) {
1453                                         disp_fn = (*s->sc_dispatch);
1454                                         (void) rw_unlock(&svc_lock);
1455                                         disp_fn(r, xprt);
1456                                         return (1);
1457                                 }
1458                                 prog_found = FALSE;
1459                         }
1460                         if (s->sc_vers < low_vers)
1461                                 low_vers = s->sc_vers;
1462                         if (s->sc_vers > high_vers)
1463                                 high_vers = s->sc_vers;
1464                 }               /* found correct program */
1465         }
1466         (void) rw_unlock(&svc_lock);
1467 
1468         /*
1469          * if we got here, the program or version
1470          * is not served ...
1471          */
1472         if (prog_found) {
1473 /* LINTED pointer alignment */
1474                 if (!version_keepquiet(xprt))
1475                         svcerr_progvers(xprt, low_vers, high_vers);
1476         } else {
1477                 svcerr_noprog(xprt);
1478         }
1479         return (0);
1480 }
1481 
1482 /* ******************* SVCXPRT allocation and deallocation ***************** */
1483 
1484 /*
1485  * svc_xprt_alloc() - allocate a service transport handle
1486  */
1487 SVCXPRT *
1488 svc_xprt_alloc(void)
1489 {
1490         SVCXPRT         *xprt = NULL;
1491         SVCXPRT_EXT     *xt = NULL;
1492         SVCXPRT_LIST    *xlist = NULL;
1493         struct rpc_msg  *msg = NULL;
1494         struct svc_req  *req = NULL;
1495         char            *cred_area = NULL;
1496 
1497         if ((xprt = calloc(1, sizeof (SVCXPRT))) == NULL)
1498                 goto err_exit;
1499 
1500         if ((xt = calloc(1, sizeof (SVCXPRT_EXT))) == NULL)
1501                 goto err_exit;
1502         xprt->xp_p3 = (caddr_t)xt; /* SVCEXT(xprt) = xt */
1503 
1504         if ((xlist = calloc(1, sizeof (SVCXPRT_LIST))) == NULL)
1505                 goto err_exit;
1506         xt->my_xlist = xlist;
1507         xlist->xprt = xprt;
1508 
1509         if ((msg = malloc(sizeof (struct rpc_msg))) == NULL)
1510                 goto err_exit;
1511         xt->msg = msg;
1512 
1513         if ((req = malloc(sizeof (struct svc_req))) == NULL)
1514                 goto err_exit;
1515         xt->req = req;
1516 
1517         if ((cred_area = malloc(2*MAX_AUTH_BYTES + RQCRED_SIZE)) == NULL)
1518                 goto err_exit;
1519         xt->cred_area = cred_area;
1520 
1521 /* LINTED pointer alignment */
1522         (void) mutex_init(&svc_send_mutex(xprt), USYNC_THREAD, (void *)0);
1523         return (xprt);
1524 
1525 err_exit:
1526         svc_xprt_free(xprt);
1527         return (NULL);
1528 }
1529 
1530 
1531 /*
1532  * svc_xprt_free() - free a service handle
1533  */
1534 void
1535 svc_xprt_free(SVCXPRT *xprt)
1536 {
1537 /* LINTED pointer alignment */
1538         SVCXPRT_EXT     *xt = xprt ? SVCEXT(xprt) : NULL;
1539         SVCXPRT_LIST    *my_xlist = xt ? xt->my_xlist: NULL;
1540         struct rpc_msg  *msg = xt ? xt->msg : NULL;
1541         struct svc_req  *req = xt ? xt->req : NULL;
1542         char            *cred_area = xt ? xt->cred_area : NULL;
1543 
1544         if (xprt)
1545                 free(xprt);
1546         if (xt)
1547                 free(xt);
1548         if (my_xlist)
1549                 free(my_xlist);
1550         if (msg)
1551                 free(msg);
1552         if (req)
1553                 free(req);
1554         if (cred_area)
1555                 free(cred_area);
1556 }
1557 
1558 
1559 /*
1560  * svc_xprt_destroy() - free parent and child xprt list
1561  */
1562 void
1563 svc_xprt_destroy(SVCXPRT *xprt)
1564 {
1565         SVCXPRT_LIST    *xlist, *xnext = NULL;
1566         int             type;
1567 
1568 /* LINTED pointer alignment */
1569         if (SVCEXT(xprt)->parent)
1570 /* LINTED pointer alignment */
1571                 xprt = SVCEXT(xprt)->parent;
1572 /* LINTED pointer alignment */
1573         type = svc_type(xprt);
1574 /* LINTED pointer alignment */
1575         for (xlist = SVCEXT(xprt)->my_xlist; xlist != NULL; xlist = xnext) {
1576                 xnext = xlist->next;
1577                 xprt = xlist->xprt;
1578                 switch (type) {
1579                 case SVC_DGRAM:
1580                         svc_dg_xprtfree(xprt);
1581                         break;
1582                 case SVC_RENDEZVOUS:
1583                         svc_vc_xprtfree(xprt);
1584                         break;
1585                 case SVC_CONNECTION:
1586                         svc_fd_xprtfree(xprt);
1587                         break;
1588                 case SVC_DOOR:
1589                         svc_door_xprtfree(xprt);
1590                         break;
1591                 }
1592         }
1593 }
1594 
1595 
1596 /*
1597  * svc_copy() - make a copy of parent
1598  */
1599 SVCXPRT *
1600 svc_copy(SVCXPRT *xprt)
1601 {
1602 /* LINTED pointer alignment */
1603         switch (svc_type(xprt)) {
1604         case SVC_DGRAM:
1605                 return (svc_dg_xprtcopy(xprt));
1606         case SVC_RENDEZVOUS:
1607                 return (svc_vc_xprtcopy(xprt));
1608         case SVC_CONNECTION:
1609                 return (svc_fd_xprtcopy(xprt));
1610         }
1611         return (NULL);
1612 }
1613 
1614 
1615 /*
1616  * _svc_destroy_private() - private SVC_DESTROY interface
1617  */
1618 void
1619 _svc_destroy_private(SVCXPRT *xprt)
1620 {
1621 /* LINTED pointer alignment */
1622         switch (svc_type(xprt)) {
1623         case SVC_DGRAM:
1624                 _svc_dg_destroy_private(xprt);
1625                 break;
1626         case SVC_RENDEZVOUS:
1627         case SVC_CONNECTION:
1628                 _svc_vc_destroy_private(xprt, TRUE);
1629                 break;
1630         }
1631 }
1632 
1633 /*
1634  * svc_get_local_cred() - fetch local user credentials.  This always
1635  * works over doors based transports.  For local transports, this
1636  * does not yield correct results unless the __rpc_negotiate_uid()
1637  * call has been invoked to enable this feature.
1638  */
1639 bool_t
1640 svc_get_local_cred(SVCXPRT *xprt, svc_local_cred_t *lcred)
1641 {
1642         /* LINTED pointer alignment */
1643         if (svc_type(xprt) == SVC_DOOR)
1644                 return (__svc_get_door_cred(xprt, lcred));
1645         return (__rpc_get_local_cred(xprt, lcred));
1646 }
1647 
1648 
1649 /* ******************* DUPLICATE ENTRY HANDLING ROUTINES ************** */
1650 
1651 /*
1652  * the dup cacheing routines below provide a cache of received
1653  * transactions. rpc service routines can use this to detect
1654  * retransmissions and re-send a non-failure response. Uses a
1655  * lru scheme to find entries to get rid of entries in the cache,
1656  * though only DUP_DONE entries are placed on the lru list.
1657  * the routines were written towards development of a generic
1658  * SVC_DUP() interface, which can be expanded to encompass the
1659  * svc_dg_enablecache() routines as well. the cache is currently
1660  * private to the automounter.
1661  */
1662 
1663 
1664 /* dupcache header contains xprt specific information */
1665 struct dupcache {
1666         rwlock_t        dc_lock;
1667         time_t          dc_time;
1668         int             dc_buckets;
1669         int             dc_maxsz;
1670         int             dc_basis;
1671         struct dupreq   *dc_mru;
1672         struct dupreq   **dc_hashtbl;
1673 };
1674 
1675 /*
1676  * private duplicate cache request routines
1677  */
1678 static int __svc_dupcache_check(struct svc_req *, caddr_t *, uint_t *,
1679                 struct dupcache *, uint32_t, uint32_t);
1680 static struct dupreq *__svc_dupcache_victim(struct dupcache *, time_t);
1681 static int __svc_dupcache_enter(struct svc_req *, struct dupreq *,
1682                 struct dupcache *, uint32_t, uint32_t, time_t);
1683 static int __svc_dupcache_update(struct svc_req *, caddr_t, uint_t, int,
1684                 struct dupcache *, uint32_t, uint32_t);
1685 #ifdef DUP_DEBUG
1686 static void __svc_dupcache_debug(struct dupcache *);
1687 #endif /* DUP_DEBUG */
1688 
1689 /* default parameters for the dupcache */
1690 #define DUPCACHE_BUCKETS        257
1691 #define DUPCACHE_TIME           900
1692 #define DUPCACHE_MAXSZ          INT_MAX
1693 
1694 /*
1695  * __svc_dupcache_init(void *condition, int basis, char *xprt_cache)
1696  * initialize the duprequest cache and assign it to the xprt_cache
1697  * Use default values depending on the cache condition and basis.
1698  * return TRUE on success and FALSE on failure
1699  */
1700 bool_t
1701 __svc_dupcache_init(void *condition, int basis, char **xprt_cache)
1702 {
1703         static mutex_t initdc_lock = DEFAULTMUTEX;
1704         int i;
1705         struct dupcache *dc;
1706 
1707         (void) mutex_lock(&initdc_lock);
1708         if (*xprt_cache != NULL) { /* do only once per xprt */
1709                 (void) mutex_unlock(&initdc_lock);
1710                 syslog(LOG_ERR,
1711                     "__svc_dupcache_init: multiply defined dup cache");
1712                 return (FALSE);
1713         }
1714 
1715         switch (basis) {
1716         case DUPCACHE_FIXEDTIME:
1717                 dc = malloc(sizeof (struct dupcache));
1718                 if (dc == NULL) {
1719                         (void) mutex_unlock(&initdc_lock);
1720                         syslog(LOG_ERR,
1721                             "__svc_dupcache_init: memory alloc failed");
1722                         return (FALSE);
1723                 }
1724                 (void) rwlock_init(&(dc->dc_lock), USYNC_THREAD, NULL);
1725                 if (condition != NULL)
1726                         dc->dc_time = *((time_t *)condition);
1727                 else
1728                         dc->dc_time = DUPCACHE_TIME;
1729                 dc->dc_buckets = DUPCACHE_BUCKETS;
1730                 dc->dc_maxsz = DUPCACHE_MAXSZ;
1731                 dc->dc_basis = basis;
1732                 dc->dc_mru = NULL;
1733                 dc->dc_hashtbl = malloc(dc->dc_buckets *
1734                     sizeof (struct dupreq *));
1735                 if (dc->dc_hashtbl == NULL) {
1736                         free(dc);
1737                         (void) mutex_unlock(&initdc_lock);
1738                         syslog(LOG_ERR,
1739                             "__svc_dupcache_init: memory alloc failed");
1740                         return (FALSE);
1741                 }
1742                 for (i = 0; i < DUPCACHE_BUCKETS; i++)
1743                         dc->dc_hashtbl[i] = NULL;
1744                 *xprt_cache = (char *)dc;
1745                 break;
1746         default:
1747                 (void) mutex_unlock(&initdc_lock);
1748                 syslog(LOG_ERR,
1749                     "__svc_dupcache_init: undefined dup cache basis");
1750                 return (FALSE);
1751         }
1752 
1753         (void) mutex_unlock(&initdc_lock);
1754 
1755         return (TRUE);
1756 }
1757 
1758 /*
1759  * __svc_dup(struct svc_req *req, caddr_t *resp_buf, uint_t *resp_bufsz,
1760  *      char *xprt_cache)
1761  * searches the request cache. Creates an entry and returns DUP_NEW if
1762  * the request is not found in the cache.  If it is found, then it
1763  * returns the state of the request (in progress, drop, or done) and
1764  * also allocates, and passes back results to the user (if any) in
1765  * resp_buf, and its length in resp_bufsz. DUP_ERROR is returned on error.
1766  */
1767 int
1768 __svc_dup(struct svc_req *req, caddr_t *resp_buf, uint_t *resp_bufsz,
1769     char *xprt_cache)
1770 {
1771         uint32_t drxid, drhash;
1772         int rc;
1773         struct dupreq *dr = NULL;
1774         time_t timenow = time(NULL);
1775 
1776         /* LINTED pointer alignment */
1777         struct dupcache *dc = (struct dupcache *)xprt_cache;
1778 
1779         if (dc == NULL) {
1780                 syslog(LOG_ERR, "__svc_dup: undefined cache");
1781                 return (DUP_ERROR);
1782         }
1783 
1784         /* get the xid of the request */
1785         if (SVC_CONTROL(req->rq_xprt, SVCGET_XID, (void*)&drxid) == FALSE) {
1786                 syslog(LOG_ERR, "__svc_dup: xid error");
1787                 return (DUP_ERROR);
1788         }
1789         drhash = drxid % dc->dc_buckets;
1790 
1791         if ((rc = __svc_dupcache_check(req, resp_buf, resp_bufsz, dc, drxid,
1792             drhash)) != DUP_NEW)
1793                 return (rc);
1794 
1795         if ((dr = __svc_dupcache_victim(dc, timenow)) == NULL)
1796                 return (DUP_ERROR);
1797 
1798         if ((rc = __svc_dupcache_enter(req, dr, dc, drxid, drhash, timenow))
1799             == DUP_ERROR)
1800                 return (rc);
1801 
1802         return (DUP_NEW);
1803 }
1804 
1805 
1806 
1807 /*
1808  * __svc_dupcache_check(struct svc_req *req, caddr_t *resp_buf,
1809  *              uint_t *resp_bufsz,truct dupcache *dc, uint32_t drxid,
1810  *              uint32_t drhash)
1811  * Checks to see whether an entry already exists in the cache. If it does
1812  * copy back into the resp_buf, if appropriate. Return the status of
1813  * the request, or DUP_NEW if the entry is not in the cache
1814  */
1815 static int
1816 __svc_dupcache_check(struct svc_req *req, caddr_t *resp_buf, uint_t *resp_bufsz,
1817     struct dupcache *dc, uint32_t drxid, uint32_t drhash)
1818 {
1819         struct dupreq *dr = NULL;
1820 
1821         (void) rw_rdlock(&(dc->dc_lock));
1822         dr = dc->dc_hashtbl[drhash];
1823         while (dr != NULL) {
1824                 if (dr->dr_xid == drxid &&
1825                     dr->dr_proc == req->rq_proc &&
1826                     dr->dr_prog == req->rq_prog &&
1827                     dr->dr_vers == req->rq_vers &&
1828                     dr->dr_addr.len == req->rq_xprt->xp_rtaddr.len &&
1829                     memcmp(dr->dr_addr.buf, req->rq_xprt->xp_rtaddr.buf,
1830                     dr->dr_addr.len) == 0) { /* entry found */
1831                         if (dr->dr_hash != drhash) {
1832                                 /* sanity check */
1833                                 (void) rw_unlock((&dc->dc_lock));
1834                                 syslog(LOG_ERR,
1835                                     "\n__svc_dupdone: hashing error");
1836                                 return (DUP_ERROR);
1837                         }
1838 
1839                         /*
1840                          * return results for requests on lru list, if
1841                          * appropriate requests must be DUP_DROP or DUP_DONE
1842                          * to have a result. A NULL buffer in the cache
1843                          * implies no results were sent during dupdone.
1844                          * A NULL buffer in the call implies not interested
1845                          * in results.
1846                          */
1847                         if (((dr->dr_status == DUP_DONE) ||
1848                             (dr->dr_status == DUP_DROP)) &&
1849                             resp_buf != NULL &&
1850                             dr->dr_resp.buf != NULL) {
1851                                 *resp_buf = malloc(dr->dr_resp.len);
1852                                 if (*resp_buf == NULL) {
1853                                         syslog(LOG_ERR,
1854                                         "__svc_dupcache_check: malloc failed");
1855                                         (void) rw_unlock(&(dc->dc_lock));
1856                                         return (DUP_ERROR);
1857                                 }
1858                                 (void) memset(*resp_buf, 0, dr->dr_resp.len);
1859                                 (void) memcpy(*resp_buf, dr->dr_resp.buf,
1860                                     dr->dr_resp.len);
1861                                 *resp_bufsz = dr->dr_resp.len;
1862                         } else {
1863                                 /* no result */
1864                                 if (resp_buf)
1865                                         *resp_buf = NULL;
1866                                 if (resp_bufsz)
1867                                         *resp_bufsz = 0;
1868                         }
1869                         (void) rw_unlock(&(dc->dc_lock));
1870                         return (dr->dr_status);
1871                 }
1872                 dr = dr->dr_chain;
1873         }
1874         (void) rw_unlock(&(dc->dc_lock));
1875         return (DUP_NEW);
1876 }
1877 
1878 /*
1879  * __svc_dupcache_victim(struct dupcache *dc, time_t timenow)
1880  * Return a victim dupreq entry to the caller, depending on cache policy.
1881  */
1882 static struct dupreq *
1883 __svc_dupcache_victim(struct dupcache *dc, time_t timenow)
1884 {
1885         struct dupreq *dr = NULL;
1886 
1887         switch (dc->dc_basis) {
1888         case DUPCACHE_FIXEDTIME:
1889                 /*
1890                  * The hash policy is to free up a bit of the hash
1891                  * table before allocating a new entry as the victim.
1892                  * Freeing up the hash table each time should split
1893                  * the cost of keeping the hash table clean among threads.
1894                  * Note that only DONE or DROPPED entries are on the lru
1895                  * list but we do a sanity check anyway.
1896                  */
1897                 (void) rw_wrlock(&(dc->dc_lock));
1898                 while ((dc->dc_mru) && (dr = dc->dc_mru->dr_next) &&
1899                     ((timenow - dr->dr_time) > dc->dc_time)) {
1900                         /* clean and then free the entry */
1901                         if (dr->dr_status != DUP_DONE &&
1902                             dr->dr_status != DUP_DROP) {
1903                                 /*
1904                                  * The LRU list can't contain an
1905                                  * entry where the status is other than
1906                                  * DUP_DONE or DUP_DROP.
1907                                  */
1908                                 syslog(LOG_ERR,
1909                                     "__svc_dupcache_victim: bad victim");
1910 #ifdef DUP_DEBUG
1911                                 /*
1912                                  * Need to hold the reader/writers lock to
1913                                  * print the cache info, since we already
1914                                  * hold the writers lock, we shall continue
1915                                  * calling __svc_dupcache_debug()
1916                                  */
1917                                 __svc_dupcache_debug(dc);
1918 #endif /* DUP_DEBUG */
1919                                 (void) rw_unlock(&(dc->dc_lock));
1920                                 return (NULL);
1921                         }
1922                         /* free buffers */
1923                         if (dr->dr_resp.buf) {
1924                                 free(dr->dr_resp.buf);
1925                                 dr->dr_resp.buf = NULL;
1926                         }
1927                         if (dr->dr_addr.buf) {
1928                                 free(dr->dr_addr.buf);
1929                                 dr->dr_addr.buf = NULL;
1930                         }
1931 
1932                         /* unhash the entry */
1933                         if (dr->dr_chain)
1934                                 dr->dr_chain->dr_prevchain = dr->dr_prevchain;
1935                         if (dr->dr_prevchain)
1936                                 dr->dr_prevchain->dr_chain = dr->dr_chain;
1937                         if (dc->dc_hashtbl[dr->dr_hash] == dr)
1938                                 dc->dc_hashtbl[dr->dr_hash] = dr->dr_chain;
1939 
1940                         /* modify the lru pointers */
1941                         if (dc->dc_mru == dr) {
1942                                 dc->dc_mru = NULL;
1943                         } else {
1944                                 dc->dc_mru->dr_next = dr->dr_next;
1945                                 dr->dr_next->dr_prev = dc->dc_mru;
1946                         }
1947                         free(dr);
1948                         dr = NULL;
1949                 }
1950                 (void) rw_unlock(&(dc->dc_lock));
1951 
1952                 /*
1953                  * Allocate and return new clean entry as victim
1954                  */
1955                 if ((dr = malloc(sizeof (*dr))) == NULL) {
1956                         syslog(LOG_ERR,
1957                             "__svc_dupcache_victim: malloc failed");
1958                         return (NULL);
1959                 }
1960                 (void) memset(dr, 0, sizeof (*dr));
1961                 return (dr);
1962         default:
1963                 syslog(LOG_ERR,
1964                     "__svc_dupcache_victim: undefined dup cache_basis");
1965                 return (NULL);
1966         }
1967 }
1968 
1969 /*
1970  * __svc_dupcache_enter(struct svc_req *req, struct dupreq *dr,
1971  *      struct dupcache *dc, uint32_t drxid, uint32_t drhash, time_t timenow)
1972  * build new duprequest entry and then insert into the cache
1973  */
1974 static int
1975 __svc_dupcache_enter(struct svc_req *req, struct dupreq *dr,
1976     struct dupcache *dc, uint32_t drxid, uint32_t drhash, time_t timenow)
1977 {
1978         dr->dr_xid = drxid;
1979         dr->dr_prog = req->rq_prog;
1980         dr->dr_vers = req->rq_vers;
1981         dr->dr_proc = req->rq_proc;
1982         dr->dr_addr.maxlen = req->rq_xprt->xp_rtaddr.len;
1983         dr->dr_addr.len = dr->dr_addr.maxlen;
1984         if ((dr->dr_addr.buf = malloc(dr->dr_addr.maxlen)) == NULL) {
1985                 syslog(LOG_ERR, "__svc_dupcache_enter: malloc failed");
1986                 free(dr);
1987                 return (DUP_ERROR);
1988         }
1989         (void) memset(dr->dr_addr.buf, 0, dr->dr_addr.len);
1990         (void) memcpy(dr->dr_addr.buf, req->rq_xprt->xp_rtaddr.buf,
1991             dr->dr_addr.len);
1992         dr->dr_resp.buf = NULL;
1993         dr->dr_resp.maxlen = 0;
1994         dr->dr_resp.len = 0;
1995         dr->dr_status = DUP_INPROGRESS;
1996         dr->dr_time = timenow;
1997         dr->dr_hash = drhash;        /* needed for efficient victim cleanup */
1998 
1999         /* place entry at head of hash table */
2000         (void) rw_wrlock(&(dc->dc_lock));
2001         dr->dr_chain = dc->dc_hashtbl[drhash];
2002         dr->dr_prevchain = NULL;
2003         if (dc->dc_hashtbl[drhash] != NULL)
2004                 dc->dc_hashtbl[drhash]->dr_prevchain = dr;
2005         dc->dc_hashtbl[drhash] = dr;
2006         (void) rw_unlock(&(dc->dc_lock));
2007         return (DUP_NEW);
2008 }
2009 
2010 /*
2011  * __svc_dupdone(struct svc_req *req, caddr_t resp_buf, uint_t resp_bufsz,
2012  *              int status, char *xprt_cache)
2013  * Marks the request done (DUP_DONE or DUP_DROP) and stores the response.
2014  * Only DONE and DROP requests can be marked as done. Sets the lru pointers
2015  * to make the entry the most recently used. Returns DUP_ERROR or status.
2016  */
2017 int
2018 __svc_dupdone(struct svc_req *req, caddr_t resp_buf, uint_t resp_bufsz,
2019     int status, char *xprt_cache)
2020 {
2021         uint32_t drxid, drhash;
2022         int rc;
2023 
2024         /* LINTED pointer alignment */
2025         struct dupcache *dc = (struct dupcache *)xprt_cache;
2026 
2027         if (dc == NULL) {
2028                 syslog(LOG_ERR, "__svc_dupdone: undefined cache");
2029                 return (DUP_ERROR);
2030         }
2031 
2032         if (status != DUP_DONE && status != DUP_DROP) {
2033                 syslog(LOG_ERR, "__svc_dupdone: invalid dupdone status");
2034                 syslog(LOG_ERR, "        must be DUP_DONE or DUP_DROP");
2035                 return (DUP_ERROR);
2036         }
2037 
2038         /* find the xid of the entry in the cache */
2039         if (SVC_CONTROL(req->rq_xprt, SVCGET_XID, (void*)&drxid) == FALSE) {
2040                 syslog(LOG_ERR, "__svc_dup: xid error");
2041                 return (DUP_ERROR);
2042         }
2043         drhash = drxid % dc->dc_buckets;
2044 
2045         /* update the status of the entry and result buffers, if required */
2046         if ((rc = __svc_dupcache_update(req, resp_buf, resp_bufsz, status,
2047             dc, drxid, drhash)) == DUP_ERROR) {
2048                 syslog(LOG_ERR, "__svc_dupdone: cache entry error");
2049                 return (DUP_ERROR);
2050         }
2051 
2052         return (rc);
2053 }
2054 
2055 /*
2056  * __svc_dupcache_update(struct svc_req *req, caddr_t resp_buf,
2057  *      uint_t resp_bufsz, int status, struct dupcache *dc, uint32_t drxid,
2058  *      uint32_t drhash)
2059  * Check if entry exists in the dupcacache. If it does, update its status
2060  * and time and also its buffer, if appropriate. Its possible, but unlikely
2061  * for DONE requests to not exist in the cache. Return DUP_ERROR or status.
2062  */
2063 static int
2064 __svc_dupcache_update(struct svc_req *req, caddr_t resp_buf, uint_t resp_bufsz,
2065     int status, struct dupcache *dc, uint32_t drxid, uint32_t drhash)
2066 {
2067         struct dupreq *dr = NULL;
2068         time_t timenow = time(NULL);
2069 
2070         (void) rw_wrlock(&(dc->dc_lock));
2071         dr = dc->dc_hashtbl[drhash];
2072         while (dr != NULL) {
2073                 if (dr->dr_xid == drxid &&
2074                     dr->dr_proc == req->rq_proc &&
2075                     dr->dr_prog == req->rq_prog &&
2076                     dr->dr_vers == req->rq_vers &&
2077                     dr->dr_addr.len == req->rq_xprt->xp_rtaddr.len &&
2078                     memcmp(dr->dr_addr.buf, req->rq_xprt->xp_rtaddr.buf,
2079                     dr->dr_addr.len) == 0) { /* entry found */
2080                         if (dr->dr_hash != drhash) {
2081                                 /* sanity check */
2082                                 (void) rw_unlock(&(dc->dc_lock));
2083                                 syslog(LOG_ERR,
2084                                 "\n__svc_dupdone: hashing error");
2085                                 return (DUP_ERROR);
2086                         }
2087 
2088                         /* store the results if bufer is not NULL */
2089                         if (resp_buf != NULL) {
2090                                 if ((dr->dr_resp.buf =
2091                                     malloc(resp_bufsz)) == NULL) {
2092                                         (void) rw_unlock(&(dc->dc_lock));
2093                                         syslog(LOG_ERR,
2094                                             "__svc_dupdone: malloc failed");
2095                                         return (DUP_ERROR);
2096                                 }
2097                                 (void) memset(dr->dr_resp.buf, 0, resp_bufsz);
2098                                 (void) memcpy(dr->dr_resp.buf, resp_buf,
2099                                     (uint_t)resp_bufsz);
2100                                 dr->dr_resp.len = resp_bufsz;
2101                         }
2102 
2103                         /* update status and done time */
2104                         dr->dr_status = status;
2105                         dr->dr_time = timenow;
2106 
2107                         /* move the entry to the mru position */
2108                         if (dc->dc_mru == NULL) {
2109                                 dr->dr_next = dr;
2110                                 dr->dr_prev = dr;
2111                         } else {
2112                                 dr->dr_next = dc->dc_mru->dr_next;
2113                                 dc->dc_mru->dr_next->dr_prev = dr;
2114                                 dr->dr_prev = dc->dc_mru;
2115                                 dc->dc_mru->dr_next = dr;
2116                         }
2117                         dc->dc_mru = dr;
2118 
2119                         (void) rw_unlock(&(dc->dc_lock));
2120                         return (status);
2121                 }
2122                 dr = dr->dr_chain;
2123         }
2124         (void) rw_unlock(&(dc->dc_lock));
2125         syslog(LOG_ERR, "__svc_dupdone: entry not in dup cache");
2126         return (DUP_ERROR);
2127 }
2128 
2129 #ifdef DUP_DEBUG
2130 /*
2131  * __svc_dupcache_debug(struct dupcache *dc)
2132  * print out the hash table stuff
2133  *
2134  * This function requires the caller to hold the reader
2135  * or writer version of the duplicate request cache lock (dc_lock).
2136  */
2137 static void
2138 __svc_dupcache_debug(struct dupcache *dc)
2139 {
2140         struct dupreq *dr = NULL;
2141         int i;
2142         bool_t bval;
2143 
2144         fprintf(stderr, "   HASHTABLE\n");
2145         for (i = 0; i < dc->dc_buckets; i++) {
2146                 bval = FALSE;
2147                 dr = dc->dc_hashtbl[i];
2148                 while (dr != NULL) {
2149                         if (!bval) {    /* ensures bucket printed only once */
2150                                 fprintf(stderr, "    bucket : %d\n", i);
2151                                 bval = TRUE;
2152                         }
2153                         fprintf(stderr, "\txid: %u status: %d time: %ld",
2154                             dr->dr_xid, dr->dr_status, dr->dr_time);
2155                         fprintf(stderr, " dr: %x chain: %x prevchain: %x\n",
2156                             dr, dr->dr_chain, dr->dr_prevchain);
2157                         dr = dr->dr_chain;
2158                 }
2159         }
2160 
2161         fprintf(stderr, "   LRU\n");
2162         if (dc->dc_mru) {
2163                 dr = dc->dc_mru->dr_next; /* lru */
2164                 while (dr != dc->dc_mru) {
2165                         fprintf(stderr, "\txid: %u status : %d time : %ld",
2166                             dr->dr_xid, dr->dr_status, dr->dr_time);
2167                         fprintf(stderr, " dr: %x next: %x prev: %x\n",
2168                             dr, dr->dr_next, dr->dr_prev);
2169                         dr = dr->dr_next;
2170                 }
2171                 fprintf(stderr, "\txid: %u status: %d time: %ld",
2172                     dr->dr_xid, dr->dr_status, dr->dr_time);
2173                 fprintf(stderr, " dr: %x next: %x prev: %x\n",
2174                     dr, dr->dr_next, dr->dr_prev);
2175         }
2176 }
2177 #endif /* DUP_DEBUG */