xref: /freebsd/sys/rpc/svc.c (revision 2be1a816b9ff69588e55be0a84cbe2a31efc0f2f)
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