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