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 static struct svc_callout *svc_find(rpcprog_t, rpcvers_t, 85 struct svc_callout **, char *); 86 static void __xprt_do_unregister (SVCXPRT *xprt, bool_t dolock); 87 88 /* *************** SVCXPRT related stuff **************** */ 89 90 /* 91 * Activate a transport handle. 92 */ 93 void 94 xprt_register(SVCXPRT *xprt) 95 { 96 int sock; 97 98 assert(xprt != NULL); 99 100 sock = xprt->xp_fd; 101 102 rwlock_wrlock(&svc_fd_lock); 103 if (__svc_xports == NULL) { 104 __svc_xports = (SVCXPRT **) 105 mem_alloc(FD_SETSIZE * sizeof(SVCXPRT *)); 106 if (__svc_xports == NULL) 107 return; 108 memset(__svc_xports, '\0', FD_SETSIZE * sizeof(SVCXPRT *)); 109 } 110 if (sock < FD_SETSIZE) { 111 __svc_xports[sock] = xprt; 112 FD_SET(sock, &svc_fdset); 113 svc_maxfd = max(svc_maxfd, sock); 114 } 115 rwlock_unlock(&svc_fd_lock); 116 } 117 118 void 119 xprt_unregister(SVCXPRT *xprt) 120 { 121 __xprt_do_unregister(xprt, TRUE); 122 } 123 124 void 125 __xprt_unregister_unlocked(SVCXPRT *xprt) 126 { 127 __xprt_do_unregister(xprt, FALSE); 128 } 129 130 /* 131 * De-activate a transport handle. 132 */ 133 static void 134 __xprt_do_unregister(SVCXPRT *xprt, bool_t dolock) 135 { 136 int sock; 137 138 assert(xprt != NULL); 139 140 sock = xprt->xp_fd; 141 142 if (dolock) 143 rwlock_wrlock(&svc_fd_lock); 144 if ((sock < FD_SETSIZE) && (__svc_xports[sock] == xprt)) { 145 __svc_xports[sock] = NULL; 146 FD_CLR(sock, &svc_fdset); 147 if (sock >= svc_maxfd) { 148 for (svc_maxfd--; svc_maxfd>=0; svc_maxfd--) 149 if (__svc_xports[svc_maxfd]) 150 break; 151 } 152 } 153 if (dolock) 154 rwlock_unlock(&svc_fd_lock); 155 } 156 157 /* 158 * Add a service program to the callout list. 159 * The dispatch routine will be called when a rpc request for this 160 * program number comes in. 161 */ 162 bool_t 163 svc_reg(SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers, 164 void (*dispatch)(struct svc_req *, SVCXPRT *), 165 const struct netconfig *nconf) 166 { 167 bool_t dummy; 168 struct svc_callout *prev; 169 struct svc_callout *s; 170 struct netconfig *tnconf; 171 char *netid = NULL; 172 int flag = 0; 173 174 /* VARIABLES PROTECTED BY svc_lock: s, prev, svc_head */ 175 176 if (xprt->xp_netid) { 177 netid = strdup(xprt->xp_netid); 178 flag = 1; 179 } else if (nconf && nconf->nc_netid) { 180 netid = strdup(nconf->nc_netid); 181 flag = 1; 182 } else if ((tnconf = __rpcgettp(xprt->xp_fd)) != NULL) { 183 netid = strdup(tnconf->nc_netid); 184 flag = 1; 185 freenetconfigent(tnconf); 186 } /* must have been created with svc_raw_create */ 187 if ((netid == NULL) && (flag == 1)) { 188 return (FALSE); 189 } 190 191 rwlock_wrlock(&svc_lock); 192 if ((s = svc_find(prog, vers, &prev, netid)) != NULL) { 193 if (netid) 194 free(netid); 195 if (s->sc_dispatch == dispatch) 196 goto rpcb_it; /* he is registering another xptr */ 197 rwlock_unlock(&svc_lock); 198 return (FALSE); 199 } 200 s = mem_alloc(sizeof (struct svc_callout)); 201 if (s == NULL) { 202 if (netid) 203 free(netid); 204 rwlock_unlock(&svc_lock); 205 return (FALSE); 206 } 207 208 s->sc_prog = prog; 209 s->sc_vers = vers; 210 s->sc_dispatch = dispatch; 211 s->sc_netid = netid; 212 s->sc_next = svc_head; 213 svc_head = s; 214 215 if ((xprt->xp_netid == NULL) && (flag == 1) && netid) 216 ((SVCXPRT *) xprt)->xp_netid = strdup(netid); 217 218 rpcb_it: 219 rwlock_unlock(&svc_lock); 220 /* now register the information with the local binder service */ 221 if (nconf) { 222 /*LINTED const castaway*/ 223 dummy = rpcb_set(prog, vers, (struct netconfig *) nconf, 224 &((SVCXPRT *) xprt)->xp_ltaddr); 225 return (dummy); 226 } 227 return (TRUE); 228 } 229 230 /* 231 * Remove a service program from the callout list. 232 */ 233 void 234 svc_unreg(const rpcprog_t prog, const rpcvers_t vers) 235 { 236 struct svc_callout *prev; 237 struct svc_callout *s; 238 239 /* unregister the information anyway */ 240 rpcb_unset(prog, vers, NULL); 241 rwlock_wrlock(&svc_lock); 242 while ((s = svc_find(prog, vers, &prev, NULL)) != NULL) { 243 if (prev == NULL) { 244 svc_head = s->sc_next; 245 } else { 246 prev->sc_next = s->sc_next; 247 } 248 s->sc_next = NULL; 249 if (s->sc_netid) 250 mem_free(s->sc_netid, sizeof (s->sc_netid) + 1); 251 mem_free(s, sizeof (struct svc_callout)); 252 } 253 rwlock_unlock(&svc_lock); 254 } 255 256 /* ********************** CALLOUT list related stuff ************* */ 257 258 #ifdef PORTMAP 259 /* 260 * Add a service program to the callout list. 261 * The dispatch routine will be called when a rpc request for this 262 * program number comes in. 263 */ 264 bool_t 265 svc_register(SVCXPRT *xprt, u_long prog, u_long vers, 266 void (*dispatch)(struct svc_req *, SVCXPRT *), int protocol) 267 { 268 struct svc_callout *prev; 269 struct svc_callout *s; 270 271 assert(xprt != NULL); 272 assert(dispatch != NULL); 273 274 if ((s = svc_find((rpcprog_t)prog, (rpcvers_t)vers, &prev, NULL)) != 275 NULL) { 276 if (s->sc_dispatch == dispatch) 277 goto pmap_it; /* he is registering another xptr */ 278 return (FALSE); 279 } 280 s = mem_alloc(sizeof(struct svc_callout)); 281 if (s == NULL) { 282 return (FALSE); 283 } 284 s->sc_prog = (rpcprog_t)prog; 285 s->sc_vers = (rpcvers_t)vers; 286 s->sc_dispatch = dispatch; 287 s->sc_next = svc_head; 288 svc_head = s; 289 pmap_it: 290 /* now register the information with the local binder service */ 291 if (protocol) { 292 return (pmap_set(prog, vers, protocol, xprt->xp_port)); 293 } 294 return (TRUE); 295 } 296 297 /* 298 * Remove a service program from the callout list. 299 */ 300 void 301 svc_unregister(u_long prog, u_long vers) 302 { 303 struct svc_callout *prev; 304 struct svc_callout *s; 305 306 if ((s = svc_find((rpcprog_t)prog, (rpcvers_t)vers, &prev, NULL)) == 307 NULL) 308 return; 309 if (prev == NULL) { 310 svc_head = s->sc_next; 311 } else { 312 prev->sc_next = s->sc_next; 313 } 314 s->sc_next = NULL; 315 mem_free(s, sizeof(struct svc_callout)); 316 /* now unregister the information with the local binder service */ 317 pmap_unset(prog, vers); 318 } 319 #endif /* PORTMAP */ 320 321 /* 322 * Search the callout list for a program number, return the callout 323 * struct. 324 */ 325 static struct svc_callout * 326 svc_find(rpcprog_t prog, rpcvers_t vers, struct svc_callout **prev, char *netid) 327 { 328 struct svc_callout *s, *p; 329 330 assert(prev != NULL); 331 332 p = NULL; 333 for (s = svc_head; s != NULL; s = s->sc_next) { 334 if (((s->sc_prog == prog) && (s->sc_vers == vers)) && 335 ((netid == NULL) || (s->sc_netid == NULL) || 336 (strcmp(netid, s->sc_netid) == 0))) 337 break; 338 p = s; 339 } 340 *prev = p; 341 return (s); 342 } 343 344 /* ******************* REPLY GENERATION ROUTINES ************ */ 345 346 /* 347 * Send a reply to an rpc request 348 */ 349 bool_t 350 svc_sendreply(SVCXPRT *xprt, xdrproc_t xdr_results, void *xdr_location) 351 { 352 struct rpc_msg rply; 353 354 assert(xprt != NULL); 355 356 rply.rm_direction = REPLY; 357 rply.rm_reply.rp_stat = MSG_ACCEPTED; 358 rply.acpted_rply.ar_verf = xprt->xp_verf; 359 rply.acpted_rply.ar_stat = SUCCESS; 360 rply.acpted_rply.ar_results.where = xdr_location; 361 rply.acpted_rply.ar_results.proc = xdr_results; 362 return (SVC_REPLY(xprt, &rply)); 363 } 364 365 /* 366 * No procedure error reply 367 */ 368 void 369 svcerr_noproc(SVCXPRT *xprt) 370 { 371 struct rpc_msg rply; 372 373 assert(xprt != NULL); 374 375 rply.rm_direction = REPLY; 376 rply.rm_reply.rp_stat = MSG_ACCEPTED; 377 rply.acpted_rply.ar_verf = xprt->xp_verf; 378 rply.acpted_rply.ar_stat = PROC_UNAVAIL; 379 SVC_REPLY(xprt, &rply); 380 } 381 382 /* 383 * Can't decode args error reply 384 */ 385 void 386 svcerr_decode(SVCXPRT *xprt) 387 { 388 struct rpc_msg rply; 389 390 assert(xprt != NULL); 391 392 rply.rm_direction = REPLY; 393 rply.rm_reply.rp_stat = MSG_ACCEPTED; 394 rply.acpted_rply.ar_verf = xprt->xp_verf; 395 rply.acpted_rply.ar_stat = GARBAGE_ARGS; 396 SVC_REPLY(xprt, &rply); 397 } 398 399 /* 400 * Some system error 401 */ 402 void 403 svcerr_systemerr(SVCXPRT *xprt) 404 { 405 struct rpc_msg rply; 406 407 assert(xprt != NULL); 408 409 rply.rm_direction = REPLY; 410 rply.rm_reply.rp_stat = MSG_ACCEPTED; 411 rply.acpted_rply.ar_verf = xprt->xp_verf; 412 rply.acpted_rply.ar_stat = SYSTEM_ERR; 413 SVC_REPLY(xprt, &rply); 414 } 415 416 /* 417 * Authentication error reply 418 */ 419 void 420 svcerr_auth(SVCXPRT *xprt, enum auth_stat why) 421 { 422 struct rpc_msg rply; 423 424 assert(xprt != NULL); 425 426 rply.rm_direction = REPLY; 427 rply.rm_reply.rp_stat = MSG_DENIED; 428 rply.rjcted_rply.rj_stat = AUTH_ERROR; 429 rply.rjcted_rply.rj_why = why; 430 SVC_REPLY(xprt, &rply); 431 } 432 433 /* 434 * Auth too weak error reply 435 */ 436 void 437 svcerr_weakauth(SVCXPRT *xprt) 438 { 439 440 assert(xprt != NULL); 441 442 svcerr_auth(xprt, AUTH_TOOWEAK); 443 } 444 445 /* 446 * Program unavailable error reply 447 */ 448 void 449 svcerr_noprog(SVCXPRT *xprt) 450 { 451 struct rpc_msg rply; 452 453 assert(xprt != NULL); 454 455 rply.rm_direction = REPLY; 456 rply.rm_reply.rp_stat = MSG_ACCEPTED; 457 rply.acpted_rply.ar_verf = xprt->xp_verf; 458 rply.acpted_rply.ar_stat = PROG_UNAVAIL; 459 SVC_REPLY(xprt, &rply); 460 } 461 462 /* 463 * Program version mismatch error reply 464 */ 465 void 466 svcerr_progvers(SVCXPRT *xprt, rpcvers_t low_vers, rpcvers_t high_vers) 467 { 468 struct rpc_msg rply; 469 470 assert(xprt != NULL); 471 472 rply.rm_direction = REPLY; 473 rply.rm_reply.rp_stat = MSG_ACCEPTED; 474 rply.acpted_rply.ar_verf = xprt->xp_verf; 475 rply.acpted_rply.ar_stat = PROG_MISMATCH; 476 rply.acpted_rply.ar_vers.low = (u_int32_t)low_vers; 477 rply.acpted_rply.ar_vers.high = (u_int32_t)high_vers; 478 SVC_REPLY(xprt, &rply); 479 } 480 481 /* ******************* SERVER INPUT STUFF ******************* */ 482 483 /* 484 * Get server side input from some transport. 485 * 486 * Statement of authentication parameters management: 487 * This function owns and manages all authentication parameters, specifically 488 * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and 489 * the "cooked" credentials (rqst->rq_clntcred). 490 * However, this function does not know the structure of the cooked 491 * credentials, so it make the following assumptions: 492 * a) the structure is contiguous (no pointers), and 493 * b) the cred structure size does not exceed RQCRED_SIZE bytes. 494 * In all events, all three parameters are freed upon exit from this routine. 495 * The storage is trivially management on the call stack in user land, but 496 * is mallocated in kernel land. 497 */ 498 499 void 500 svc_getreq(int rdfds) 501 { 502 fd_set readfds; 503 504 FD_ZERO(&readfds); 505 readfds.fds_bits[0] = rdfds; 506 svc_getreqset(&readfds); 507 } 508 509 void 510 svc_getreqset(fd_set *readfds) 511 { 512 int bit, fd; 513 fd_mask mask, *maskp; 514 int sock; 515 516 assert(readfds != NULL); 517 518 maskp = readfds->fds_bits; 519 for (sock = 0; sock < FD_SETSIZE; sock += NFDBITS) { 520 for (mask = *maskp++; (bit = ffs(mask)) != 0; 521 mask ^= (1 << (bit - 1))) { 522 /* sock has input waiting */ 523 fd = sock + bit - 1; 524 svc_getreq_common(fd); 525 } 526 } 527 } 528 529 void 530 svc_getreq_common(int fd) 531 { 532 SVCXPRT *xprt; 533 struct svc_req r; 534 struct rpc_msg msg; 535 int prog_found; 536 rpcvers_t low_vers; 537 rpcvers_t high_vers; 538 enum xprt_stat stat; 539 char cred_area[2*MAX_AUTH_BYTES + RQCRED_SIZE]; 540 541 msg.rm_call.cb_cred.oa_base = cred_area; 542 msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]); 543 r.rq_clntcred = &(cred_area[2*MAX_AUTH_BYTES]); 544 545 rwlock_rdlock(&svc_fd_lock); 546 xprt = __svc_xports[fd]; 547 rwlock_unlock(&svc_fd_lock); 548 if (xprt == NULL) 549 /* But do we control sock? */ 550 return; 551 /* now receive msgs from xprtprt (support batch calls) */ 552 do { 553 if (SVC_RECV(xprt, &msg)) { 554 555 /* now find the exported program and call it */ 556 struct svc_callout *s; 557 enum auth_stat why; 558 559 r.rq_xprt = xprt; 560 r.rq_prog = msg.rm_call.cb_prog; 561 r.rq_vers = msg.rm_call.cb_vers; 562 r.rq_proc = msg.rm_call.cb_proc; 563 r.rq_cred = msg.rm_call.cb_cred; 564 /* first authenticate the message */ 565 if ((why = _authenticate(&r, &msg)) != AUTH_OK) { 566 svcerr_auth(xprt, why); 567 goto call_done; 568 } 569 /* now match message with a registered service*/ 570 prog_found = FALSE; 571 low_vers = (rpcvers_t) -1L; 572 high_vers = (rpcvers_t) 0L; 573 for (s = svc_head; s != NULL; s = s->sc_next) { 574 if (s->sc_prog == r.rq_prog) { 575 if (s->sc_vers == r.rq_vers) { 576 (*s->sc_dispatch)(&r, xprt); 577 goto call_done; 578 } /* found correct version */ 579 prog_found = TRUE; 580 if (s->sc_vers < low_vers) 581 low_vers = s->sc_vers; 582 if (s->sc_vers > high_vers) 583 high_vers = s->sc_vers; 584 } /* found correct program */ 585 } 586 /* 587 * if we got here, the program or version 588 * is not served ... 589 */ 590 if (prog_found) 591 svcerr_progvers(xprt, low_vers, high_vers); 592 else 593 svcerr_noprog(xprt); 594 /* Fall through to ... */ 595 } 596 /* 597 * Check if the xprt has been disconnected in a 598 * recursive call in the service dispatch routine. 599 * If so, then break. 600 */ 601 rwlock_rdlock(&svc_fd_lock); 602 if (xprt != __svc_xports[fd]) { 603 rwlock_unlock(&svc_fd_lock); 604 break; 605 } 606 rwlock_unlock(&svc_fd_lock); 607 call_done: 608 if ((stat = SVC_STAT(xprt)) == XPRT_DIED){ 609 SVC_DESTROY(xprt); 610 break; 611 } 612 } while (stat == XPRT_MOREREQS); 613 } 614 615 616 void 617 svc_getreq_poll(struct pollfd *pfdp, int pollretval) 618 { 619 int i; 620 int fds_found; 621 622 for (i = fds_found = 0; fds_found < pollretval; i++) { 623 struct pollfd *p = &pfdp[i]; 624 625 if (p->revents) { 626 /* fd has input waiting */ 627 fds_found++; 628 /* 629 * We assume that this function is only called 630 * via someone _select()ing from svc_fdset or 631 * _poll()ing from svc_pollset[]. Thus it's safe 632 * to handle the POLLNVAL event by simply turning 633 * the corresponding bit off in svc_fdset. The 634 * svc_pollset[] array is derived from svc_fdset 635 * and so will also be updated eventually. 636 * 637 * XXX Should we do an xprt_unregister() instead? 638 */ 639 if (p->revents & POLLNVAL) { 640 rwlock_wrlock(&svc_fd_lock); 641 FD_CLR(p->fd, &svc_fdset); 642 rwlock_unlock(&svc_fd_lock); 643 } else 644 svc_getreq_common(p->fd); 645 } 646 } 647 } 648 649 bool_t 650 rpc_control(int what, void *arg) 651 { 652 int val; 653 654 switch (what) { 655 case RPC_SVC_CONNMAXREC_SET: 656 val = *(int *)arg; 657 if (val <= 0) 658 return FALSE; 659 __svc_maxrec = val; 660 return TRUE; 661 case RPC_SVC_CONNMAXREC_GET: 662 *(int *)arg = __svc_maxrec; 663 return TRUE; 664 default: 665 break; 666 } 667 return FALSE; 668 } 669