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) 33 static char *sccsid2 = "@(#)svc.c 1.44 88/02/08 Copyr 1984 Sun Micro"; 34 static char *sccsid = "@(#)svc.c 2.4 88/08/11 4.0 RPCSRC"; 35 #endif 36 #include <sys/cdefs.h> 37 __FBSDID("$FreeBSD$"); 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/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 66 static struct svc_callout *svc_find(SVCPOOL *pool, rpcprog_t, rpcvers_t, 67 char *); 68 static void __xprt_do_unregister (SVCXPRT *xprt, bool_t dolock); 69 70 /* *************** SVCXPRT related stuff **************** */ 71 72 SVCPOOL* 73 svcpool_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 87 void 88 svcpool_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 */ 116 void 117 xprt_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 128 void 129 xprt_unregister(SVCXPRT *xprt) 130 { 131 __xprt_do_unregister(xprt, TRUE); 132 } 133 134 void 135 __xprt_unregister_unlocked(SVCXPRT *xprt) 136 { 137 __xprt_do_unregister(xprt, FALSE); 138 } 139 140 /* 141 * De-activate a transport handle. 142 */ 143 static 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 164 void 165 xprt_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 180 void 181 xprt_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 */ 201 bool_t 202 svc_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 250 rpcb_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 */ 267 void 268 svc_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 */ 290 static struct svc_callout * 291 svc_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 */ 311 bool_t 312 svc_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 */ 329 void 330 svcerr_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 */ 345 void 346 svcerr_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 */ 361 void 362 svcerr_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 */ 377 void 378 svcerr_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 */ 393 void 394 svcerr_weakauth(SVCXPRT *xprt) 395 { 396 397 svcerr_auth(xprt, AUTH_TOOWEAK); 398 } 399 400 /* 401 * Program unavailable error reply 402 */ 403 void 404 svcerr_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 */ 419 void 420 svcerr_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 449 static void 450 svc_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); 521 call_done: 522 if ((stat = SVC_STAT(xprt)) == XPRT_DIED) { 523 SVC_DESTROY(xprt); 524 break; 525 } 526 } while (stat == XPRT_MOREREQS); 527 } 528 529 void 530 svc_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 567 void 568 svc_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