svc.c revision 177633
1/* $NetBSD: svc.c,v 1.21 2000/07/06 03:10:35 christos Exp $ */ 2 3/* 4 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 5 * unrestricted use provided that this legend is included on all tape 6 * media and as a part of the software program in whole or part. Users 7 * may copy or modify Sun RPC without charge, but are not authorized 8 * to license or distribute it to anyone else except as part of a product or 9 * program developed by the user. 10 * 11 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 12 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 13 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 14 * 15 * Sun RPC is provided with no support and without any obligation on the 16 * part of Sun Microsystems, Inc. to assist in its use, correction, 17 * modification or enhancement. 18 * 19 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 20 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 21 * OR ANY PART THEREOF. 22 * 23 * In no event will Sun Microsystems, Inc. be liable for any lost revenue 24 * or profits or other special, indirect and consequential damages, even if 25 * Sun has been advised of the possibility of such damages. 26 * 27 * Sun Microsystems, Inc. 28 * 2550 Garcia Avenue 29 * Mountain View, California 94043 30 */ 31 32#if defined(LIBC_SCCS) && !defined(lint) 33static char *sccsid2 = "@(#)svc.c 1.44 88/02/08 Copyr 1984 Sun Micro"; 34static char *sccsid = "@(#)svc.c 2.4 88/08/11 4.0 RPCSRC"; 35#endif 36#include <sys/cdefs.h> 37__FBSDID("$FreeBSD: head/sys/rpc/svc.c 177633 2008-03-26 15:23:12Z dfr $"); 38 39/* 40 * svc.c, Server-side remote procedure call interface. 41 * 42 * There are two sets of procedures here. The xprt routines are 43 * for handling transport handles. The svc routines handle the 44 * list of service routines. 45 * 46 * Copyright (C) 1984, Sun Microsystems, Inc. 47 */ 48 49#include <sys/param.h> 50#include <sys/lock.h> 51#include <sys/kernel.h> 52#include <sys/malloc.h> 53#include <sys/mutex.h> 54#include <sys/queue.h> 55#include <sys/systm.h> 56#include <sys/ucred.h> 57 58#include <rpc/rpc.h> 59#include <rpc/rpcb_clnt.h> 60 61#include "rpc_com.h" 62 63#define SVC_VERSQUIET 0x0001 /* keep quiet about vers mismatch */ 64#define version_keepquiet(xp) ((u_long)(xp)->xp_p3 & SVC_VERSQUIET) 65 66static struct svc_callout *svc_find(SVCPOOL *pool, rpcprog_t, rpcvers_t, 67 char *); 68static void __xprt_do_unregister (SVCXPRT *xprt, bool_t dolock); 69 70/* *************** SVCXPRT related stuff **************** */ 71 72SVCPOOL* 73svcpool_create(void) 74{ 75 SVCPOOL *pool; 76 77 pool = malloc(sizeof(SVCPOOL), M_RPC, M_WAITOK|M_ZERO); 78 79 mtx_init(&pool->sp_lock, "sp_lock", NULL, MTX_DEF); 80 TAILQ_INIT(&pool->sp_xlist); 81 TAILQ_INIT(&pool->sp_active); 82 TAILQ_INIT(&pool->sp_callouts); 83 84 return pool; 85} 86 87void 88svcpool_destroy(SVCPOOL *pool) 89{ 90 SVCXPRT *xprt; 91 struct svc_callout *s; 92 93 mtx_lock(&pool->sp_lock); 94 95 while (TAILQ_FIRST(&pool->sp_xlist)) { 96 xprt = TAILQ_FIRST(&pool->sp_xlist); 97 mtx_unlock(&pool->sp_lock); 98 SVC_DESTROY(xprt); 99 mtx_lock(&pool->sp_lock); 100 } 101 102 while (TAILQ_FIRST(&pool->sp_callouts)) { 103 s = TAILQ_FIRST(&pool->sp_callouts); 104 mtx_unlock(&pool->sp_lock); 105 svc_unreg(pool, s->sc_prog, s->sc_vers); 106 mtx_lock(&pool->sp_lock); 107 } 108 109 mtx_destroy(&pool->sp_lock); 110 free(pool, M_RPC); 111} 112 113/* 114 * Activate a transport handle. 115 */ 116void 117xprt_register(SVCXPRT *xprt) 118{ 119 SVCPOOL *pool = xprt->xp_pool; 120 121 mtx_lock(&pool->sp_lock); 122 xprt->xp_registered = TRUE; 123 xprt->xp_active = FALSE; 124 TAILQ_INSERT_TAIL(&pool->sp_xlist, xprt, xp_link); 125 mtx_unlock(&pool->sp_lock); 126} 127 128void 129xprt_unregister(SVCXPRT *xprt) 130{ 131 __xprt_do_unregister(xprt, TRUE); 132} 133 134void 135__xprt_unregister_unlocked(SVCXPRT *xprt) 136{ 137 __xprt_do_unregister(xprt, FALSE); 138} 139 140/* 141 * De-activate a transport handle. 142 */ 143static void 144__xprt_do_unregister(SVCXPRT *xprt, bool_t dolock) 145{ 146 SVCPOOL *pool = xprt->xp_pool; 147 148 //__svc_generic_cleanup(xprt); 149 150 if (dolock) 151 mtx_lock(&pool->sp_lock); 152 153 if (xprt->xp_active) { 154 TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink); 155 xprt->xp_active = FALSE; 156 } 157 TAILQ_REMOVE(&pool->sp_xlist, xprt, xp_link); 158 xprt->xp_registered = FALSE; 159 160 if (dolock) 161 mtx_unlock(&pool->sp_lock); 162} 163 164void 165xprt_active(SVCXPRT *xprt) 166{ 167 SVCPOOL *pool = xprt->xp_pool; 168 169 mtx_lock(&pool->sp_lock); 170 171 if (!xprt->xp_active) { 172 TAILQ_INSERT_TAIL(&pool->sp_active, xprt, xp_alink); 173 xprt->xp_active = TRUE; 174 } 175 wakeup(&pool->sp_active); 176 177 mtx_unlock(&pool->sp_lock); 178} 179 180void 181xprt_inactive(SVCXPRT *xprt) 182{ 183 SVCPOOL *pool = xprt->xp_pool; 184 185 mtx_lock(&pool->sp_lock); 186 187 if (xprt->xp_active) { 188 TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink); 189 xprt->xp_active = FALSE; 190 } 191 wakeup(&pool->sp_active); 192 193 mtx_unlock(&pool->sp_lock); 194} 195 196/* 197 * Add a service program to the callout list. 198 * The dispatch routine will be called when a rpc request for this 199 * program number comes in. 200 */ 201bool_t 202svc_reg(SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers, 203 void (*dispatch)(struct svc_req *, SVCXPRT *), 204 const struct netconfig *nconf) 205{ 206 SVCPOOL *pool = xprt->xp_pool; 207 struct svc_callout *s; 208 char *netid = NULL; 209 int flag = 0; 210 211/* VARIABLES PROTECTED BY svc_lock: s, svc_head */ 212 213 if (xprt->xp_netid) { 214 netid = strdup(xprt->xp_netid, M_RPC); 215 flag = 1; 216 } else if (nconf && nconf->nc_netid) { 217 netid = strdup(nconf->nc_netid, M_RPC); 218 flag = 1; 219 } /* must have been created with svc_raw_create */ 220 if ((netid == NULL) && (flag == 1)) { 221 return (FALSE); 222 } 223 224 mtx_lock(&pool->sp_lock); 225 if ((s = svc_find(pool, prog, vers, netid)) != NULL) { 226 if (netid) 227 free(netid, M_RPC); 228 if (s->sc_dispatch == dispatch) 229 goto rpcb_it; /* he is registering another xptr */ 230 mtx_unlock(&pool->sp_lock); 231 return (FALSE); 232 } 233 s = malloc(sizeof (struct svc_callout), M_RPC, M_NOWAIT); 234 if (s == NULL) { 235 if (netid) 236 free(netid, M_RPC); 237 mtx_unlock(&pool->sp_lock); 238 return (FALSE); 239 } 240 241 s->sc_prog = prog; 242 s->sc_vers = vers; 243 s->sc_dispatch = dispatch; 244 s->sc_netid = netid; 245 TAILQ_INSERT_TAIL(&pool->sp_callouts, s, sc_link); 246 247 if ((xprt->xp_netid == NULL) && (flag == 1) && netid) 248 ((SVCXPRT *) xprt)->xp_netid = strdup(netid, M_RPC); 249 250rpcb_it: 251 mtx_unlock(&pool->sp_lock); 252 /* now register the information with the local binder service */ 253 if (nconf) { 254 bool_t dummy; 255 struct netconfig tnc; 256 tnc = *nconf; 257 dummy = rpcb_set(prog, vers, &tnc, 258 &((SVCXPRT *) xprt)->xp_ltaddr); 259 return (dummy); 260 } 261 return (TRUE); 262} 263 264/* 265 * Remove a service program from the callout list. 266 */ 267void 268svc_unreg(SVCPOOL *pool, const rpcprog_t prog, const rpcvers_t vers) 269{ 270 struct svc_callout *s; 271 272 /* unregister the information anyway */ 273 (void) rpcb_unset(prog, vers, NULL); 274 mtx_lock(&pool->sp_lock); 275 while ((s = svc_find(pool, prog, vers, NULL)) != NULL) { 276 TAILQ_REMOVE(&pool->sp_callouts, s, sc_link); 277 if (s->sc_netid) 278 mem_free(s->sc_netid, sizeof (s->sc_netid) + 1); 279 mem_free(s, sizeof (struct svc_callout)); 280 } 281 mtx_unlock(&pool->sp_lock); 282} 283 284/* ********************** CALLOUT list related stuff ************* */ 285 286/* 287 * Search the callout list for a program number, return the callout 288 * struct. 289 */ 290static struct svc_callout * 291svc_find(SVCPOOL *pool, rpcprog_t prog, rpcvers_t vers, char *netid) 292{ 293 struct svc_callout *s; 294 295 mtx_assert(&pool->sp_lock, MA_OWNED); 296 TAILQ_FOREACH(s, &pool->sp_callouts, sc_link) { 297 if (s->sc_prog == prog && s->sc_vers == vers 298 && (netid == NULL || s->sc_netid == NULL || 299 strcmp(netid, s->sc_netid) == 0)) 300 break; 301 } 302 303 return (s); 304} 305 306/* ******************* REPLY GENERATION ROUTINES ************ */ 307 308/* 309 * Send a reply to an rpc request 310 */ 311bool_t 312svc_sendreply(SVCXPRT *xprt, xdrproc_t xdr_results, void * xdr_location) 313{ 314 struct rpc_msg rply; 315 316 rply.rm_direction = REPLY; 317 rply.rm_reply.rp_stat = MSG_ACCEPTED; 318 rply.acpted_rply.ar_verf = xprt->xp_verf; 319 rply.acpted_rply.ar_stat = SUCCESS; 320 rply.acpted_rply.ar_results.where = xdr_location; 321 rply.acpted_rply.ar_results.proc = xdr_results; 322 323 return (SVC_REPLY(xprt, &rply)); 324} 325 326/* 327 * No procedure error reply 328 */ 329void 330svcerr_noproc(SVCXPRT *xprt) 331{ 332 struct rpc_msg rply; 333 334 rply.rm_direction = REPLY; 335 rply.rm_reply.rp_stat = MSG_ACCEPTED; 336 rply.acpted_rply.ar_verf = xprt->xp_verf; 337 rply.acpted_rply.ar_stat = PROC_UNAVAIL; 338 339 SVC_REPLY(xprt, &rply); 340} 341 342/* 343 * Can't decode args error reply 344 */ 345void 346svcerr_decode(SVCXPRT *xprt) 347{ 348 struct rpc_msg rply; 349 350 rply.rm_direction = REPLY; 351 rply.rm_reply.rp_stat = MSG_ACCEPTED; 352 rply.acpted_rply.ar_verf = xprt->xp_verf; 353 rply.acpted_rply.ar_stat = GARBAGE_ARGS; 354 355 SVC_REPLY(xprt, &rply); 356} 357 358/* 359 * Some system error 360 */ 361void 362svcerr_systemerr(SVCXPRT *xprt) 363{ 364 struct rpc_msg rply; 365 366 rply.rm_direction = REPLY; 367 rply.rm_reply.rp_stat = MSG_ACCEPTED; 368 rply.acpted_rply.ar_verf = xprt->xp_verf; 369 rply.acpted_rply.ar_stat = SYSTEM_ERR; 370 371 SVC_REPLY(xprt, &rply); 372} 373 374/* 375 * Authentication error reply 376 */ 377void 378svcerr_auth(SVCXPRT *xprt, enum auth_stat why) 379{ 380 struct rpc_msg rply; 381 382 rply.rm_direction = REPLY; 383 rply.rm_reply.rp_stat = MSG_DENIED; 384 rply.rjcted_rply.rj_stat = AUTH_ERROR; 385 rply.rjcted_rply.rj_why = why; 386 387 SVC_REPLY(xprt, &rply); 388} 389 390/* 391 * Auth too weak error reply 392 */ 393void 394svcerr_weakauth(SVCXPRT *xprt) 395{ 396 397 svcerr_auth(xprt, AUTH_TOOWEAK); 398} 399 400/* 401 * Program unavailable error reply 402 */ 403void 404svcerr_noprog(SVCXPRT *xprt) 405{ 406 struct rpc_msg rply; 407 408 rply.rm_direction = REPLY; 409 rply.rm_reply.rp_stat = MSG_ACCEPTED; 410 rply.acpted_rply.ar_verf = xprt->xp_verf; 411 rply.acpted_rply.ar_stat = PROG_UNAVAIL; 412 413 SVC_REPLY(xprt, &rply); 414} 415 416/* 417 * Program version mismatch error reply 418 */ 419void 420svcerr_progvers(SVCXPRT *xprt, rpcvers_t low_vers, rpcvers_t high_vers) 421{ 422 struct rpc_msg rply; 423 424 rply.rm_direction = REPLY; 425 rply.rm_reply.rp_stat = MSG_ACCEPTED; 426 rply.acpted_rply.ar_verf = xprt->xp_verf; 427 rply.acpted_rply.ar_stat = PROG_MISMATCH; 428 rply.acpted_rply.ar_vers.low = (uint32_t)low_vers; 429 rply.acpted_rply.ar_vers.high = (uint32_t)high_vers; 430 431 SVC_REPLY(xprt, &rply); 432} 433 434/* ******************* SERVER INPUT STUFF ******************* */ 435 436/* 437 * Get server side input from some transport. 438 * 439 * Statement of authentication parameters management: 440 * This function owns and manages all authentication parameters, specifically 441 * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and 442 * the "cooked" credentials (rqst->rq_clntcred). 443 * In-kernel, we represent non-trivial cooked creds with struct ucred. 444 * In all events, all three parameters are freed upon exit from this routine. 445 * The storage is trivially management on the call stack in user land, but 446 * is mallocated in kernel land. 447 */ 448 449static void 450svc_getreq(SVCXPRT *xprt) 451{ 452 SVCPOOL *pool = xprt->xp_pool; 453 struct svc_req r; 454 struct rpc_msg msg; 455 int prog_found; 456 rpcvers_t low_vers; 457 rpcvers_t high_vers; 458 enum xprt_stat stat; 459 char cred_area[2*MAX_AUTH_BYTES + sizeof(struct xucred)]; 460 461 msg.rm_call.cb_cred.oa_base = cred_area; 462 msg.rm_call.cb_verf.oa_base = &cred_area[MAX_AUTH_BYTES]; 463 r.rq_clntcred = &cred_area[2*MAX_AUTH_BYTES]; 464 465 /* now receive msgs from xprtprt (support batch calls) */ 466 do { 467 if (SVC_RECV(xprt, &msg)) { 468 469 /* now find the exported program and call it */ 470 struct svc_callout *s; 471 enum auth_stat why; 472 473 r.rq_xprt = xprt; 474 r.rq_prog = msg.rm_call.cb_prog; 475 r.rq_vers = msg.rm_call.cb_vers; 476 r.rq_proc = msg.rm_call.cb_proc; 477 r.rq_cred = msg.rm_call.cb_cred; 478 /* first authenticate the message */ 479 if ((why = _authenticate(&r, &msg)) != AUTH_OK) { 480 svcerr_auth(xprt, why); 481 goto call_done; 482 } 483 /* now match message with a registered service*/ 484 prog_found = FALSE; 485 low_vers = (rpcvers_t) -1L; 486 high_vers = (rpcvers_t) 0L; 487 TAILQ_FOREACH(s, &pool->sp_callouts, sc_link) { 488 if (s->sc_prog == r.rq_prog) { 489 if (s->sc_vers == r.rq_vers) { 490 (*s->sc_dispatch)(&r, xprt); 491 goto call_done; 492 } /* found correct version */ 493 prog_found = TRUE; 494 if (s->sc_vers < low_vers) 495 low_vers = s->sc_vers; 496 if (s->sc_vers > high_vers) 497 high_vers = s->sc_vers; 498 } /* found correct program */ 499 } 500 /* 501 * if we got here, the program or version 502 * is not served ... 503 */ 504 if (prog_found) 505 svcerr_progvers(xprt, low_vers, high_vers); 506 else 507 svcerr_noprog(xprt); 508 /* Fall through to ... */ 509 } 510 /* 511 * Check if the xprt has been disconnected in a 512 * recursive call in the service dispatch routine. 513 * If so, then break. 514 */ 515 mtx_lock(&pool->sp_lock); 516 if (!xprt->xp_registered) { 517 mtx_unlock(&pool->sp_lock); 518 break; 519 } 520 mtx_unlock(&pool->sp_lock); 521call_done: 522 if ((stat = SVC_STAT(xprt)) == XPRT_DIED) { 523 SVC_DESTROY(xprt); 524 break; 525 } 526 } while (stat == XPRT_MOREREQS); 527} 528 529void 530svc_run(SVCPOOL *pool) 531{ 532 SVCXPRT *xprt; 533 int error; 534 535 mtx_lock(&pool->sp_lock); 536 537 pool->sp_exited = FALSE; 538 539 while (!pool->sp_exited) { 540 xprt = TAILQ_FIRST(&pool->sp_active); 541 if (!xprt) { 542 error = msleep(&pool->sp_active, &pool->sp_lock, PCATCH, 543 "rpcsvc", 0); 544 if (error) 545 break; 546 continue; 547 } 548 549 /* 550 * Move this transport to the end to ensure fairness 551 * when multiple transports are active. If this was 552 * the last queued request, svc_getreq will end up 553 * calling xprt_inactive to remove from the active 554 * list. 555 */ 556 TAILQ_REMOVE(&pool->sp_active, xprt, xp_alink); 557 TAILQ_INSERT_TAIL(&pool->sp_active, xprt, xp_alink); 558 559 mtx_unlock(&pool->sp_lock); 560 svc_getreq(xprt); 561 mtx_lock(&pool->sp_lock); 562 } 563 564 mtx_unlock(&pool->sp_lock); 565} 566 567void 568svc_exit(SVCPOOL *pool) 569{ 570 mtx_lock(&pool->sp_lock); 571 pool->sp_exited = TRUE; 572 wakeup(&pool->sp_active); 573 mtx_unlock(&pool->sp_lock); 574} 575