1/*	$NetBSD: svc.c,v 1.21 2000/07/06 03:10:35 christos Exp $	*/
2
3/*-
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 * Copyright (c) 2009, Sun Microsystems, Inc.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 * - Redistributions of source code must retain the above copyright notice,
12 *   this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright notice,
14 *   this list of conditions and the following disclaimer in the documentation
15 *   and/or other materials provided with the distribution.
16 * - Neither the name of Sun Microsystems, Inc. nor the names of its
17 *   contributors may be used to endorse or promote products derived
18 *   from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * svc.c, Server-side remote procedure call interface.
35 *
36 * There are two sets of procedures here.  The xprt routines are
37 * for handling transport handles.  The svc routines handle the
38 * list of service routines.
39 *
40 * Copyright (C) 1984, Sun Microsystems, Inc.
41 */
42
43#include "namespace.h"
44#include "reentrant.h"
45#include <sys/types.h>
46#include <sys/poll.h>
47#include <assert.h>
48#include <errno.h>
49#include <stdlib.h>
50#include <string.h>
51
52#include <rpc/rpc.h>
53#ifdef PORTMAP
54#include <rpc/pmap_clnt.h>
55#endif				/* PORTMAP */
56#include "un-namespace.h"
57
58#include "rpc_com.h"
59#include "mt_misc.h"
60
61#define	RQCRED_SIZE	400		/* this size is excessive */
62
63#define SVC_VERSQUIET 0x0001		/* keep quiet about vers mismatch */
64#define version_keepquiet(xp) (SVC_EXT(xp)->xp_flags & SVC_VERSQUIET)
65
66#define max(a, b) (a > b ? a : b)
67
68/*
69 * The services list
70 * Each entry represents a set of procedures (an rpc program).
71 * The dispatch routine takes request structs and runs the
72 * appropriate procedure.
73 */
74static struct svc_callout {
75	struct svc_callout *sc_next;
76	rpcprog_t	    sc_prog;
77	rpcvers_t	    sc_vers;
78	char		   *sc_netid;
79	void		    (*sc_dispatch)(struct svc_req *, SVCXPRT *);
80} *svc_head;
81
82SVCXPRT **__svc_xports;
83int __svc_maxrec;
84
85static struct svc_callout *svc_find(rpcprog_t, rpcvers_t,
86    struct svc_callout **, char *);
87static void __xprt_do_unregister (SVCXPRT *xprt, bool_t dolock);
88
89/* ***************  SVCXPRT related stuff **************** */
90
91/*
92 * Activate a transport handle.
93 */
94void
95xprt_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 + 1) * sizeof(SVCXPRT *));
107		if (__svc_xports == NULL) {
108			rwlock_unlock(&svc_fd_lock);
109			return;
110		}
111		memset(__svc_xports, '\0', (FD_SETSIZE + 1) * sizeof(SVCXPRT *));
112	}
113	if (sock < FD_SETSIZE) {
114		__svc_xports[sock] = xprt;
115		FD_SET(sock, &svc_fdset);
116		svc_maxfd = max(svc_maxfd, sock);
117	} else if (sock == FD_SETSIZE)
118		__svc_xports[sock] = xprt;
119	rwlock_unlock(&svc_fd_lock);
120}
121
122void
123xprt_unregister(SVCXPRT *xprt)
124{
125	__xprt_do_unregister(xprt, TRUE);
126}
127
128void
129__xprt_unregister_unlocked(SVCXPRT *xprt)
130{
131	__xprt_do_unregister(xprt, FALSE);
132}
133
134/*
135 * De-activate a transport handle.
136 */
137static void
138__xprt_do_unregister(SVCXPRT *xprt, bool_t dolock)
139{
140	int sock;
141
142	assert(xprt != NULL);
143
144	sock = xprt->xp_fd;
145
146	if (dolock)
147		rwlock_wrlock(&svc_fd_lock);
148	if ((sock < FD_SETSIZE) && (__svc_xports[sock] == xprt)) {
149		__svc_xports[sock] = NULL;
150		FD_CLR(sock, &svc_fdset);
151		if (sock >= svc_maxfd) {
152			for (svc_maxfd--; svc_maxfd>=0; svc_maxfd--)
153				if (__svc_xports[svc_maxfd])
154					break;
155		}
156	} else if ((sock == FD_SETSIZE) && (__svc_xports[sock] == xprt))
157		__svc_xports[sock] = NULL;
158	if (dolock)
159		rwlock_unlock(&svc_fd_lock);
160}
161
162/*
163 * Add a service program to the callout list.
164 * The dispatch routine will be called when a rpc request for this
165 * program number comes in.
166 */
167bool_t
168svc_reg(SVCXPRT *xprt, const rpcprog_t prog, const rpcvers_t vers,
169    void (*dispatch)(struct svc_req *, SVCXPRT *),
170    const struct netconfig *nconf)
171{
172	bool_t dummy;
173	struct svc_callout *prev;
174	struct svc_callout *s;
175	struct netconfig *tnconf;
176	char *netid = NULL;
177	int flag = 0;
178
179/* VARIABLES PROTECTED BY svc_lock: s, prev, svc_head */
180
181	if (xprt->xp_netid) {
182		netid = strdup(xprt->xp_netid);
183		flag = 1;
184	} else if (nconf && nconf->nc_netid) {
185		netid = strdup(nconf->nc_netid);
186		flag = 1;
187	} else if ((tnconf = __rpcgettp(xprt->xp_fd)) != NULL) {
188		netid = strdup(tnconf->nc_netid);
189		flag = 1;
190		freenetconfigent(tnconf);
191	} /* must have been created with svc_raw_create */
192	if ((netid == NULL) && (flag == 1)) {
193		return (FALSE);
194	}
195
196	rwlock_wrlock(&svc_lock);
197	if ((s = svc_find(prog, vers, &prev, netid)) != NULL) {
198		free(netid);
199		if (s->sc_dispatch == dispatch)
200			goto rpcb_it; /* he is registering another xptr */
201		rwlock_unlock(&svc_lock);
202		return (FALSE);
203	}
204	s = mem_alloc(sizeof (struct svc_callout));
205	if (s == NULL) {
206		free(netid);
207		rwlock_unlock(&svc_lock);
208		return (FALSE);
209	}
210
211	s->sc_prog = prog;
212	s->sc_vers = vers;
213	s->sc_dispatch = dispatch;
214	s->sc_netid = netid;
215	s->sc_next = svc_head;
216	svc_head = s;
217
218	if ((xprt->xp_netid == NULL) && (flag == 1) && netid)
219		((SVCXPRT *) xprt)->xp_netid = strdup(netid);
220
221rpcb_it:
222	rwlock_unlock(&svc_lock);
223	/* now register the information with the local binder service */
224	if (nconf) {
225		/*LINTED const castaway*/
226		dummy = rpcb_set(prog, vers, (struct netconfig *) nconf,
227		&((SVCXPRT *) xprt)->xp_ltaddr);
228		return (dummy);
229	}
230	return (TRUE);
231}
232
233/*
234 * Remove a service program from the callout list.
235 */
236void
237svc_unreg(const rpcprog_t prog, const rpcvers_t vers)
238{
239	struct svc_callout *prev;
240	struct svc_callout *s;
241
242	/* unregister the information anyway */
243	(void) rpcb_unset(prog, vers, NULL);
244	rwlock_wrlock(&svc_lock);
245	while ((s = svc_find(prog, vers, &prev, NULL)) != NULL) {
246		if (prev == NULL) {
247			svc_head = s->sc_next;
248		} else {
249			prev->sc_next = s->sc_next;
250		}
251		s->sc_next = NULL;
252		if (s->sc_netid)
253			mem_free(s->sc_netid, sizeof (s->sc_netid) + 1);
254		mem_free(s, sizeof (struct svc_callout));
255	}
256	rwlock_unlock(&svc_lock);
257}
258
259/* ********************** CALLOUT list related stuff ************* */
260
261#ifdef PORTMAP
262/*
263 * Add a service program to the callout list.
264 * The dispatch routine will be called when a rpc request for this
265 * program number comes in.
266 */
267bool_t
268svc_register(SVCXPRT *xprt, u_long prog, u_long vers,
269    void (*dispatch)(struct svc_req *, SVCXPRT *),
270    int protocol)
271{
272	struct svc_callout *prev;
273	struct svc_callout *s;
274
275	assert(xprt != NULL);
276	assert(dispatch != NULL);
277
278	if ((s = svc_find((rpcprog_t)prog, (rpcvers_t)vers, &prev, NULL)) !=
279	    NULL) {
280		if (s->sc_dispatch == dispatch)
281			goto pmap_it;  /* he is registering another xptr */
282		return (FALSE);
283	}
284	s = mem_alloc(sizeof(struct svc_callout));
285	if (s == NULL) {
286		return (FALSE);
287	}
288	s->sc_prog = (rpcprog_t)prog;
289	s->sc_vers = (rpcvers_t)vers;
290	s->sc_dispatch = dispatch;
291	s->sc_next = svc_head;
292	svc_head = s;
293pmap_it:
294	/* now register the information with the local binder service */
295	if (protocol) {
296		return (pmap_set(prog, vers, protocol, xprt->xp_port));
297	}
298	return (TRUE);
299}
300
301/*
302 * Remove a service program from the callout list.
303 */
304void
305svc_unregister(u_long prog, u_long vers)
306{
307	struct svc_callout *prev;
308	struct svc_callout *s;
309
310	if ((s = svc_find((rpcprog_t)prog, (rpcvers_t)vers, &prev, NULL)) ==
311	    NULL)
312		return;
313	if (prev == NULL) {
314		svc_head = s->sc_next;
315	} else {
316		prev->sc_next = s->sc_next;
317	}
318	s->sc_next = NULL;
319	mem_free(s, sizeof(struct svc_callout));
320	/* now unregister the information with the local binder service */
321	(void)pmap_unset(prog, vers);
322}
323#endif				/* PORTMAP */
324
325/*
326 * Search the callout list for a program number, return the callout
327 * struct.
328 */
329static struct svc_callout *
330svc_find(rpcprog_t prog, rpcvers_t vers, struct svc_callout **prev,
331    char *netid)
332{
333	struct svc_callout *s, *p;
334
335	assert(prev != NULL);
336
337	p = NULL;
338	for (s = svc_head; s != NULL; s = s->sc_next) {
339		if (((s->sc_prog == prog) && (s->sc_vers == vers)) &&
340		    ((netid == NULL) || (s->sc_netid == NULL) ||
341		    (strcmp(netid, s->sc_netid) == 0)))
342			break;
343		p = s;
344	}
345	*prev = p;
346	return (s);
347}
348
349/* ******************* REPLY GENERATION ROUTINES  ************ */
350
351/*
352 * Send a reply to an rpc request
353 */
354bool_t
355svc_sendreply(SVCXPRT *xprt, xdrproc_t xdr_results,
356    void * xdr_location)
357{
358	struct rpc_msg rply;
359
360	assert(xprt != NULL);
361
362	rply.rm_direction = REPLY;
363	rply.rm_reply.rp_stat = MSG_ACCEPTED;
364	rply.acpted_rply.ar_verf = xprt->xp_verf;
365	rply.acpted_rply.ar_stat = SUCCESS;
366	rply.acpted_rply.ar_results.where = xdr_location;
367	rply.acpted_rply.ar_results.proc = xdr_results;
368	return (SVC_REPLY(xprt, &rply));
369}
370
371/*
372 * No procedure error reply
373 */
374void
375svcerr_noproc(SVCXPRT *xprt)
376{
377	struct rpc_msg rply;
378
379	assert(xprt != NULL);
380
381	rply.rm_direction = REPLY;
382	rply.rm_reply.rp_stat = MSG_ACCEPTED;
383	rply.acpted_rply.ar_verf = xprt->xp_verf;
384	rply.acpted_rply.ar_stat = PROC_UNAVAIL;
385	SVC_REPLY(xprt, &rply);
386}
387
388/*
389 * Can't decode args error reply
390 */
391void
392svcerr_decode(SVCXPRT *xprt)
393{
394	struct rpc_msg rply;
395
396	assert(xprt != NULL);
397
398	rply.rm_direction = REPLY;
399	rply.rm_reply.rp_stat = MSG_ACCEPTED;
400	rply.acpted_rply.ar_verf = xprt->xp_verf;
401	rply.acpted_rply.ar_stat = GARBAGE_ARGS;
402	SVC_REPLY(xprt, &rply);
403}
404
405/*
406 * Some system error
407 */
408void
409svcerr_systemerr(SVCXPRT *xprt)
410{
411	struct rpc_msg rply;
412
413	assert(xprt != NULL);
414
415	rply.rm_direction = REPLY;
416	rply.rm_reply.rp_stat = MSG_ACCEPTED;
417	rply.acpted_rply.ar_verf = xprt->xp_verf;
418	rply.acpted_rply.ar_stat = SYSTEM_ERR;
419	SVC_REPLY(xprt, &rply);
420}
421
422#if 0
423/*
424 * Tell RPC package to not complain about version errors to the client.	 This
425 * is useful when revving broadcast protocols that sit on a fixed address.
426 * There is really one (or should be only one) example of this kind of
427 * protocol: the portmapper (or rpc binder).
428 */
429void
430__svc_versquiet_on(SVCXPRT *xprt)
431{
432
433	SVC_EXT(xprt)->xp_flags |= SVC_VERSQUIET;
434}
435
436void
437__svc_versquiet_off(SVCXPRT *xprt)
438{
439
440	SVC_EXT(xprt)->xp_flags &= ~SVC_VERSQUIET;
441}
442
443void
444svc_versquiet(SVCXPRT *xprt)
445{
446	__svc_versquiet_on(xprt);
447}
448
449int
450__svc_versquiet_get(SVCXPRT *xprt)
451{
452
453	return (SVC_EXT(xprt)->xp_flags & SVC_VERSQUIET);
454}
455#endif
456
457/*
458 * Authentication error reply
459 */
460void
461svcerr_auth(SVCXPRT *xprt, enum auth_stat why)
462{
463	struct rpc_msg rply;
464
465	assert(xprt != NULL);
466
467	rply.rm_direction = REPLY;
468	rply.rm_reply.rp_stat = MSG_DENIED;
469	rply.rjcted_rply.rj_stat = AUTH_ERROR;
470	rply.rjcted_rply.rj_why = why;
471	SVC_REPLY(xprt, &rply);
472}
473
474/*
475 * Auth too weak error reply
476 */
477void
478svcerr_weakauth(SVCXPRT *xprt)
479{
480
481	assert(xprt != NULL);
482
483	svcerr_auth(xprt, AUTH_TOOWEAK);
484}
485
486/*
487 * Program unavailable error reply
488 */
489void
490svcerr_noprog(SVCXPRT *xprt)
491{
492	struct rpc_msg rply;
493
494	assert(xprt != NULL);
495
496	rply.rm_direction = REPLY;
497	rply.rm_reply.rp_stat = MSG_ACCEPTED;
498	rply.acpted_rply.ar_verf = xprt->xp_verf;
499	rply.acpted_rply.ar_stat = PROG_UNAVAIL;
500	SVC_REPLY(xprt, &rply);
501}
502
503/*
504 * Program version mismatch error reply
505 */
506void
507svcerr_progvers(SVCXPRT *xprt, rpcvers_t low_vers, rpcvers_t high_vers)
508{
509	struct rpc_msg rply;
510
511	assert(xprt != NULL);
512
513	rply.rm_direction = REPLY;
514	rply.rm_reply.rp_stat = MSG_ACCEPTED;
515	rply.acpted_rply.ar_verf = xprt->xp_verf;
516	rply.acpted_rply.ar_stat = PROG_MISMATCH;
517	rply.acpted_rply.ar_vers.low = (u_int32_t)low_vers;
518	rply.acpted_rply.ar_vers.high = (u_int32_t)high_vers;
519	SVC_REPLY(xprt, &rply);
520}
521
522/*
523 * Allocate a new server transport structure. All fields are
524 * initialized to zero and xp_p3 is initialized to point at an
525 * extension structure to hold various flags and authentication
526 * parameters.
527 */
528SVCXPRT *
529svc_xprt_alloc(void)
530{
531	SVCXPRT *xprt;
532	SVCXPRT_EXT *ext;
533
534	xprt = mem_alloc(sizeof(SVCXPRT));
535	if (xprt == NULL)
536		return (NULL);
537	memset(xprt, 0, sizeof(SVCXPRT));
538	ext = mem_alloc(sizeof(SVCXPRT_EXT));
539	if (ext == NULL) {
540		mem_free(xprt, sizeof(SVCXPRT));
541		return (NULL);
542	}
543	memset(ext, 0, sizeof(SVCXPRT_EXT));
544	xprt->xp_p3 = ext;
545	ext->xp_auth.svc_ah_ops = &svc_auth_null_ops;
546
547	return (xprt);
548}
549
550/*
551 * Free a server transport structure.
552 */
553void
554svc_xprt_free(SVCXPRT *xprt)
555{
556
557	mem_free(xprt->xp_p3, sizeof(SVCXPRT_EXT));
558	mem_free(xprt, sizeof(SVCXPRT));
559}
560
561/* ******************* SERVER INPUT STUFF ******************* */
562
563/*
564 * Get server side input from some transport.
565 *
566 * Statement of authentication parameters management:
567 * This function owns and manages all authentication parameters, specifically
568 * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
569 * the "cooked" credentials (rqst->rq_clntcred).
570 * However, this function does not know the structure of the cooked
571 * credentials, so it make the following assumptions:
572 *   a) the structure is contiguous (no pointers), and
573 *   b) the cred structure size does not exceed RQCRED_SIZE bytes.
574 * In all events, all three parameters are freed upon exit from this routine.
575 * The storage is trivially management on the call stack in user land, but
576 * is mallocated in kernel land.
577 */
578
579void
580svc_getreq(int rdfds)
581{
582	fd_set readfds;
583
584	FD_ZERO(&readfds);
585	readfds.fds_bits[0] = rdfds;
586	svc_getreqset(&readfds);
587}
588
589void
590svc_getreqset(fd_set *readfds)
591{
592	int bit, fd;
593	fd_mask mask, *maskp;
594	int sock;
595
596	assert(readfds != NULL);
597
598	maskp = readfds->fds_bits;
599	for (sock = 0; sock < FD_SETSIZE; sock += NFDBITS) {
600	    for (mask = *maskp++; (bit = ffsl(mask)) != 0;
601		mask ^= (1ul << (bit - 1))) {
602		/* sock has input waiting */
603		fd = sock + bit - 1;
604		svc_getreq_common(fd);
605	    }
606	}
607}
608
609void
610svc_getreq_common(int fd)
611{
612	SVCXPRT *xprt;
613	struct svc_req r;
614	struct rpc_msg msg;
615	int prog_found;
616	rpcvers_t low_vers;
617	rpcvers_t high_vers;
618	enum xprt_stat stat;
619	char cred_area[2*MAX_AUTH_BYTES + RQCRED_SIZE];
620
621	msg.rm_call.cb_cred.oa_base = cred_area;
622	msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]);
623	r.rq_clntcred = &(cred_area[2*MAX_AUTH_BYTES]);
624
625	rwlock_rdlock(&svc_fd_lock);
626	xprt = __svc_xports[fd];
627	rwlock_unlock(&svc_fd_lock);
628	if (xprt == NULL)
629		/* But do we control sock? */
630		return;
631	/* now receive msgs from xprtprt (support batch calls) */
632	do {
633		if (SVC_RECV(xprt, &msg)) {
634
635			/* now find the exported program and call it */
636			struct svc_callout *s;
637			enum auth_stat why;
638
639			r.rq_xprt = xprt;
640			r.rq_prog = msg.rm_call.cb_prog;
641			r.rq_vers = msg.rm_call.cb_vers;
642			r.rq_proc = msg.rm_call.cb_proc;
643			r.rq_cred = msg.rm_call.cb_cred;
644			/* first authenticate the message */
645			if ((why = _authenticate(&r, &msg)) != AUTH_OK) {
646				/*
647				 * RPCSEC_GSS uses this return code
648				 * for requests that form part of its
649				 * context establishment protocol and
650				 * should not be dispatched to the
651				 * application.
652				 */
653				if (why != RPCSEC_GSS_NODISPATCH)
654					svcerr_auth(xprt, why);
655				goto call_done;
656			}
657			/* now match message with a registered service*/
658			prog_found = FALSE;
659			low_vers = (rpcvers_t) -1L;
660			high_vers = (rpcvers_t) 0L;
661			for (s = svc_head; s != NULL; s = s->sc_next) {
662				if (s->sc_prog == r.rq_prog) {
663					if (s->sc_vers == r.rq_vers) {
664						(*s->sc_dispatch)(&r, xprt);
665						goto call_done;
666					}  /* found correct version */
667					prog_found = TRUE;
668					if (s->sc_vers < low_vers)
669						low_vers = s->sc_vers;
670					if (s->sc_vers > high_vers)
671						high_vers = s->sc_vers;
672				}   /* found correct program */
673			}
674			/*
675			 * if we got here, the program or version
676			 * is not served ...
677			 */
678			if (prog_found)
679				svcerr_progvers(xprt, low_vers, high_vers);
680			else
681				svcerr_noprog(xprt);
682			/* Fall through to ... */
683		}
684		/*
685		 * Check if the xprt has been disconnected in a
686		 * recursive call in the service dispatch routine.
687		 * If so, then break.
688		 */
689		rwlock_rdlock(&svc_fd_lock);
690		if (xprt != __svc_xports[fd]) {
691			rwlock_unlock(&svc_fd_lock);
692			break;
693		}
694		rwlock_unlock(&svc_fd_lock);
695call_done:
696		if ((stat = SVC_STAT(xprt)) == XPRT_DIED){
697			SVC_DESTROY(xprt);
698			break;
699		}
700	} while (stat == XPRT_MOREREQS);
701}
702
703
704void
705svc_getreq_poll(struct pollfd *pfdp, int pollretval)
706{
707	int i;
708	int fds_found;
709
710	for (i = fds_found = 0; fds_found < pollretval; i++) {
711		struct pollfd *p = &pfdp[i];
712
713		if (p->revents) {
714			/* fd has input waiting */
715			fds_found++;
716			/*
717			 *	We assume that this function is only called
718			 *	via someone _select()ing from svc_fdset or
719			 *	_poll()ing from svc_pollset[].  Thus it's safe
720			 *	to handle the POLLNVAL event by simply turning
721			 *	the corresponding bit off in svc_fdset.  The
722			 *	svc_pollset[] array is derived from svc_fdset
723			 *	and so will also be updated eventually.
724			 *
725			 *	XXX Should we do an xprt_unregister() instead?
726			 */
727			if (p->revents & POLLNVAL) {
728				rwlock_wrlock(&svc_fd_lock);
729				FD_CLR(p->fd, &svc_fdset);
730				rwlock_unlock(&svc_fd_lock);
731			} else
732				svc_getreq_common(p->fd);
733		}
734	}
735}
736
737bool_t
738rpc_control(int what, void *arg)
739{
740	int val;
741
742	switch (what) {
743	case RPC_SVC_CONNMAXREC_SET:
744		val = *(int *)arg;
745		if (val <= 0)
746			return FALSE;
747		__svc_maxrec = val;
748		return TRUE;
749	case RPC_SVC_CONNMAXREC_GET:
750		*(int *)arg = __svc_maxrec;
751		return TRUE;
752	default:
753		break;
754	}
755	return FALSE;
756}
757