1 /*- 2 * Copyright (c) 2009, Sun Microsystems, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * - Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * - Redistributions in binary form must reproduce the above copyright notice, 10 * this list of conditions and the following disclaimer in the documentation 11 * and/or other materials provided with the distribution. 12 * - Neither the name of Sun Microsystems, Inc. nor the names of its 13 * contributors may be used to endorse or promote products derived 14 * from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 * 28 * @(#)svc.c 1.44 88/02/08 Copyr 1984 Sun Micro 29 * @(#)svc.c 2.4 88/08/11 4.0 RPCSRC 30 * $NetBSD: svc.c,v 1.21 2000/07/06 03:10:35 christos Exp $ 31 * $FreeBSD: src/lib/libc/rpc/svc.c,v 1.24 2006/02/27 22:10:59 deischen Exp $ 32 * $DragonFly: src/lib/libc/rpc/svc.c,v 1.4 2005/11/13 12:27:04 swildner Exp $ 33 */ 34 35 /* 36 * svc.c, Server-side remote procedure call interface. 37 * 38 * There are two sets of procedures here. The xprt routines are 39 * for handling transport handles. The svc routines handle the 40 * list of service routines. 41 * 42 * Copyright (C) 1984, Sun Microsystems, Inc. 43 */ 44 45 #include "namespace.h" 46 #include "reentrant.h" 47 #include <sys/types.h> 48 #include <sys/poll.h> 49 #include <assert.h> 50 #include <errno.h> 51 #include <stdlib.h> 52 #include <string.h> 53 54 #include <rpc/rpc.h> 55 #ifdef PORTMAP 56 #include <rpc/pmap_clnt.h> 57 #endif /* PORTMAP */ 58 #include "un-namespace.h" 59 60 #include "rpc_com.h" 61 #include "mt_misc.h" 62 63 #define RQCRED_SIZE 400 /* this size is excessive */ 64 65 #define SVC_VERSQUIET 0x0001 /* keep quiet about vers mismatch */ 66 #define version_keepquiet(xp) ((u_long)(xp)->xp_p3 & SVC_VERSQUIET) 67 68 #define max(a, b) (a > b ? a : b) 69 70 /* 71 * The services list 72 * Each entry represents a set of procedures (an rpc program). 73 * The dispatch routine takes request structs and runs the 74 * apropriate procedure. 75 */ 76 static struct svc_callout { 77 struct svc_callout *sc_next; 78 rpcprog_t sc_prog; 79 rpcvers_t sc_vers; 80 char *sc_netid; 81 void (*sc_dispatch)(struct svc_req *, SVCXPRT *); 82 } *svc_head; 83 84 SVCXPRT **__svc_xports; 85 int __svc_maxrec; 86 87 static struct svc_callout *svc_find(rpcprog_t, rpcvers_t, 88 struct svc_callout **, char *); 89 static void __xprt_do_unregister (SVCXPRT *xprt, bool_t dolock); 90 91 /* *************** SVCXPRT related stuff **************** */ 92 93 /* 94 * Activate a transport handle. 95 */ 96 void 97 xprt_register(SVCXPRT *xprt) 98 { 99 int sock; 100 101 assert(xprt != NULL); 102 103 sock = xprt->xp_fd; 104 105 rwlock_wrlock(&svc_fd_lock); 106 if (__svc_xports == NULL) { 107 __svc_xports = (SVCXPRT **) 108 mem_alloc(FD_SETSIZE * sizeof(SVCXPRT *)); 109 if (__svc_xports == NULL) 110 return; 111 memset(__svc_xports, '\0', FD_SETSIZE * sizeof(SVCXPRT *)); 112 } 113 if (sock < FD_SETSIZE) { 114 __svc_xports[sock] = xprt; 115 FD_SET(sock, &svc_fdset); 116 svc_maxfd = max(svc_maxfd, sock); 117 } 118 rwlock_unlock(&svc_fd_lock); 119 } 120 121 void 122 xprt_unregister(SVCXPRT *xprt) 123 { 124 __xprt_do_unregister(xprt, TRUE); 125 } 126 127 void 128 __xprt_unregister_unlocked(SVCXPRT *xprt) 129 { 130 __xprt_do_unregister(xprt, FALSE); 131 } 132 133 /* 134 * De-activate a transport handle. 135 */ 136 static void 137 __xprt_do_unregister(SVCXPRT *xprt, bool_t dolock) 138 { 139 int sock; 140 141 assert(xprt != NULL); 142 143 sock = xprt->xp_fd; 144 145 if (dolock) 146 rwlock_wrlock(&svc_fd_lock); 147 if ((sock < FD_SETSIZE) && (__svc_xports[sock] == xprt)) { 148 __svc_xports[sock] = NULL; 149 FD_CLR(sock, &svc_fdset); 150 if (sock >= svc_maxfd) { 151 for (svc_maxfd--; svc_maxfd>=0; svc_maxfd--) 152 if (__svc_xports[svc_maxfd]) 153 break; 154 } 155 } 156 if (dolock) 157 rwlock_unlock(&svc_fd_lock); 158 } 159 160 /* 161 * Add a service program to the callout list. 162 * The dispatch routine will be called when a rpc request for this 163 * program number comes in. 164 */ 165 bool_t 166 svc_reg(SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers, 167 void (*dispatch)(struct svc_req *, SVCXPRT *), 168 const struct netconfig *nconf) 169 { 170 bool_t dummy; 171 struct svc_callout *prev; 172 struct svc_callout *s; 173 struct netconfig *tnconf; 174 char *netid = NULL; 175 int flag = 0; 176 177 /* VARIABLES PROTECTED BY svc_lock: s, prev, svc_head */ 178 179 if (xprt->xp_netid) { 180 netid = strdup(xprt->xp_netid); 181 flag = 1; 182 } else if (nconf && nconf->nc_netid) { 183 netid = strdup(nconf->nc_netid); 184 flag = 1; 185 } else if ((tnconf = __rpcgettp(xprt->xp_fd)) != NULL) { 186 netid = strdup(tnconf->nc_netid); 187 flag = 1; 188 freenetconfigent(tnconf); 189 } /* must have been created with svc_raw_create */ 190 if ((netid == NULL) && (flag == 1)) { 191 return (FALSE); 192 } 193 194 rwlock_wrlock(&svc_lock); 195 if ((s = svc_find(prog, vers, &prev, netid)) != NULL) { 196 if (netid) 197 free(netid); 198 if (s->sc_dispatch == dispatch) 199 goto rpcb_it; /* he is registering another xptr */ 200 rwlock_unlock(&svc_lock); 201 return (FALSE); 202 } 203 s = mem_alloc(sizeof (struct svc_callout)); 204 if (s == NULL) { 205 if (netid) 206 free(netid); 207 rwlock_unlock(&svc_lock); 208 return (FALSE); 209 } 210 211 s->sc_prog = prog; 212 s->sc_vers = vers; 213 s->sc_dispatch = dispatch; 214 s->sc_netid = netid; 215 s->sc_next = svc_head; 216 svc_head = s; 217 218 if ((xprt->xp_netid == NULL) && (flag == 1) && netid) 219 ((SVCXPRT *) xprt)->xp_netid = strdup(netid); 220 221 rpcb_it: 222 rwlock_unlock(&svc_lock); 223 /* now register the information with the local binder service */ 224 if (nconf) { 225 /*LINTED const castaway*/ 226 dummy = rpcb_set(prog, vers, (struct netconfig *) nconf, 227 &((SVCXPRT *) xprt)->xp_ltaddr); 228 return (dummy); 229 } 230 return (TRUE); 231 } 232 233 /* 234 * Remove a service program from the callout list. 235 */ 236 void 237 svc_unreg(const rpcprog_t prog, const rpcvers_t vers) 238 { 239 struct svc_callout *prev; 240 struct svc_callout *s; 241 242 /* unregister the information anyway */ 243 rpcb_unset(prog, vers, NULL); 244 rwlock_wrlock(&svc_lock); 245 while ((s = svc_find(prog, vers, &prev, NULL)) != NULL) { 246 if (prev == NULL) { 247 svc_head = s->sc_next; 248 } else { 249 prev->sc_next = s->sc_next; 250 } 251 s->sc_next = NULL; 252 if (s->sc_netid) 253 mem_free(s->sc_netid, sizeof (s->sc_netid) + 1); 254 mem_free(s, sizeof (struct svc_callout)); 255 } 256 rwlock_unlock(&svc_lock); 257 } 258 259 /* ********************** CALLOUT list related stuff ************* */ 260 261 #ifdef PORTMAP 262 /* 263 * Add a service program to the callout list. 264 * The dispatch routine will be called when a rpc request for this 265 * program number comes in. 266 */ 267 bool_t 268 svc_register(SVCXPRT *xprt, u_long prog, u_long vers, 269 void (*dispatch)(struct svc_req *, SVCXPRT *), int protocol) 270 { 271 struct svc_callout *prev; 272 struct svc_callout *s; 273 274 assert(xprt != NULL); 275 assert(dispatch != NULL); 276 277 if ((s = svc_find((rpcprog_t)prog, (rpcvers_t)vers, &prev, NULL)) != 278 NULL) { 279 if (s->sc_dispatch == dispatch) 280 goto pmap_it; /* he is registering another xptr */ 281 return (FALSE); 282 } 283 s = mem_alloc(sizeof(struct svc_callout)); 284 if (s == NULL) { 285 return (FALSE); 286 } 287 s->sc_prog = (rpcprog_t)prog; 288 s->sc_vers = (rpcvers_t)vers; 289 s->sc_dispatch = dispatch; 290 s->sc_next = svc_head; 291 svc_head = s; 292 pmap_it: 293 /* now register the information with the local binder service */ 294 if (protocol) { 295 return (pmap_set(prog, vers, protocol, xprt->xp_port)); 296 } 297 return (TRUE); 298 } 299 300 /* 301 * Remove a service program from the callout list. 302 */ 303 void 304 svc_unregister(u_long prog, u_long vers) 305 { 306 struct svc_callout *prev; 307 struct svc_callout *s; 308 309 if ((s = svc_find((rpcprog_t)prog, (rpcvers_t)vers, &prev, NULL)) == 310 NULL) 311 return; 312 if (prev == NULL) { 313 svc_head = s->sc_next; 314 } else { 315 prev->sc_next = s->sc_next; 316 } 317 s->sc_next = NULL; 318 mem_free(s, sizeof(struct svc_callout)); 319 /* now unregister the information with the local binder service */ 320 pmap_unset(prog, vers); 321 } 322 #endif /* PORTMAP */ 323 324 /* 325 * Search the callout list for a program number, return the callout 326 * struct. 327 */ 328 static struct svc_callout * 329 svc_find(rpcprog_t prog, rpcvers_t vers, struct svc_callout **prev, char *netid) 330 { 331 struct svc_callout *s, *p; 332 333 assert(prev != NULL); 334 335 p = NULL; 336 for (s = svc_head; s != NULL; s = s->sc_next) { 337 if (((s->sc_prog == prog) && (s->sc_vers == vers)) && 338 ((netid == NULL) || (s->sc_netid == NULL) || 339 (strcmp(netid, s->sc_netid) == 0))) 340 break; 341 p = s; 342 } 343 *prev = p; 344 return (s); 345 } 346 347 /* ******************* REPLY GENERATION ROUTINES ************ */ 348 349 /* 350 * Send a reply to an rpc request 351 */ 352 bool_t 353 svc_sendreply(SVCXPRT *xprt, xdrproc_t xdr_results, void *xdr_location) 354 { 355 struct rpc_msg rply; 356 357 assert(xprt != NULL); 358 359 rply.rm_direction = REPLY; 360 rply.rm_reply.rp_stat = MSG_ACCEPTED; 361 rply.acpted_rply.ar_verf = xprt->xp_verf; 362 rply.acpted_rply.ar_stat = SUCCESS; 363 rply.acpted_rply.ar_results.where = xdr_location; 364 rply.acpted_rply.ar_results.proc = xdr_results; 365 return (SVC_REPLY(xprt, &rply)); 366 } 367 368 /* 369 * No procedure error reply 370 */ 371 void 372 svcerr_noproc(SVCXPRT *xprt) 373 { 374 struct rpc_msg rply; 375 376 assert(xprt != NULL); 377 378 rply.rm_direction = REPLY; 379 rply.rm_reply.rp_stat = MSG_ACCEPTED; 380 rply.acpted_rply.ar_verf = xprt->xp_verf; 381 rply.acpted_rply.ar_stat = PROC_UNAVAIL; 382 SVC_REPLY(xprt, &rply); 383 } 384 385 /* 386 * Can't decode args error reply 387 */ 388 void 389 svcerr_decode(SVCXPRT *xprt) 390 { 391 struct rpc_msg rply; 392 393 assert(xprt != NULL); 394 395 rply.rm_direction = REPLY; 396 rply.rm_reply.rp_stat = MSG_ACCEPTED; 397 rply.acpted_rply.ar_verf = xprt->xp_verf; 398 rply.acpted_rply.ar_stat = GARBAGE_ARGS; 399 SVC_REPLY(xprt, &rply); 400 } 401 402 /* 403 * Some system error 404 */ 405 void 406 svcerr_systemerr(SVCXPRT *xprt) 407 { 408 struct rpc_msg rply; 409 410 assert(xprt != NULL); 411 412 rply.rm_direction = REPLY; 413 rply.rm_reply.rp_stat = MSG_ACCEPTED; 414 rply.acpted_rply.ar_verf = xprt->xp_verf; 415 rply.acpted_rply.ar_stat = SYSTEM_ERR; 416 SVC_REPLY(xprt, &rply); 417 } 418 419 /* 420 * Authentication error reply 421 */ 422 void 423 svcerr_auth(SVCXPRT *xprt, enum auth_stat why) 424 { 425 struct rpc_msg rply; 426 427 assert(xprt != NULL); 428 429 rply.rm_direction = REPLY; 430 rply.rm_reply.rp_stat = MSG_DENIED; 431 rply.rjcted_rply.rj_stat = AUTH_ERROR; 432 rply.rjcted_rply.rj_why = why; 433 SVC_REPLY(xprt, &rply); 434 } 435 436 /* 437 * Auth too weak error reply 438 */ 439 void 440 svcerr_weakauth(SVCXPRT *xprt) 441 { 442 443 assert(xprt != NULL); 444 445 svcerr_auth(xprt, AUTH_TOOWEAK); 446 } 447 448 /* 449 * Program unavailable error reply 450 */ 451 void 452 svcerr_noprog(SVCXPRT *xprt) 453 { 454 struct rpc_msg rply; 455 456 assert(xprt != NULL); 457 458 rply.rm_direction = REPLY; 459 rply.rm_reply.rp_stat = MSG_ACCEPTED; 460 rply.acpted_rply.ar_verf = xprt->xp_verf; 461 rply.acpted_rply.ar_stat = PROG_UNAVAIL; 462 SVC_REPLY(xprt, &rply); 463 } 464 465 /* 466 * Program version mismatch error reply 467 */ 468 void 469 svcerr_progvers(SVCXPRT *xprt, rpcvers_t low_vers, rpcvers_t high_vers) 470 { 471 struct rpc_msg rply; 472 473 assert(xprt != NULL); 474 475 rply.rm_direction = REPLY; 476 rply.rm_reply.rp_stat = MSG_ACCEPTED; 477 rply.acpted_rply.ar_verf = xprt->xp_verf; 478 rply.acpted_rply.ar_stat = PROG_MISMATCH; 479 rply.acpted_rply.ar_vers.low = (u_int32_t)low_vers; 480 rply.acpted_rply.ar_vers.high = (u_int32_t)high_vers; 481 SVC_REPLY(xprt, &rply); 482 } 483 484 /* ******************* SERVER INPUT STUFF ******************* */ 485 486 /* 487 * Get server side input from some transport. 488 * 489 * Statement of authentication parameters management: 490 * This function owns and manages all authentication parameters, specifically 491 * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and 492 * the "cooked" credentials (rqst->rq_clntcred). 493 * However, this function does not know the structure of the cooked 494 * credentials, so it make the following assumptions: 495 * a) the structure is contiguous (no pointers), and 496 * b) the cred structure size does not exceed RQCRED_SIZE bytes. 497 * In all events, all three parameters are freed upon exit from this routine. 498 * The storage is trivially management on the call stack in user land, but 499 * is mallocated in kernel land. 500 */ 501 502 void 503 svc_getreq(int rdfds) 504 { 505 fd_set readfds; 506 507 FD_ZERO(&readfds); 508 readfds.fds_bits[0] = rdfds; 509 svc_getreqset(&readfds); 510 } 511 512 void 513 svc_getreqset(fd_set *readfds) 514 { 515 int bit, fd; 516 fd_mask mask, *maskp; 517 int sock; 518 519 assert(readfds != NULL); 520 521 maskp = readfds->fds_bits; 522 for (sock = 0; sock < FD_SETSIZE; sock += NFDBITS) { 523 for (mask = *maskp++; (bit = ffs(mask)) != 0; 524 mask ^= (1 << (bit - 1))) { 525 /* sock has input waiting */ 526 fd = sock + bit - 1; 527 svc_getreq_common(fd); 528 } 529 } 530 } 531 532 void 533 svc_getreq_common(int fd) 534 { 535 SVCXPRT *xprt; 536 struct svc_req r; 537 struct rpc_msg msg; 538 int prog_found; 539 rpcvers_t low_vers; 540 rpcvers_t high_vers; 541 enum xprt_stat stat; 542 char cred_area[2*MAX_AUTH_BYTES + RQCRED_SIZE]; 543 544 msg.rm_call.cb_cred.oa_base = cred_area; 545 msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]); 546 r.rq_clntcred = &(cred_area[2*MAX_AUTH_BYTES]); 547 548 rwlock_rdlock(&svc_fd_lock); 549 xprt = __svc_xports[fd]; 550 rwlock_unlock(&svc_fd_lock); 551 if (xprt == NULL) 552 /* But do we control sock? */ 553 return; 554 /* now receive msgs from xprtprt (support batch calls) */ 555 do { 556 if (SVC_RECV(xprt, &msg)) { 557 558 /* now find the exported program and call it */ 559 struct svc_callout *s; 560 enum auth_stat why; 561 562 r.rq_xprt = xprt; 563 r.rq_prog = msg.rm_call.cb_prog; 564 r.rq_vers = msg.rm_call.cb_vers; 565 r.rq_proc = msg.rm_call.cb_proc; 566 r.rq_cred = msg.rm_call.cb_cred; 567 /* first authenticate the message */ 568 if ((why = _authenticate(&r, &msg)) != AUTH_OK) { 569 svcerr_auth(xprt, why); 570 goto call_done; 571 } 572 /* now match message with a registered service*/ 573 prog_found = FALSE; 574 low_vers = (rpcvers_t) -1L; 575 high_vers = (rpcvers_t) 0L; 576 for (s = svc_head; s != NULL; s = s->sc_next) { 577 if (s->sc_prog == r.rq_prog) { 578 if (s->sc_vers == r.rq_vers) { 579 (*s->sc_dispatch)(&r, xprt); 580 goto call_done; 581 } /* found correct version */ 582 prog_found = TRUE; 583 if (s->sc_vers < low_vers) 584 low_vers = s->sc_vers; 585 if (s->sc_vers > high_vers) 586 high_vers = s->sc_vers; 587 } /* found correct program */ 588 } 589 /* 590 * if we got here, the program or version 591 * is not served ... 592 */ 593 if (prog_found) 594 svcerr_progvers(xprt, low_vers, high_vers); 595 else 596 svcerr_noprog(xprt); 597 /* Fall through to ... */ 598 } 599 /* 600 * Check if the xprt has been disconnected in a 601 * recursive call in the service dispatch routine. 602 * If so, then break. 603 */ 604 rwlock_rdlock(&svc_fd_lock); 605 if (xprt != __svc_xports[fd]) { 606 rwlock_unlock(&svc_fd_lock); 607 break; 608 } 609 rwlock_unlock(&svc_fd_lock); 610 call_done: 611 if ((stat = SVC_STAT(xprt)) == XPRT_DIED){ 612 SVC_DESTROY(xprt); 613 break; 614 } 615 } while (stat == XPRT_MOREREQS); 616 } 617 618 619 void 620 svc_getreq_poll(struct pollfd *pfdp, int pollretval) 621 { 622 int i; 623 int fds_found; 624 625 for (i = fds_found = 0; fds_found < pollretval; i++) { 626 struct pollfd *p = &pfdp[i]; 627 628 if (p->revents) { 629 /* fd has input waiting */ 630 fds_found++; 631 /* 632 * We assume that this function is only called 633 * via someone _select()ing from svc_fdset or 634 * _poll()ing from svc_pollset[]. Thus it's safe 635 * to handle the POLLNVAL event by simply turning 636 * the corresponding bit off in svc_fdset. The 637 * svc_pollset[] array is derived from svc_fdset 638 * and so will also be updated eventually. 639 * 640 * XXX Should we do an xprt_unregister() instead? 641 */ 642 if (p->revents & POLLNVAL) { 643 rwlock_wrlock(&svc_fd_lock); 644 FD_CLR(p->fd, &svc_fdset); 645 rwlock_unlock(&svc_fd_lock); 646 } else 647 svc_getreq_common(p->fd); 648 } 649 } 650 } 651 652 bool_t 653 rpc_control(int what, void *arg) 654 { 655 int val; 656 657 switch (what) { 658 case RPC_SVC_CONNMAXREC_SET: 659 val = *(int *)arg; 660 if (val <= 0) 661 return FALSE; 662 __svc_maxrec = val; 663 return TRUE; 664 case RPC_SVC_CONNMAXREC_GET: 665 *(int *)arg = __svc_maxrec; 666 return TRUE; 667 default: 668 break; 669 } 670 return FALSE; 671 } 672