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