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