xref: /dragonfly/lib/libc/rpc/svc.c (revision cecb9aae)
1 /*
2  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
3  * unrestricted use provided that this legend is included on all tape
4  * media and as a part of the software program in whole or part.  Users
5  * may copy or modify Sun RPC without charge, but are not authorized
6  * to license or distribute it to anyone else except as part of a product or
7  * program developed by the user.
8  *
9  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
10  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
11  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
12  *
13  * Sun RPC is provided with no support and without any obligation on the
14  * part of Sun Microsystems, Inc. to assist in its use, correction,
15  * modification or enhancement.
16  *
17  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
18  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
19  * OR ANY PART THEREOF.
20  *
21  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
22  * or profits or other special, indirect and consequential damages, even if
23  * Sun has been advised of the possibility of such damages.
24  *
25  * Sun Microsystems, Inc.
26  * 2550 Garcia Avenue
27  * Mountain View, California  94043
28  *
29  * @(#)svc.c 1.44 88/02/08 Copyr 1984 Sun Micro
30  * @(#)svc.c	2.4 88/08/11 4.0 RPCSRC
31  * $NetBSD: svc.c,v 1.21 2000/07/06 03:10:35 christos Exp $
32  * $FreeBSD: src/lib/libc/rpc/svc.c,v 1.24 2006/02/27 22:10:59 deischen Exp $
33  * $DragonFly: src/lib/libc/rpc/svc.c,v 1.4 2005/11/13 12:27:04 swildner Exp $
34  */
35 
36 /*
37  * svc.c, Server-side remote procedure call interface.
38  *
39  * There are two sets of procedures here.  The xprt routines are
40  * for handling transport handles.  The svc routines handle the
41  * list of service routines.
42  *
43  * Copyright (C) 1984, Sun Microsystems, Inc.
44  */
45 
46 #include "namespace.h"
47 #include "reentrant.h"
48 #include <sys/types.h>
49 #include <sys/poll.h>
50 #include <assert.h>
51 #include <errno.h>
52 #include <stdlib.h>
53 #include <string.h>
54 
55 #include <rpc/rpc.h>
56 #ifdef PORTMAP
57 #include <rpc/pmap_clnt.h>
58 #endif				/* PORTMAP */
59 #include "un-namespace.h"
60 
61 #include "rpc_com.h"
62 #include "mt_misc.h"
63 
64 #define	RQCRED_SIZE	400		/* this size is excessive */
65 
66 #define SVC_VERSQUIET 0x0001		/* keep quiet about vers mismatch */
67 #define version_keepquiet(xp) ((u_long)(xp)->xp_p3 & SVC_VERSQUIET)
68 
69 #define max(a, b) (a > b ? a : b)
70 
71 /*
72  * The services list
73  * Each entry represents a set of procedures (an rpc program).
74  * The dispatch routine takes request structs and runs the
75  * apropriate procedure.
76  */
77 static struct svc_callout {
78 	struct svc_callout *sc_next;
79 	rpcprog_t	    sc_prog;
80 	rpcvers_t	    sc_vers;
81 	char		   *sc_netid;
82 	void		    (*sc_dispatch)(struct svc_req *, SVCXPRT *);
83 } *svc_head;
84 
85 static struct svc_callout *svc_find(rpcprog_t, rpcvers_t,
86     struct svc_callout **, char *);
87 static void __xprt_do_unregister (SVCXPRT *xprt, bool_t dolock);
88 
89 /* ***************  SVCXPRT related stuff **************** */
90 
91 /*
92  * Activate a transport handle.
93  */
94 void
95 xprt_register(SVCXPRT *xprt)
96 {
97 	int sock;
98 
99 	assert(xprt != NULL);
100 
101 	sock = xprt->xp_fd;
102 
103 	rwlock_wrlock(&svc_fd_lock);
104 	if (__svc_xports == NULL) {
105 		__svc_xports = (SVCXPRT **)
106 			mem_alloc(FD_SETSIZE * sizeof(SVCXPRT *));
107 		if (__svc_xports == NULL)
108 			return;
109 		memset(__svc_xports, '\0', FD_SETSIZE * sizeof(SVCXPRT *));
110 	}
111 	if (sock < FD_SETSIZE) {
112 		__svc_xports[sock] = xprt;
113 		FD_SET(sock, &svc_fdset);
114 		svc_maxfd = max(svc_maxfd, sock);
115 	}
116 	rwlock_unlock(&svc_fd_lock);
117 }
118 
119 void
120 xprt_unregister(SVCXPRT *xprt)
121 {
122 	__xprt_do_unregister(xprt, TRUE);
123 }
124 
125 void
126 __xprt_unregister_unlocked(SVCXPRT *xprt)
127 {
128 	__xprt_do_unregister(xprt, FALSE);
129 }
130 
131 /*
132  * De-activate a transport handle.
133  */
134 static void
135 __xprt_do_unregister(SVCXPRT *xprt, bool_t dolock)
136 {
137 	int sock;
138 
139 	assert(xprt != NULL);
140 
141 	sock = xprt->xp_fd;
142 
143 	if (dolock)
144 		rwlock_wrlock(&svc_fd_lock);
145 	if ((sock < FD_SETSIZE) && (__svc_xports[sock] == xprt)) {
146 		__svc_xports[sock] = NULL;
147 		FD_CLR(sock, &svc_fdset);
148 		if (sock >= svc_maxfd) {
149 			for (svc_maxfd--; svc_maxfd>=0; svc_maxfd--)
150 				if (__svc_xports[svc_maxfd])
151 					break;
152 		}
153 	}
154 	if (dolock)
155 		rwlock_unlock(&svc_fd_lock);
156 }
157 
158 /*
159  * Add a service program to the callout list.
160  * The dispatch routine will be called when a rpc request for this
161  * program number comes in.
162  */
163 bool_t
164 svc_reg(SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers,
165 	void (*dispatch)(struct svc_req *, SVCXPRT *),
166 	const struct netconfig *nconf)
167 {
168 	bool_t dummy;
169 	struct svc_callout *prev;
170 	struct svc_callout *s;
171 	struct netconfig *tnconf;
172 	char *netid = NULL;
173 	int flag = 0;
174 
175 /* VARIABLES PROTECTED BY svc_lock: s, prev, svc_head */
176 
177 	if (xprt->xp_netid) {
178 		netid = strdup(xprt->xp_netid);
179 		flag = 1;
180 	} else if (nconf && nconf->nc_netid) {
181 		netid = strdup(nconf->nc_netid);
182 		flag = 1;
183 	} else if ((tnconf = __rpcgettp(xprt->xp_fd)) != NULL) {
184 		netid = strdup(tnconf->nc_netid);
185 		flag = 1;
186 		freenetconfigent(tnconf);
187 	} /* must have been created with svc_raw_create */
188 	if ((netid == NULL) && (flag == 1)) {
189 		return (FALSE);
190 	}
191 
192 	rwlock_wrlock(&svc_lock);
193 	if ((s = svc_find(prog, vers, &prev, netid)) != NULL) {
194 		if (netid)
195 			free(netid);
196 		if (s->sc_dispatch == dispatch)
197 			goto rpcb_it; /* he is registering another xptr */
198 		rwlock_unlock(&svc_lock);
199 		return (FALSE);
200 	}
201 	s = mem_alloc(sizeof (struct svc_callout));
202 	if (s == NULL) {
203 		if (netid)
204 			free(netid);
205 		rwlock_unlock(&svc_lock);
206 		return (FALSE);
207 	}
208 
209 	s->sc_prog = prog;
210 	s->sc_vers = vers;
211 	s->sc_dispatch = dispatch;
212 	s->sc_netid = netid;
213 	s->sc_next = svc_head;
214 	svc_head = s;
215 
216 	if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
217 		((SVCXPRT *) xprt)->xp_netid = strdup(netid);
218 
219 rpcb_it:
220 	rwlock_unlock(&svc_lock);
221 	/* now register the information with the local binder service */
222 	if (nconf) {
223 		/*LINTED const castaway*/
224 		dummy = rpcb_set(prog, vers, (struct netconfig *) nconf,
225 		&((SVCXPRT *) xprt)->xp_ltaddr);
226 		return (dummy);
227 	}
228 	return (TRUE);
229 }
230 
231 /*
232  * Remove a service program from the callout list.
233  */
234 void
235 svc_unreg(const rpcprog_t prog, const rpcvers_t vers)
236 {
237 	struct svc_callout *prev;
238 	struct svc_callout *s;
239 
240 	/* unregister the information anyway */
241 	rpcb_unset(prog, vers, NULL);
242 	rwlock_wrlock(&svc_lock);
243 	while ((s = svc_find(prog, vers, &prev, NULL)) != NULL) {
244 		if (prev == NULL) {
245 			svc_head = s->sc_next;
246 		} else {
247 			prev->sc_next = s->sc_next;
248 		}
249 		s->sc_next = NULL;
250 		if (s->sc_netid)
251 			mem_free(s->sc_netid, sizeof (s->sc_netid) + 1);
252 		mem_free(s, sizeof (struct svc_callout));
253 	}
254 	rwlock_unlock(&svc_lock);
255 }
256 
257 /* ********************** CALLOUT list related stuff ************* */
258 
259 #ifdef PORTMAP
260 /*
261  * Add a service program to the callout list.
262  * The dispatch routine will be called when a rpc request for this
263  * program number comes in.
264  */
265 bool_t
266 svc_register(SVCXPRT *xprt, u_long prog, u_long vers,
267 	     void (*dispatch)(struct svc_req *, SVCXPRT *), int protocol)
268 {
269 	struct svc_callout *prev;
270 	struct svc_callout *s;
271 
272 	assert(xprt != NULL);
273 	assert(dispatch != NULL);
274 
275 	if ((s = svc_find((rpcprog_t)prog, (rpcvers_t)vers, &prev, NULL)) !=
276 	    NULL) {
277 		if (s->sc_dispatch == dispatch)
278 			goto pmap_it;  /* he is registering another xptr */
279 		return (FALSE);
280 	}
281 	s = mem_alloc(sizeof(struct svc_callout));
282 	if (s == NULL) {
283 		return (FALSE);
284 	}
285 	s->sc_prog = (rpcprog_t)prog;
286 	s->sc_vers = (rpcvers_t)vers;
287 	s->sc_dispatch = dispatch;
288 	s->sc_next = svc_head;
289 	svc_head = s;
290 pmap_it:
291 	/* now register the information with the local binder service */
292 	if (protocol) {
293 		return (pmap_set(prog, vers, protocol, xprt->xp_port));
294 	}
295 	return (TRUE);
296 }
297 
298 /*
299  * Remove a service program from the callout list.
300  */
301 void
302 svc_unregister(u_long prog, u_long vers)
303 {
304 	struct svc_callout *prev;
305 	struct svc_callout *s;
306 
307 	if ((s = svc_find((rpcprog_t)prog, (rpcvers_t)vers, &prev, NULL)) ==
308 	    NULL)
309 		return;
310 	if (prev == NULL) {
311 		svc_head = s->sc_next;
312 	} else {
313 		prev->sc_next = s->sc_next;
314 	}
315 	s->sc_next = NULL;
316 	mem_free(s, sizeof(struct svc_callout));
317 	/* now unregister the information with the local binder service */
318 	pmap_unset(prog, vers);
319 }
320 #endif				/* PORTMAP */
321 
322 /*
323  * Search the callout list for a program number, return the callout
324  * struct.
325  */
326 static struct svc_callout *
327 svc_find(rpcprog_t prog, rpcvers_t vers, struct svc_callout **prev, char *netid)
328 {
329 	struct svc_callout *s, *p;
330 
331 	assert(prev != NULL);
332 
333 	p = NULL;
334 	for (s = svc_head; s != NULL; s = s->sc_next) {
335 		if (((s->sc_prog == prog) && (s->sc_vers == vers)) &&
336 		    ((netid == NULL) || (s->sc_netid == NULL) ||
337 		    (strcmp(netid, s->sc_netid) == 0)))
338 			break;
339 		p = s;
340 	}
341 	*prev = p;
342 	return (s);
343 }
344 
345 /* ******************* REPLY GENERATION ROUTINES  ************ */
346 
347 /*
348  * Send a reply to an rpc request
349  */
350 bool_t
351 svc_sendreply(SVCXPRT *xprt, xdrproc_t xdr_results, void *xdr_location)
352 {
353 	struct rpc_msg rply;
354 
355 	assert(xprt != NULL);
356 
357 	rply.rm_direction = REPLY;
358 	rply.rm_reply.rp_stat = MSG_ACCEPTED;
359 	rply.acpted_rply.ar_verf = xprt->xp_verf;
360 	rply.acpted_rply.ar_stat = SUCCESS;
361 	rply.acpted_rply.ar_results.where = xdr_location;
362 	rply.acpted_rply.ar_results.proc = xdr_results;
363 	return (SVC_REPLY(xprt, &rply));
364 }
365 
366 /*
367  * No procedure error reply
368  */
369 void
370 svcerr_noproc(SVCXPRT *xprt)
371 {
372 	struct rpc_msg rply;
373 
374 	assert(xprt != NULL);
375 
376 	rply.rm_direction = REPLY;
377 	rply.rm_reply.rp_stat = MSG_ACCEPTED;
378 	rply.acpted_rply.ar_verf = xprt->xp_verf;
379 	rply.acpted_rply.ar_stat = PROC_UNAVAIL;
380 	SVC_REPLY(xprt, &rply);
381 }
382 
383 /*
384  * Can't decode args error reply
385  */
386 void
387 svcerr_decode(SVCXPRT *xprt)
388 {
389 	struct rpc_msg rply;
390 
391 	assert(xprt != NULL);
392 
393 	rply.rm_direction = REPLY;
394 	rply.rm_reply.rp_stat = MSG_ACCEPTED;
395 	rply.acpted_rply.ar_verf = xprt->xp_verf;
396 	rply.acpted_rply.ar_stat = GARBAGE_ARGS;
397 	SVC_REPLY(xprt, &rply);
398 }
399 
400 /*
401  * Some system error
402  */
403 void
404 svcerr_systemerr(SVCXPRT *xprt)
405 {
406 	struct rpc_msg rply;
407 
408 	assert(xprt != NULL);
409 
410 	rply.rm_direction = REPLY;
411 	rply.rm_reply.rp_stat = MSG_ACCEPTED;
412 	rply.acpted_rply.ar_verf = xprt->xp_verf;
413 	rply.acpted_rply.ar_stat = SYSTEM_ERR;
414 	SVC_REPLY(xprt, &rply);
415 }
416 
417 /*
418  * Authentication error reply
419  */
420 void
421 svcerr_auth(SVCXPRT *xprt, enum auth_stat why)
422 {
423 	struct rpc_msg rply;
424 
425 	assert(xprt != NULL);
426 
427 	rply.rm_direction = REPLY;
428 	rply.rm_reply.rp_stat = MSG_DENIED;
429 	rply.rjcted_rply.rj_stat = AUTH_ERROR;
430 	rply.rjcted_rply.rj_why = why;
431 	SVC_REPLY(xprt, &rply);
432 }
433 
434 /*
435  * Auth too weak error reply
436  */
437 void
438 svcerr_weakauth(SVCXPRT *xprt)
439 {
440 
441 	assert(xprt != NULL);
442 
443 	svcerr_auth(xprt, AUTH_TOOWEAK);
444 }
445 
446 /*
447  * Program unavailable error reply
448  */
449 void
450 svcerr_noprog(SVCXPRT *xprt)
451 {
452 	struct rpc_msg rply;
453 
454 	assert(xprt != NULL);
455 
456 	rply.rm_direction = REPLY;
457 	rply.rm_reply.rp_stat = MSG_ACCEPTED;
458 	rply.acpted_rply.ar_verf = xprt->xp_verf;
459 	rply.acpted_rply.ar_stat = PROG_UNAVAIL;
460 	SVC_REPLY(xprt, &rply);
461 }
462 
463 /*
464  * Program version mismatch error reply
465  */
466 void
467 svcerr_progvers(SVCXPRT *xprt, rpcvers_t low_vers, rpcvers_t high_vers)
468 {
469 	struct rpc_msg rply;
470 
471 	assert(xprt != NULL);
472 
473 	rply.rm_direction = REPLY;
474 	rply.rm_reply.rp_stat = MSG_ACCEPTED;
475 	rply.acpted_rply.ar_verf = xprt->xp_verf;
476 	rply.acpted_rply.ar_stat = PROG_MISMATCH;
477 	rply.acpted_rply.ar_vers.low = (u_int32_t)low_vers;
478 	rply.acpted_rply.ar_vers.high = (u_int32_t)high_vers;
479 	SVC_REPLY(xprt, &rply);
480 }
481 
482 /* ******************* SERVER INPUT STUFF ******************* */
483 
484 /*
485  * Get server side input from some transport.
486  *
487  * Statement of authentication parameters management:
488  * This function owns and manages all authentication parameters, specifically
489  * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
490  * the "cooked" credentials (rqst->rq_clntcred).
491  * However, this function does not know the structure of the cooked
492  * credentials, so it make the following assumptions:
493  *   a) the structure is contiguous (no pointers), and
494  *   b) the cred structure size does not exceed RQCRED_SIZE bytes.
495  * In all events, all three parameters are freed upon exit from this routine.
496  * The storage is trivially management on the call stack in user land, but
497  * is mallocated in kernel land.
498  */
499 
500 void
501 svc_getreq(int rdfds)
502 {
503 	fd_set readfds;
504 
505 	FD_ZERO(&readfds);
506 	readfds.fds_bits[0] = rdfds;
507 	svc_getreqset(&readfds);
508 }
509 
510 void
511 svc_getreqset(fd_set *readfds)
512 {
513 	int bit, fd;
514 	fd_mask mask, *maskp;
515 	int sock;
516 
517 	assert(readfds != NULL);
518 
519 	maskp = readfds->fds_bits;
520 	for (sock = 0; sock < FD_SETSIZE; sock += NFDBITS) {
521 	    for (mask = *maskp++; (bit = ffs(mask)) != 0;
522 		mask ^= (1 << (bit - 1))) {
523 		/* sock has input waiting */
524 		fd = sock + bit - 1;
525 		svc_getreq_common(fd);
526 	    }
527 	}
528 }
529 
530 void
531 svc_getreq_common(int fd)
532 {
533 	SVCXPRT *xprt;
534 	struct svc_req r;
535 	struct rpc_msg msg;
536 	int prog_found;
537 	rpcvers_t low_vers;
538 	rpcvers_t high_vers;
539 	enum xprt_stat stat;
540 	char cred_area[2*MAX_AUTH_BYTES + RQCRED_SIZE];
541 
542 	msg.rm_call.cb_cred.oa_base = cred_area;
543 	msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]);
544 	r.rq_clntcred = &(cred_area[2*MAX_AUTH_BYTES]);
545 
546 	rwlock_rdlock(&svc_fd_lock);
547 	xprt = __svc_xports[fd];
548 	rwlock_unlock(&svc_fd_lock);
549 	if (xprt == NULL)
550 		/* But do we control sock? */
551 		return;
552 	/* now receive msgs from xprtprt (support batch calls) */
553 	do {
554 		if (SVC_RECV(xprt, &msg)) {
555 
556 			/* now find the exported program and call it */
557 			struct svc_callout *s;
558 			enum auth_stat why;
559 
560 			r.rq_xprt = xprt;
561 			r.rq_prog = msg.rm_call.cb_prog;
562 			r.rq_vers = msg.rm_call.cb_vers;
563 			r.rq_proc = msg.rm_call.cb_proc;
564 			r.rq_cred = msg.rm_call.cb_cred;
565 			/* first authenticate the message */
566 			if ((why = _authenticate(&r, &msg)) != AUTH_OK) {
567 				svcerr_auth(xprt, why);
568 				goto call_done;
569 			}
570 			/* now match message with a registered service*/
571 			prog_found = FALSE;
572 			low_vers = (rpcvers_t) -1L;
573 			high_vers = (rpcvers_t) 0L;
574 			for (s = svc_head; s != NULL; s = s->sc_next) {
575 				if (s->sc_prog == r.rq_prog) {
576 					if (s->sc_vers == r.rq_vers) {
577 						(*s->sc_dispatch)(&r, xprt);
578 						goto call_done;
579 					}  /* found correct version */
580 					prog_found = TRUE;
581 					if (s->sc_vers < low_vers)
582 						low_vers = s->sc_vers;
583 					if (s->sc_vers > high_vers)
584 						high_vers = s->sc_vers;
585 				}   /* found correct program */
586 			}
587 			/*
588 			 * if we got here, the program or version
589 			 * is not served ...
590 			 */
591 			if (prog_found)
592 				svcerr_progvers(xprt, low_vers, high_vers);
593 			else
594 				 svcerr_noprog(xprt);
595 			/* Fall through to ... */
596 		}
597 		/*
598 		 * Check if the xprt has been disconnected in a
599 		 * recursive call in the service dispatch routine.
600 		 * If so, then break.
601 		 */
602 		rwlock_rdlock(&svc_fd_lock);
603 		if (xprt != __svc_xports[fd]) {
604 			rwlock_unlock(&svc_fd_lock);
605 			break;
606 		}
607 		rwlock_unlock(&svc_fd_lock);
608 call_done:
609 		if ((stat = SVC_STAT(xprt)) == XPRT_DIED){
610 			SVC_DESTROY(xprt);
611 			break;
612 		}
613 	} while (stat == XPRT_MOREREQS);
614 }
615 
616 
617 void
618 svc_getreq_poll(struct pollfd *pfdp, int pollretval)
619 {
620 	int i;
621 	int fds_found;
622 
623 	for (i = fds_found = 0; fds_found < pollretval; i++) {
624 		struct pollfd *p = &pfdp[i];
625 
626 		if (p->revents) {
627 			/* fd has input waiting */
628 			fds_found++;
629 			/*
630 			 *	We assume that this function is only called
631 			 *	via someone _select()ing from svc_fdset or
632 			 *	_poll()ing from svc_pollset[].  Thus it's safe
633 			 *	to handle the POLLNVAL event by simply turning
634 			 *	the corresponding bit off in svc_fdset.  The
635 			 *	svc_pollset[] array is derived from svc_fdset
636 			 *	and so will also be updated eventually.
637 			 *
638 			 *	XXX Should we do an xprt_unregister() instead?
639 			 */
640 			if (p->revents & POLLNVAL) {
641 				rwlock_wrlock(&svc_fd_lock);
642 				FD_CLR(p->fd, &svc_fdset);
643 				rwlock_unlock(&svc_fd_lock);
644 			} else
645 				svc_getreq_common(p->fd);
646 		}
647 	}
648 }
649 
650 bool_t
651 rpc_control(int what, void *arg)
652 {
653 	int val;
654 
655 	switch (what) {
656 	case RPC_SVC_CONNMAXREC_SET:
657 		val = *(int *)arg;
658 		if (val <= 0)
659 			return FALSE;
660 		__svc_maxrec = val;
661 		return TRUE;
662 	case RPC_SVC_CONNMAXREC_GET:
663 		*(int *)arg = __svc_maxrec;
664 		return TRUE;
665 	default:
666 		break;
667 	}
668 	return FALSE;
669 }
670