rpc_generic.c revision 261046
1/*	$NetBSD: rpc_generic.c,v 1.4 2000/09/28 09:07:04 kleink Exp $	*/
2
3/*-
4 * Copyright (c) 2009, Sun Microsystems, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * - Redistributions of source code must retain the above copyright notice,
10 *   this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright notice,
12 *   this list of conditions and the following disclaimer in the documentation
13 *   and/or other materials provided with the distribution.
14 * - Neither the name of Sun Microsystems, Inc. nor the names of its
15 *   contributors may be used to endorse or promote products derived
16 *   from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30/*
31 * Copyright (c) 1986-1991 by Sun Microsystems Inc.
32 */
33
34/* #pragma ident	"@(#)rpc_generic.c	1.17	94/04/24 SMI" */
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: stable/10/sys/rpc/rpc_generic.c 261046 2014-01-22 23:45:27Z mav $");
37
38/*
39 * rpc_generic.c, Miscl routines for RPC.
40 *
41 */
42
43#include "opt_inet6.h"
44
45#include <sys/param.h>
46#include <sys/kernel.h>
47#include <sys/malloc.h>
48#include <sys/mbuf.h>
49#include <sys/module.h>
50#include <sys/proc.h>
51#include <sys/protosw.h>
52#include <sys/sbuf.h>
53#include <sys/systm.h>
54#include <sys/socket.h>
55#include <sys/socketvar.h>
56#include <sys/syslog.h>
57
58#include <net/vnet.h>
59
60#include <rpc/rpc.h>
61#include <rpc/nettype.h>
62#include <rpc/rpcsec_gss.h>
63
64#include <rpc/rpc_com.h>
65
66extern	u_long sb_max_adj;	/* not defined in socketvar.h */
67
68#if __FreeBSD_version < 700000
69#define strrchr rindex
70#endif
71
72/* Provide an entry point hook for the rpcsec_gss module. */
73struct rpc_gss_entries	rpc_gss_entries;
74
75struct handle {
76	NCONF_HANDLE *nhandle;
77	int nflag;		/* Whether NETPATH or NETCONFIG */
78	int nettype;
79};
80
81static const struct _rpcnettype {
82	const char *name;
83	const int type;
84} _rpctypelist[] = {
85	{ "netpath", _RPC_NETPATH },
86	{ "visible", _RPC_VISIBLE },
87	{ "circuit_v", _RPC_CIRCUIT_V },
88	{ "datagram_v", _RPC_DATAGRAM_V },
89	{ "circuit_n", _RPC_CIRCUIT_N },
90	{ "datagram_n", _RPC_DATAGRAM_N },
91	{ "tcp", _RPC_TCP },
92	{ "udp", _RPC_UDP },
93	{ 0, _RPC_NONE }
94};
95
96struct netid_af {
97	const char	*netid;
98	int		af;
99	int		protocol;
100};
101
102static const struct netid_af na_cvt[] = {
103	{ "udp",  AF_INET,  IPPROTO_UDP },
104	{ "tcp",  AF_INET,  IPPROTO_TCP },
105#ifdef INET6
106	{ "udp6", AF_INET6, IPPROTO_UDP },
107	{ "tcp6", AF_INET6, IPPROTO_TCP },
108#endif
109	{ "local", AF_LOCAL, 0 }
110};
111
112struct rpc_createerr rpc_createerr;
113
114/*
115 * Find the appropriate buffer size
116 */
117u_int
118/*ARGSUSED*/
119__rpc_get_t_size(int af, int proto, int size)
120{
121	int defsize;
122
123	switch (proto) {
124	case IPPROTO_TCP:
125		defsize = 64 * 1024;	/* XXX */
126		break;
127	case IPPROTO_UDP:
128		defsize = UDPMSGSIZE;
129		break;
130	default:
131		defsize = RPC_MAXDATASIZE;
132		break;
133	}
134	if (size == 0)
135		return defsize;
136
137	/* Check whether the value is within the upper max limit */
138	return (size > sb_max_adj ? (u_int)sb_max_adj : (u_int)size);
139}
140
141/*
142 * Find the appropriate address buffer size
143 */
144u_int
145__rpc_get_a_size(af)
146	int af;
147{
148	switch (af) {
149	case AF_INET:
150		return sizeof (struct sockaddr_in);
151#ifdef INET6
152	case AF_INET6:
153		return sizeof (struct sockaddr_in6);
154#endif
155	case AF_LOCAL:
156		return sizeof (struct sockaddr_un);
157	default:
158		break;
159	}
160	return ((u_int)RPC_MAXADDRSIZE);
161}
162
163#if 0
164
165/*
166 * Used to ping the NULL procedure for clnt handle.
167 * Returns NULL if fails, else a non-NULL pointer.
168 */
169void *
170rpc_nullproc(clnt)
171	CLIENT *clnt;
172{
173	struct timeval TIMEOUT = {25, 0};
174
175	if (clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void, NULL,
176		(xdrproc_t) xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
177		return (NULL);
178	}
179	return ((void *) clnt);
180}
181
182#endif
183
184int
185__rpc_socket2sockinfo(struct socket *so, struct __rpc_sockinfo *sip)
186{
187	int type, proto;
188	struct sockaddr *sa;
189	sa_family_t family;
190	struct sockopt opt;
191	int error;
192
193	CURVNET_SET(so->so_vnet);
194	error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
195	CURVNET_RESTORE();
196	if (error)
197		return 0;
198
199	sip->si_alen = sa->sa_len;
200	family = sa->sa_family;
201	free(sa, M_SONAME);
202
203	opt.sopt_dir = SOPT_GET;
204	opt.sopt_level = SOL_SOCKET;
205	opt.sopt_name = SO_TYPE;
206	opt.sopt_val = &type;
207	opt.sopt_valsize = sizeof type;
208	opt.sopt_td = NULL;
209	error = sogetopt(so, &opt);
210	if (error)
211		return 0;
212
213	/* XXX */
214	if (family != AF_LOCAL) {
215		if (type == SOCK_STREAM)
216			proto = IPPROTO_TCP;
217		else if (type == SOCK_DGRAM)
218			proto = IPPROTO_UDP;
219		else
220			return 0;
221	} else
222		proto = 0;
223
224	sip->si_af = family;
225	sip->si_proto = proto;
226	sip->si_socktype = type;
227
228	return 1;
229}
230
231/*
232 * Linear search, but the number of entries is small.
233 */
234int
235__rpc_nconf2sockinfo(const struct netconfig *nconf, struct __rpc_sockinfo *sip)
236{
237	int i;
238
239	for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++)
240		if (strcmp(na_cvt[i].netid, nconf->nc_netid) == 0 || (
241		    strcmp(nconf->nc_netid, "unix") == 0 &&
242		    strcmp(na_cvt[i].netid, "local") == 0)) {
243			sip->si_af = na_cvt[i].af;
244			sip->si_proto = na_cvt[i].protocol;
245			sip->si_socktype =
246			    __rpc_seman2socktype((int)nconf->nc_semantics);
247			if (sip->si_socktype == -1)
248				return 0;
249			sip->si_alen = __rpc_get_a_size(sip->si_af);
250			return 1;
251		}
252
253	return 0;
254}
255
256struct socket *
257__rpc_nconf2socket(const struct netconfig *nconf)
258{
259	struct __rpc_sockinfo si;
260	struct socket *so;
261	int error;
262
263	if (!__rpc_nconf2sockinfo(nconf, &si))
264		return 0;
265
266	so = NULL;
267	error =  socreate(si.si_af, &so, si.si_socktype, si.si_proto,
268	    curthread->td_ucred, curthread);
269
270	if (error)
271		return NULL;
272	else
273		return so;
274}
275
276char *
277taddr2uaddr(const struct netconfig *nconf, const struct netbuf *nbuf)
278{
279	struct __rpc_sockinfo si;
280
281	if (!__rpc_nconf2sockinfo(nconf, &si))
282		return NULL;
283	return __rpc_taddr2uaddr_af(si.si_af, nbuf);
284}
285
286struct netbuf *
287uaddr2taddr(const struct netconfig *nconf, const char *uaddr)
288{
289	struct __rpc_sockinfo si;
290
291	if (!__rpc_nconf2sockinfo(nconf, &si))
292		return NULL;
293	return __rpc_uaddr2taddr_af(si.si_af, uaddr);
294}
295
296char *
297__rpc_taddr2uaddr_af(int af, const struct netbuf *nbuf)
298{
299	char *ret;
300	struct sbuf sb;
301	struct sockaddr_in *sin;
302	struct sockaddr_un *sun;
303	char namebuf[INET_ADDRSTRLEN];
304#ifdef INET6
305	struct sockaddr_in6 *sin6;
306	char namebuf6[INET6_ADDRSTRLEN];
307#endif
308	u_int16_t port;
309
310	sbuf_new(&sb, NULL, 0, SBUF_AUTOEXTEND);
311
312	switch (af) {
313	case AF_INET:
314		sin = nbuf->buf;
315		if (inet_ntop(af, &sin->sin_addr, namebuf, sizeof namebuf)
316		    == NULL)
317			return NULL;
318		port = ntohs(sin->sin_port);
319		if (sbuf_printf(&sb, "%s.%u.%u", namebuf,
320			((uint32_t)port) >> 8,
321			port & 0xff) < 0)
322			return NULL;
323		break;
324#ifdef INET6
325	case AF_INET6:
326		sin6 = nbuf->buf;
327		if (inet_ntop(af, &sin6->sin6_addr, namebuf6, sizeof namebuf6)
328		    == NULL)
329			return NULL;
330		port = ntohs(sin6->sin6_port);
331		if (sbuf_printf(&sb, "%s.%u.%u", namebuf6,
332			((uint32_t)port) >> 8,
333			port & 0xff) < 0)
334			return NULL;
335		break;
336#endif
337	case AF_LOCAL:
338		sun = nbuf->buf;
339		if (sbuf_printf(&sb, "%.*s", (int)(sun->sun_len -
340			    offsetof(struct sockaddr_un, sun_path)),
341			sun->sun_path) < 0)
342			return (NULL);
343		break;
344	default:
345		return NULL;
346	}
347
348	sbuf_finish(&sb);
349	ret = strdup(sbuf_data(&sb), M_RPC);
350	sbuf_delete(&sb);
351
352	return ret;
353}
354
355struct netbuf *
356__rpc_uaddr2taddr_af(int af, const char *uaddr)
357{
358	struct netbuf *ret = NULL;
359	char *addrstr, *p;
360	unsigned port, portlo, porthi;
361	struct sockaddr_in *sin;
362#ifdef INET6
363	struct sockaddr_in6 *sin6;
364#endif
365	struct sockaddr_un *sun;
366
367	port = 0;
368	sin = NULL;
369	addrstr = strdup(uaddr, M_RPC);
370	if (addrstr == NULL)
371		return NULL;
372
373	/*
374	 * AF_LOCAL addresses are expected to be absolute
375	 * pathnames, anything else will be AF_INET or AF_INET6.
376	 */
377	if (*addrstr != '/') {
378		p = strrchr(addrstr, '.');
379		if (p == NULL)
380			goto out;
381		portlo = (unsigned)strtol(p + 1, NULL, 10);
382		*p = '\0';
383
384		p = strrchr(addrstr, '.');
385		if (p == NULL)
386			goto out;
387		porthi = (unsigned)strtol(p + 1, NULL, 10);
388		*p = '\0';
389		port = (porthi << 8) | portlo;
390	}
391
392	ret = (struct netbuf *)malloc(sizeof *ret, M_RPC, M_WAITOK);
393	if (ret == NULL)
394		goto out;
395
396	switch (af) {
397	case AF_INET:
398		sin = (struct sockaddr_in *)malloc(sizeof *sin, M_RPC,
399		    M_WAITOK);
400		if (sin == NULL)
401			goto out;
402		memset(sin, 0, sizeof *sin);
403		sin->sin_family = AF_INET;
404		sin->sin_port = htons(port);
405		if (inet_pton(AF_INET, addrstr, &sin->sin_addr) <= 0) {
406			free(sin, M_RPC);
407			free(ret, M_RPC);
408			ret = NULL;
409			goto out;
410		}
411		sin->sin_len = ret->maxlen = ret->len = sizeof *sin;
412		ret->buf = sin;
413		break;
414#ifdef INET6
415	case AF_INET6:
416		sin6 = (struct sockaddr_in6 *)malloc(sizeof *sin6, M_RPC,
417		    M_WAITOK);
418		if (sin6 == NULL)
419			goto out;
420		memset(sin6, 0, sizeof *sin6);
421		sin6->sin6_family = AF_INET6;
422		sin6->sin6_port = htons(port);
423		if (inet_pton(AF_INET6, addrstr, &sin6->sin6_addr) <= 0) {
424			free(sin6, M_RPC);
425			free(ret, M_RPC);
426			ret = NULL;
427			goto out;
428		}
429		sin6->sin6_len = ret->maxlen = ret->len = sizeof *sin6;
430		ret->buf = sin6;
431		break;
432#endif
433	case AF_LOCAL:
434		sun = (struct sockaddr_un *)malloc(sizeof *sun, M_RPC,
435		    M_WAITOK);
436		if (sun == NULL)
437			goto out;
438		memset(sun, 0, sizeof *sun);
439		sun->sun_family = AF_LOCAL;
440		strncpy(sun->sun_path, addrstr, sizeof(sun->sun_path) - 1);
441		ret->len = ret->maxlen = sun->sun_len = SUN_LEN(sun);
442		ret->buf = sun;
443		break;
444	default:
445		break;
446	}
447out:
448	free(addrstr, M_RPC);
449	return ret;
450}
451
452int
453__rpc_seman2socktype(int semantics)
454{
455	switch (semantics) {
456	case NC_TPI_CLTS:
457		return SOCK_DGRAM;
458	case NC_TPI_COTS_ORD:
459		return SOCK_STREAM;
460	case NC_TPI_RAW:
461		return SOCK_RAW;
462	default:
463		break;
464	}
465
466	return -1;
467}
468
469int
470__rpc_socktype2seman(int socktype)
471{
472	switch (socktype) {
473	case SOCK_DGRAM:
474		return NC_TPI_CLTS;
475	case SOCK_STREAM:
476		return NC_TPI_COTS_ORD;
477	case SOCK_RAW:
478		return NC_TPI_RAW;
479	default:
480		break;
481	}
482
483	return -1;
484}
485
486/*
487 * Returns the type of the network as defined in <rpc/nettype.h>
488 * If nettype is NULL, it defaults to NETPATH.
489 */
490static int
491getnettype(const char *nettype)
492{
493	int i;
494
495	if ((nettype == NULL) || (nettype[0] == 0)) {
496		return (_RPC_NETPATH);	/* Default */
497	}
498
499#if 0
500	nettype = strlocase(nettype);
501#endif
502	for (i = 0; _rpctypelist[i].name; i++)
503		if (strcasecmp(nettype, _rpctypelist[i].name) == 0) {
504			return (_rpctypelist[i].type);
505		}
506	return (_rpctypelist[i].type);
507}
508
509/*
510 * For the given nettype (tcp or udp only), return the first structure found.
511 * This should be freed by calling freenetconfigent()
512 */
513struct netconfig *
514__rpc_getconfip(const char *nettype)
515{
516	char *netid;
517	static char *netid_tcp = (char *) NULL;
518	static char *netid_udp = (char *) NULL;
519	struct netconfig *dummy;
520
521	if (!netid_udp && !netid_tcp) {
522		struct netconfig *nconf;
523		void *confighandle;
524
525		if (!(confighandle = setnetconfig())) {
526			log(LOG_ERR, "rpc: failed to open " NETCONFIG);
527			return (NULL);
528		}
529		while ((nconf = getnetconfig(confighandle)) != NULL) {
530			if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
531				if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
532					netid_tcp = strdup(nconf->nc_netid,
533					    M_RPC);
534				} else
535				if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
536					netid_udp = strdup(nconf->nc_netid,
537					    M_RPC);
538				}
539			}
540		}
541		endnetconfig(confighandle);
542	}
543	if (strcmp(nettype, "udp") == 0)
544		netid = netid_udp;
545	else if (strcmp(nettype, "tcp") == 0)
546		netid = netid_tcp;
547	else {
548		return (NULL);
549	}
550	if ((netid == NULL) || (netid[0] == 0)) {
551		return (NULL);
552	}
553	dummy = getnetconfigent(netid);
554	return (dummy);
555}
556
557/*
558 * Returns the type of the nettype, which should then be used with
559 * __rpc_getconf().
560 *
561 * For simplicity in the kernel, we don't support the NETPATH
562 * environment variable. We behave as userland would then NETPATH is
563 * unset, i.e. iterate over all visible entries in netconfig.
564 */
565void *
566__rpc_setconf(nettype)
567	const char *nettype;
568{
569	struct handle *handle;
570
571	handle = (struct handle *) malloc(sizeof (struct handle),
572	    M_RPC, M_WAITOK);
573	switch (handle->nettype = getnettype(nettype)) {
574	case _RPC_NETPATH:
575	case _RPC_CIRCUIT_N:
576	case _RPC_DATAGRAM_N:
577		if (!(handle->nhandle = setnetconfig()))
578			goto failed;
579		handle->nflag = TRUE;
580		break;
581	case _RPC_VISIBLE:
582	case _RPC_CIRCUIT_V:
583	case _RPC_DATAGRAM_V:
584	case _RPC_TCP:
585	case _RPC_UDP:
586		if (!(handle->nhandle = setnetconfig())) {
587		        log(LOG_ERR, "rpc: failed to open " NETCONFIG);
588			goto failed;
589		}
590		handle->nflag = FALSE;
591		break;
592	default:
593		goto failed;
594	}
595
596	return (handle);
597
598failed:
599	free(handle, M_RPC);
600	return (NULL);
601}
602
603/*
604 * Returns the next netconfig struct for the given "net" type.
605 * __rpc_setconf() should have been called previously.
606 */
607struct netconfig *
608__rpc_getconf(void *vhandle)
609{
610	struct handle *handle;
611	struct netconfig *nconf;
612
613	handle = (struct handle *)vhandle;
614	if (handle == NULL) {
615		return (NULL);
616	}
617	for (;;) {
618		if (handle->nflag) {
619			nconf = getnetconfig(handle->nhandle);
620			if (nconf && !(nconf->nc_flag & NC_VISIBLE))
621				continue;
622		} else {
623			nconf = getnetconfig(handle->nhandle);
624		}
625		if (nconf == NULL)
626			break;
627		if ((nconf->nc_semantics != NC_TPI_CLTS) &&
628			(nconf->nc_semantics != NC_TPI_COTS) &&
629			(nconf->nc_semantics != NC_TPI_COTS_ORD))
630			continue;
631		switch (handle->nettype) {
632		case _RPC_VISIBLE:
633			if (!(nconf->nc_flag & NC_VISIBLE))
634				continue;
635			/* FALLTHROUGH */
636		case _RPC_NETPATH:	/* Be happy */
637			break;
638		case _RPC_CIRCUIT_V:
639			if (!(nconf->nc_flag & NC_VISIBLE))
640				continue;
641			/* FALLTHROUGH */
642		case _RPC_CIRCUIT_N:
643			if ((nconf->nc_semantics != NC_TPI_COTS) &&
644				(nconf->nc_semantics != NC_TPI_COTS_ORD))
645				continue;
646			break;
647		case _RPC_DATAGRAM_V:
648			if (!(nconf->nc_flag & NC_VISIBLE))
649				continue;
650			/* FALLTHROUGH */
651		case _RPC_DATAGRAM_N:
652			if (nconf->nc_semantics != NC_TPI_CLTS)
653				continue;
654			break;
655		case _RPC_TCP:
656			if (((nconf->nc_semantics != NC_TPI_COTS) &&
657				(nconf->nc_semantics != NC_TPI_COTS_ORD)) ||
658				(strcmp(nconf->nc_protofmly, NC_INET)
659#ifdef INET6
660				 && strcmp(nconf->nc_protofmly, NC_INET6))
661#else
662				)
663#endif
664				||
665				strcmp(nconf->nc_proto, NC_TCP))
666				continue;
667			break;
668		case _RPC_UDP:
669			if ((nconf->nc_semantics != NC_TPI_CLTS) ||
670				(strcmp(nconf->nc_protofmly, NC_INET)
671#ifdef INET6
672				&& strcmp(nconf->nc_protofmly, NC_INET6))
673#else
674				)
675#endif
676				||
677				strcmp(nconf->nc_proto, NC_UDP))
678				continue;
679			break;
680		}
681		break;
682	}
683	return (nconf);
684}
685
686void
687__rpc_endconf(vhandle)
688	void * vhandle;
689{
690	struct handle *handle;
691
692	handle = (struct handle *) vhandle;
693	if (handle == NULL) {
694		return;
695	}
696	endnetconfig(handle->nhandle);
697	free(handle, M_RPC);
698}
699
700int
701__rpc_sockisbound(struct socket *so)
702{
703	struct sockaddr *sa;
704	int error, bound;
705
706	error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
707	if (error)
708		return (0);
709
710	switch (sa->sa_family) {
711		case AF_INET:
712			bound = (((struct sockaddr_in *) sa)->sin_port != 0);
713			break;
714#ifdef INET6
715		case AF_INET6:
716			bound = (((struct sockaddr_in6 *) sa)->sin6_port != 0);
717			break;
718#endif
719		case AF_LOCAL:
720			/* XXX check this */
721			bound = (((struct sockaddr_un *) sa)->sun_path[0] != '\0');
722			break;
723		default:
724			bound = FALSE;
725			break;
726	}
727
728	free(sa, M_SONAME);
729
730	return bound;
731}
732
733/*
734 * Implement XDR-style API for RPC call.
735 */
736enum clnt_stat
737clnt_call_private(
738	CLIENT		*cl,		/* client handle */
739	struct rpc_callextra *ext,	/* call metadata */
740	rpcproc_t	proc,		/* procedure number */
741	xdrproc_t	xargs,		/* xdr routine for args */
742	void		*argsp,		/* pointer to args */
743	xdrproc_t	xresults,	/* xdr routine for results */
744	void		*resultsp,	/* pointer to results */
745	struct timeval	utimeout)	/* seconds to wait before giving up */
746{
747	XDR xdrs;
748	struct mbuf *mreq;
749	struct mbuf *mrep;
750	enum clnt_stat stat;
751
752	mreq = m_getcl(M_WAITOK, MT_DATA, 0);
753
754	xdrmbuf_create(&xdrs, mreq, XDR_ENCODE);
755	if (!xargs(&xdrs, argsp)) {
756		m_freem(mreq);
757		return (RPC_CANTENCODEARGS);
758	}
759	XDR_DESTROY(&xdrs);
760
761	stat = CLNT_CALL_MBUF(cl, ext, proc, mreq, &mrep, utimeout);
762	m_freem(mreq);
763
764	if (stat == RPC_SUCCESS) {
765		xdrmbuf_create(&xdrs, mrep, XDR_DECODE);
766		if (!xresults(&xdrs, resultsp)) {
767			XDR_DESTROY(&xdrs);
768			return (RPC_CANTDECODERES);
769		}
770		XDR_DESTROY(&xdrs);
771	}
772
773	return (stat);
774}
775
776/*
777 * Bind a socket to a privileged IP port
778 */
779int
780bindresvport(struct socket *so, struct sockaddr *sa)
781{
782	int old, error, af;
783	bool_t freesa = FALSE;
784	struct sockaddr_in *sin;
785#ifdef INET6
786	struct sockaddr_in6 *sin6;
787#endif
788	struct sockopt opt;
789	int proto, portrange, portlow;
790	u_int16_t *portp;
791	socklen_t salen;
792
793	if (sa == NULL) {
794		error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
795		if (error)
796			return (error);
797		freesa = TRUE;
798		af = sa->sa_family;
799		salen = sa->sa_len;
800		memset(sa, 0, sa->sa_len);
801	} else {
802		af = sa->sa_family;
803		salen = sa->sa_len;
804	}
805
806	switch (af) {
807	case AF_INET:
808		proto = IPPROTO_IP;
809		portrange = IP_PORTRANGE;
810		portlow = IP_PORTRANGE_LOW;
811		sin = (struct sockaddr_in *)sa;
812		portp = &sin->sin_port;
813		break;
814#ifdef INET6
815	case AF_INET6:
816		proto = IPPROTO_IPV6;
817		portrange = IPV6_PORTRANGE;
818		portlow = IPV6_PORTRANGE_LOW;
819		sin6 = (struct sockaddr_in6 *)sa;
820		portp = &sin6->sin6_port;
821		break;
822#endif
823	default:
824		return (EPFNOSUPPORT);
825	}
826
827	sa->sa_family = af;
828	sa->sa_len = salen;
829
830	if (*portp == 0) {
831		bzero(&opt, sizeof(opt));
832		opt.sopt_dir = SOPT_GET;
833		opt.sopt_level = proto;
834		opt.sopt_name = portrange;
835		opt.sopt_val = &old;
836		opt.sopt_valsize = sizeof(old);
837		error = sogetopt(so, &opt);
838		if (error) {
839			goto out;
840		}
841
842		opt.sopt_dir = SOPT_SET;
843		opt.sopt_val = &portlow;
844		error = sosetopt(so, &opt);
845		if (error)
846			goto out;
847	}
848
849	error = sobind(so, sa, curthread);
850
851	if (*portp == 0) {
852		if (error) {
853			opt.sopt_dir = SOPT_SET;
854			opt.sopt_val = &old;
855			sosetopt(so, &opt);
856		}
857	}
858out:
859	if (freesa)
860		free(sa, M_SONAME);
861
862	return (error);
863}
864
865/*
866 * Kernel module glue
867 */
868static int
869krpc_modevent(module_t mod, int type, void *data)
870{
871
872	return (0);
873}
874static moduledata_t krpc_mod = {
875	"krpc",
876	krpc_modevent,
877	NULL,
878};
879DECLARE_MODULE(krpc, krpc_mod, SI_SUB_VFS, SI_ORDER_ANY);
880
881/* So that loader and kldload(2) can find us, wherever we are.. */
882MODULE_VERSION(krpc, 1);
883