1/*	$NetBSD: rpc_generic.c,v 1.4 2000/09/28 09:07:04 kleink 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 * Copyright (c) 1986-1991 by Sun Microsystems Inc.
34 */
35
36#include <sys/cdefs.h>
37/*
38 * rpc_generic.c, Miscl routines for RPC.
39 *
40 */
41
42#include "opt_inet6.h"
43
44#include <sys/param.h>
45#include <sys/kernel.h>
46#include <sys/malloc.h>
47#include <sys/mbuf.h>
48#include <sys/module.h>
49#include <sys/proc.h>
50#include <sys/protosw.h>
51#include <sys/sbuf.h>
52#include <sys/systm.h>
53#include <sys/socket.h>
54#include <sys/socketvar.h>
55#include <sys/syslog.h>
56
57#include <net/vnet.h>
58
59#include <rpc/rpc.h>
60#include <rpc/nettype.h>
61#include <rpc/rpcsec_gss.h>
62#include <rpc/rpcsec_tls.h>
63
64#include <rpc/rpc_com.h>
65#include <rpc/krpc.h>
66
67#include <vm/vm.h>
68#include <vm/pmap.h>
69#include <vm/vm_param.h>
70
71extern	u_long sb_max_adj;	/* not defined in socketvar.h */
72
73/* Provide an entry point hook for the rpcsec_gss module. */
74struct rpc_gss_entries	rpc_gss_entries;
75
76struct handle {
77	NCONF_HANDLE *nhandle;
78	int nflag;		/* Whether NETPATH or NETCONFIG */
79	int nettype;
80};
81
82static const struct _rpcnettype {
83	const char *name;
84	const int type;
85} _rpctypelist[] = {
86	{ "netpath", _RPC_NETPATH },
87	{ "visible", _RPC_VISIBLE },
88	{ "circuit_v", _RPC_CIRCUIT_V },
89	{ "datagram_v", _RPC_DATAGRAM_V },
90	{ "circuit_n", _RPC_CIRCUIT_N },
91	{ "datagram_n", _RPC_DATAGRAM_N },
92	{ "tcp", _RPC_TCP },
93	{ "udp", _RPC_UDP },
94	{ 0, _RPC_NONE }
95};
96
97struct netid_af {
98	const char	*netid;
99	int		af;
100	int		protocol;
101};
102
103static const struct netid_af na_cvt[] = {
104	{ "udp",  AF_INET,  IPPROTO_UDP },
105	{ "tcp",  AF_INET,  IPPROTO_TCP },
106#ifdef INET6
107	{ "udp6", AF_INET6, IPPROTO_UDP },
108	{ "tcp6", AF_INET6, IPPROTO_TCP },
109#endif
110	{ "local", AF_LOCAL, 0 }
111};
112
113struct rpc_createerr rpc_createerr;
114
115/*
116 * Find the appropriate buffer size
117 */
118u_int
119/*ARGSUSED*/
120__rpc_get_t_size(int af, int proto, int size)
121{
122	int defsize;
123
124	switch (proto) {
125	case IPPROTO_TCP:
126		defsize = 64 * 1024;	/* XXX */
127		break;
128	case IPPROTO_UDP:
129		defsize = UDPMSGSIZE;
130		break;
131	default:
132		defsize = RPC_MAXDATASIZE;
133		break;
134	}
135	if (size == 0)
136		return defsize;
137
138	/* Check whether the value is within the upper max limit */
139	return (size > sb_max_adj ? (u_int)sb_max_adj : (u_int)size);
140}
141
142/*
143 * Find the appropriate address buffer size
144 */
145u_int
146__rpc_get_a_size(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_storage ss = { .ss_len = sizeof(ss) };
189	sa_family_t family;
190	struct sockopt opt;
191	int error;
192
193	error = sosockaddr(so, (struct sockaddr *)&ss);
194	if (error)
195		return 0;
196
197	sip->si_alen = ss.ss_len;
198	family = ss.ss_family;
199
200	opt.sopt_dir = SOPT_GET;
201	opt.sopt_level = SOL_SOCKET;
202	opt.sopt_name = SO_TYPE;
203	opt.sopt_val = &type;
204	opt.sopt_valsize = sizeof type;
205	opt.sopt_td = NULL;
206	error = sogetopt(so, &opt);
207	if (error)
208		return 0;
209
210	/* XXX */
211	if (family != AF_LOCAL) {
212		if (type == SOCK_STREAM)
213			proto = IPPROTO_TCP;
214		else if (type == SOCK_DGRAM)
215			proto = IPPROTO_UDP;
216		else
217			return 0;
218	} else
219		proto = 0;
220
221	sip->si_af = family;
222	sip->si_proto = proto;
223	sip->si_socktype = type;
224
225	return 1;
226}
227
228/*
229 * Linear search, but the number of entries is small.
230 */
231int
232__rpc_nconf2sockinfo(const struct netconfig *nconf, struct __rpc_sockinfo *sip)
233{
234	int i;
235
236	for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++)
237		if (strcmp(na_cvt[i].netid, nconf->nc_netid) == 0 || (
238		    strcmp(nconf->nc_netid, "unix") == 0 &&
239		    strcmp(na_cvt[i].netid, "local") == 0)) {
240			sip->si_af = na_cvt[i].af;
241			sip->si_proto = na_cvt[i].protocol;
242			sip->si_socktype =
243			    __rpc_seman2socktype((int)nconf->nc_semantics);
244			if (sip->si_socktype == -1)
245				return 0;
246			sip->si_alen = __rpc_get_a_size(sip->si_af);
247			return 1;
248		}
249
250	return 0;
251}
252
253struct socket *
254__rpc_nconf2socket(const struct netconfig *nconf)
255{
256	struct __rpc_sockinfo si;
257	struct socket *so;
258	int error;
259
260	if (!__rpc_nconf2sockinfo(nconf, &si))
261		return 0;
262
263	so = NULL;
264	error =  socreate(si.si_af, &so, si.si_socktype, si.si_proto,
265	    curthread->td_ucred, curthread);
266
267	if (error)
268		return NULL;
269	else
270		return so;
271}
272
273char *
274taddr2uaddr(const struct netconfig *nconf, const struct netbuf *nbuf)
275{
276	struct __rpc_sockinfo si;
277
278	if (!__rpc_nconf2sockinfo(nconf, &si))
279		return NULL;
280	return __rpc_taddr2uaddr_af(si.si_af, nbuf);
281}
282
283struct netbuf *
284uaddr2taddr(const struct netconfig *nconf, const char *uaddr)
285{
286	struct __rpc_sockinfo si;
287
288	if (!__rpc_nconf2sockinfo(nconf, &si))
289		return NULL;
290	return __rpc_uaddr2taddr_af(si.si_af, uaddr);
291}
292
293char *
294__rpc_taddr2uaddr_af(int af, const struct netbuf *nbuf)
295{
296	char *ret;
297	struct sbuf sb;
298	struct sockaddr_in *sin;
299	struct sockaddr_un *sun;
300	char namebuf[INET_ADDRSTRLEN];
301#ifdef INET6
302	struct sockaddr_in6 *sin6;
303	char namebuf6[INET6_ADDRSTRLEN];
304#endif
305	uint16_t port;
306
307	sbuf_new(&sb, NULL, 0, SBUF_AUTOEXTEND);
308
309	switch (af) {
310	case AF_INET:
311		if (nbuf->len < sizeof(*sin))
312			return NULL;
313		sin = nbuf->buf;
314		if (inet_ntop(af, &sin->sin_addr, namebuf, sizeof namebuf)
315		    == NULL)
316			return NULL;
317		port = ntohs(sin->sin_port);
318		if (sbuf_printf(&sb, "%s.%u.%u", namebuf,
319			((uint32_t)port) >> 8,
320			port & 0xff) < 0)
321			return NULL;
322		break;
323#ifdef INET6
324	case AF_INET6:
325		if (nbuf->len < sizeof(*sin6))
326			return NULL;
327		sin6 = nbuf->buf;
328		if (inet_ntop(af, &sin6->sin6_addr, namebuf6, sizeof namebuf6)
329		    == NULL)
330			return NULL;
331		port = ntohs(sin6->sin6_port);
332		if (sbuf_printf(&sb, "%s.%u.%u", namebuf6,
333			((uint32_t)port) >> 8,
334			port & 0xff) < 0)
335			return NULL;
336		break;
337#endif
338	case AF_LOCAL:
339		sun = nbuf->buf;
340		if (sbuf_printf(&sb, "%.*s", (int)(sun->sun_len -
341			    offsetof(struct sockaddr_un, sun_path)),
342			sun->sun_path) < 0)
343			return (NULL);
344		break;
345	default:
346		return NULL;
347	}
348
349	sbuf_finish(&sb);
350	ret = strdup(sbuf_data(&sb), M_RPC);
351	sbuf_delete(&sb);
352
353	return ret;
354}
355
356struct netbuf *
357__rpc_uaddr2taddr_af(int af, const char *uaddr)
358{
359	struct netbuf *ret = NULL;
360	char *addrstr, *p;
361	unsigned port, portlo, porthi;
362	struct sockaddr_in *sin;
363#ifdef INET6
364	struct sockaddr_in6 *sin6;
365#endif
366	struct sockaddr_un *sun;
367
368	port = 0;
369	sin = NULL;
370
371	if (uaddr == NULL)
372		return NULL;
373
374	addrstr = strdup(uaddr, M_RPC);
375	if (addrstr == NULL)
376		return NULL;
377
378	/*
379	 * AF_LOCAL addresses are expected to be absolute
380	 * pathnames, anything else will be AF_INET or AF_INET6.
381	 */
382	if (*addrstr != '/') {
383		p = strrchr(addrstr, '.');
384		if (p == NULL)
385			goto out;
386		portlo = (unsigned)strtol(p + 1, NULL, 10);
387		*p = '\0';
388
389		p = strrchr(addrstr, '.');
390		if (p == NULL)
391			goto out;
392		porthi = (unsigned)strtol(p + 1, NULL, 10);
393		*p = '\0';
394		port = (porthi << 8) | portlo;
395	}
396
397	ret = (struct netbuf *)malloc(sizeof *ret, M_RPC, M_WAITOK);
398
399	switch (af) {
400	case AF_INET:
401		sin = (struct sockaddr_in *)malloc(sizeof *sin, M_RPC,
402		    M_WAITOK);
403		memset(sin, 0, sizeof *sin);
404		sin->sin_family = AF_INET;
405		sin->sin_port = htons(port);
406		if (inet_pton(AF_INET, addrstr, &sin->sin_addr) <= 0) {
407			free(sin, M_RPC);
408			free(ret, M_RPC);
409			ret = NULL;
410			goto out;
411		}
412		sin->sin_len = ret->maxlen = ret->len = sizeof *sin;
413		ret->buf = sin;
414		break;
415#ifdef INET6
416	case AF_INET6:
417		sin6 = (struct sockaddr_in6 *)malloc(sizeof *sin6, M_RPC,
418		    M_WAITOK);
419		memset(sin6, 0, sizeof *sin6);
420		sin6->sin6_family = AF_INET6;
421		sin6->sin6_port = htons(port);
422		if (inet_pton(AF_INET6, addrstr, &sin6->sin6_addr) <= 0) {
423			free(sin6, M_RPC);
424			free(ret, M_RPC);
425			ret = NULL;
426			goto out;
427		}
428		sin6->sin6_len = ret->maxlen = ret->len = sizeof *sin6;
429		ret->buf = sin6;
430		break;
431#endif
432	case AF_LOCAL:
433		sun = (struct sockaddr_un *)malloc(sizeof *sun, M_RPC,
434		    M_WAITOK);
435		memset(sun, 0, sizeof *sun);
436		sun->sun_family = AF_LOCAL;
437		strncpy(sun->sun_path, addrstr, sizeof(sun->sun_path) - 1);
438		ret->len = ret->maxlen = sun->sun_len = SUN_LEN(sun);
439		ret->buf = sun;
440		break;
441	default:
442		break;
443	}
444out:
445	free(addrstr, M_RPC);
446	return ret;
447}
448
449int
450__rpc_seman2socktype(int semantics)
451{
452	switch (semantics) {
453	case NC_TPI_CLTS:
454		return SOCK_DGRAM;
455	case NC_TPI_COTS_ORD:
456		return SOCK_STREAM;
457	case NC_TPI_RAW:
458		return SOCK_RAW;
459	default:
460		break;
461	}
462
463	return -1;
464}
465
466int
467__rpc_socktype2seman(int socktype)
468{
469	switch (socktype) {
470	case SOCK_DGRAM:
471		return NC_TPI_CLTS;
472	case SOCK_STREAM:
473		return NC_TPI_COTS_ORD;
474	case SOCK_RAW:
475		return NC_TPI_RAW;
476	default:
477		break;
478	}
479
480	return -1;
481}
482
483/*
484 * Returns the type of the network as defined in <rpc/nettype.h>
485 * If nettype is NULL, it defaults to NETPATH.
486 */
487static int
488getnettype(const char *nettype)
489{
490	int i;
491
492	if ((nettype == NULL) || (nettype[0] == 0)) {
493		return (_RPC_NETPATH);	/* Default */
494	}
495
496#if 0
497	nettype = strlocase(nettype);
498#endif
499	for (i = 0; _rpctypelist[i].name; i++)
500		if (strcasecmp(nettype, _rpctypelist[i].name) == 0) {
501			return (_rpctypelist[i].type);
502		}
503	return (_rpctypelist[i].type);
504}
505
506/*
507 * For the given nettype (tcp or udp only), return the first structure found.
508 * This should be freed by calling freenetconfigent()
509 */
510struct netconfig *
511__rpc_getconfip(const char *nettype)
512{
513	char *netid;
514	static char *netid_tcp = (char *) NULL;
515	static char *netid_udp = (char *) NULL;
516	struct netconfig *dummy;
517
518	if (!netid_udp && !netid_tcp) {
519		struct netconfig *nconf;
520		void *confighandle;
521
522		if (!(confighandle = setnetconfig())) {
523			log(LOG_ERR, "rpc: failed to open " NETCONFIG);
524			return (NULL);
525		}
526		while ((nconf = getnetconfig(confighandle)) != NULL) {
527			if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
528				if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
529					netid_tcp = strdup(nconf->nc_netid,
530					    M_RPC);
531				} else
532				if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
533					netid_udp = strdup(nconf->nc_netid,
534					    M_RPC);
535				}
536			}
537		}
538		endnetconfig(confighandle);
539	}
540	if (strcmp(nettype, "udp") == 0)
541		netid = netid_udp;
542	else if (strcmp(nettype, "tcp") == 0)
543		netid = netid_tcp;
544	else {
545		return (NULL);
546	}
547	if ((netid == NULL) || (netid[0] == 0)) {
548		return (NULL);
549	}
550	dummy = getnetconfigent(netid);
551	return (dummy);
552}
553
554/*
555 * Returns the type of the nettype, which should then be used with
556 * __rpc_getconf().
557 *
558 * For simplicity in the kernel, we don't support the NETPATH
559 * environment variable. We behave as userland would then NETPATH is
560 * unset, i.e. iterate over all visible entries in netconfig.
561 */
562void *
563__rpc_setconf(const char *nettype)
564{
565	struct handle *handle;
566
567	handle = (struct handle *) malloc(sizeof (struct handle),
568	    M_RPC, M_WAITOK);
569	switch (handle->nettype = getnettype(nettype)) {
570	case _RPC_NETPATH:
571	case _RPC_CIRCUIT_N:
572	case _RPC_DATAGRAM_N:
573		if (!(handle->nhandle = setnetconfig()))
574			goto failed;
575		handle->nflag = TRUE;
576		break;
577	case _RPC_VISIBLE:
578	case _RPC_CIRCUIT_V:
579	case _RPC_DATAGRAM_V:
580	case _RPC_TCP:
581	case _RPC_UDP:
582		if (!(handle->nhandle = setnetconfig())) {
583		        log(LOG_ERR, "rpc: failed to open " NETCONFIG);
584			goto failed;
585		}
586		handle->nflag = FALSE;
587		break;
588	default:
589		goto failed;
590	}
591
592	return (handle);
593
594failed:
595	free(handle, M_RPC);
596	return (NULL);
597}
598
599/*
600 * Returns the next netconfig struct for the given "net" type.
601 * __rpc_setconf() should have been called previously.
602 */
603struct netconfig *
604__rpc_getconf(void *vhandle)
605{
606	struct handle *handle;
607	struct netconfig *nconf;
608
609	handle = (struct handle *)vhandle;
610	if (handle == NULL) {
611		return (NULL);
612	}
613	for (;;) {
614		if (handle->nflag) {
615			nconf = getnetconfig(handle->nhandle);
616			if (nconf && !(nconf->nc_flag & NC_VISIBLE))
617				continue;
618		} else {
619			nconf = getnetconfig(handle->nhandle);
620		}
621		if (nconf == NULL)
622			break;
623		if ((nconf->nc_semantics != NC_TPI_CLTS) &&
624			(nconf->nc_semantics != NC_TPI_COTS) &&
625			(nconf->nc_semantics != NC_TPI_COTS_ORD))
626			continue;
627		switch (handle->nettype) {
628		case _RPC_VISIBLE:
629			if (!(nconf->nc_flag & NC_VISIBLE))
630				continue;
631			/* FALLTHROUGH */
632		case _RPC_NETPATH:	/* Be happy */
633			break;
634		case _RPC_CIRCUIT_V:
635			if (!(nconf->nc_flag & NC_VISIBLE))
636				continue;
637			/* FALLTHROUGH */
638		case _RPC_CIRCUIT_N:
639			if ((nconf->nc_semantics != NC_TPI_COTS) &&
640				(nconf->nc_semantics != NC_TPI_COTS_ORD))
641				continue;
642			break;
643		case _RPC_DATAGRAM_V:
644			if (!(nconf->nc_flag & NC_VISIBLE))
645				continue;
646			/* FALLTHROUGH */
647		case _RPC_DATAGRAM_N:
648			if (nconf->nc_semantics != NC_TPI_CLTS)
649				continue;
650			break;
651		case _RPC_TCP:
652			if (((nconf->nc_semantics != NC_TPI_COTS) &&
653				(nconf->nc_semantics != NC_TPI_COTS_ORD)) ||
654				(strcmp(nconf->nc_protofmly, NC_INET)
655#ifdef INET6
656				 && strcmp(nconf->nc_protofmly, NC_INET6))
657#else
658				)
659#endif
660				||
661				strcmp(nconf->nc_proto, NC_TCP))
662				continue;
663			break;
664		case _RPC_UDP:
665			if ((nconf->nc_semantics != NC_TPI_CLTS) ||
666				(strcmp(nconf->nc_protofmly, NC_INET)
667#ifdef INET6
668				&& strcmp(nconf->nc_protofmly, NC_INET6))
669#else
670				)
671#endif
672				||
673				strcmp(nconf->nc_proto, NC_UDP))
674				continue;
675			break;
676		}
677		break;
678	}
679	return (nconf);
680}
681
682void
683__rpc_endconf(void *vhandle)
684{
685	struct handle *handle;
686
687	handle = (struct handle *) vhandle;
688	if (handle == NULL) {
689		return;
690	}
691	endnetconfig(handle->nhandle);
692	free(handle, M_RPC);
693}
694
695int
696__rpc_sockisbound(struct socket *so)
697{
698	struct sockaddr_storage ss = { .ss_len = sizeof(ss) };
699	int error, bound;
700
701	error = sosockaddr(so, (struct sockaddr *)&ss);
702	if (error)
703		return (0);
704
705	switch (ss.ss_family) {
706		case AF_INET:
707			bound = (((struct sockaddr_in *)&ss)->sin_port != 0);
708			break;
709#ifdef INET6
710		case AF_INET6:
711			bound = (((struct sockaddr_in6 *)&ss)->sin6_port != 0);
712			break;
713#endif
714		case AF_LOCAL:
715			/* XXX check this */
716			bound = (((struct sockaddr_un *)&ss)->sun_path[0] != '\0');
717			break;
718		default:
719			bound = FALSE;
720			break;
721	}
722
723	return bound;
724}
725
726/*
727 * Implement XDR-style API for RPC call.
728 */
729enum clnt_stat
730clnt_call_private(
731	CLIENT		*cl,		/* client handle */
732	struct rpc_callextra *ext,	/* call metadata */
733	rpcproc_t	proc,		/* procedure number */
734	xdrproc_t	xargs,		/* xdr routine for args */
735	void		*argsp,		/* pointer to args */
736	xdrproc_t	xresults,	/* xdr routine for results */
737	void		*resultsp,	/* pointer to results */
738	struct timeval	utimeout)	/* seconds to wait before giving up */
739{
740	XDR xdrs;
741	struct mbuf *mreq;
742	struct mbuf *mrep;
743	enum clnt_stat stat;
744
745	mreq = m_getcl(M_WAITOK, MT_DATA, 0);
746
747	xdrmbuf_create(&xdrs, mreq, XDR_ENCODE);
748	if (!xargs(&xdrs, argsp)) {
749		m_freem(mreq);
750		return (RPC_CANTENCODEARGS);
751	}
752	XDR_DESTROY(&xdrs);
753
754	stat = CLNT_CALL_MBUF(cl, ext, proc, mreq, &mrep, utimeout);
755	m_freem(mreq);
756
757	if (stat == RPC_SUCCESS) {
758		xdrmbuf_create(&xdrs, mrep, XDR_DECODE);
759		if (!xresults(&xdrs, resultsp)) {
760			XDR_DESTROY(&xdrs);
761			return (RPC_CANTDECODERES);
762		}
763		XDR_DESTROY(&xdrs);
764	}
765
766	return (stat);
767}
768
769/*
770 * Bind a socket to a privileged IP port
771 */
772int
773bindresvport(struct socket *so, struct sockaddr *sa)
774{
775	struct sockaddr_storage ss = { .ss_len = sizeof(ss) };
776	int old, error, af;
777	struct sockaddr_in *sin;
778#ifdef INET6
779	struct sockaddr_in6 *sin6;
780#endif
781	struct sockopt opt;
782	int proto, portrange, portlow;
783	uint16_t *portp;
784	socklen_t salen;
785
786	if (sa == NULL) {
787		sa = (struct sockaddr *)&ss;
788		error = sosockaddr(so, sa);
789		if (error)
790			return (error);
791		af = sa->sa_family;
792		salen = sa->sa_len;
793		memset(sa, 0, sa->sa_len);
794	} else {
795		af = sa->sa_family;
796		salen = sa->sa_len;
797	}
798
799	switch (af) {
800	case AF_INET:
801		proto = IPPROTO_IP;
802		portrange = IP_PORTRANGE;
803		portlow = IP_PORTRANGE_LOW;
804		sin = (struct sockaddr_in *)sa;
805		portp = &sin->sin_port;
806		break;
807#ifdef INET6
808	case AF_INET6:
809		proto = IPPROTO_IPV6;
810		portrange = IPV6_PORTRANGE;
811		portlow = IPV6_PORTRANGE_LOW;
812		sin6 = (struct sockaddr_in6 *)sa;
813		portp = &sin6->sin6_port;
814		break;
815#endif
816	default:
817		return (EPFNOSUPPORT);
818	}
819
820	sa->sa_family = af;
821	sa->sa_len = salen;
822
823	if (*portp == 0) {
824		bzero(&opt, sizeof(opt));
825		opt.sopt_dir = SOPT_GET;
826		opt.sopt_level = proto;
827		opt.sopt_name = portrange;
828		opt.sopt_val = &old;
829		opt.sopt_valsize = sizeof(old);
830		error = sogetopt(so, &opt);
831		if (error)
832			return (error);
833
834		opt.sopt_dir = SOPT_SET;
835		opt.sopt_val = &portlow;
836		error = sosetopt(so, &opt);
837		if (error)
838			return (error);
839	}
840
841	error = sobind(so, sa, curthread);
842
843	if (*portp == 0) {
844		if (error) {
845			opt.sopt_dir = SOPT_SET;
846			opt.sopt_val = &old;
847			sosetopt(so, &opt);
848		}
849	}
850
851	return (error);
852}
853
854/*
855 * Make sure an mbuf list is made up entirely of ext_pgs mbufs.
856 * This is needed for sosend() when KERN_TLS is being used.
857 * (There might also be a performance improvement for certain
858 *  network interfaces that handle ext_pgs mbufs efficiently.)
859 * It expects at least one non-ext_pgs mbuf followed by zero
860 * or more ext_pgs mbufs.  It does not handle the case where
861 * non-ext_pgs mbuf(s) follow ext_pgs ones.
862 * It also performs sanity checks on the resultant list.
863 * The "mp" argument list is consumed.
864 * The "maxextsiz" argument is the upper bound on the data
865 * size for each mbuf (usually 16K for KERN_TLS).
866 */
867struct mbuf *
868_rpc_copym_into_ext_pgs(struct mbuf *mp, int maxextsiz)
869{
870	struct mbuf *m, *m2, *m3, *mhead;
871	int tlen;
872
873	KASSERT((mp->m_flags & (M_EXT | M_EXTPG)) !=
874	    (M_EXT | M_EXTPG), ("_rpc_copym_into_ext_pgs:"
875	    " first mbuf is an ext_pgs"));
876	/*
877	 * Find the last non-ext_pgs mbuf and the total
878	 * length of the non-ext_pgs mbuf(s).
879	 * The first mbuf must always be a non-ext_pgs
880	 * mbuf.
881	 */
882	tlen = mp->m_len;
883	m2 = mp;
884	for (m = mp->m_next; m != NULL; m = m->m_next) {
885		if ((m->m_flags & M_EXTPG) != 0)
886			break;
887		tlen += m->m_len;
888		m2 = m;
889	}
890
891	/*
892	 * Copy the non-ext_pgs mbuf(s) into an ext_pgs
893	 * mbuf list.
894	 */
895	m2->m_next = NULL;
896	mhead = mb_mapped_to_unmapped(mp, tlen, maxextsiz,
897	    M_WAITOK, &m2);
898
899	/*
900	 * Link the ext_pgs list onto the newly copied
901	 * list and free up the non-ext_pgs mbuf(s).
902	 */
903	m2->m_next = m;
904	m_freem(mp);
905
906	/*
907	 * Sanity check the resultant mbuf list.  Check for and
908	 * remove any 0 length mbufs in the list, since the
909	 * KERN_TLS code does not expect any 0 length mbuf(s)
910	 * in the list.
911	 */
912	m3 = NULL;
913	m2 = mhead;
914	tlen = 0;
915	while (m2 != NULL) {
916		KASSERT(m2->m_len >= 0, ("_rpc_copym_into_ext_pgs:"
917		    " negative m_len"));
918		KASSERT((m2->m_flags & (M_EXT | M_EXTPG)) ==
919		    (M_EXT | M_EXTPG), ("_rpc_copym_into_ext_pgs:"
920			    " non-nomap mbuf in list"));
921		if (m2->m_len == 0) {
922			if (m3 != NULL)
923				m3->m_next = m2->m_next;
924			else
925				m = m2->m_next;
926			m2->m_next = NULL;
927			m_free(m2);
928			if (m3 != NULL)
929				m2 = m3->m_next;
930			else
931				m2 = m;
932		} else {
933			MBUF_EXT_PGS_ASSERT_SANITY(m2);
934			m3 = m2;
935			tlen += m2->m_len;
936			m2 = m2->m_next;
937		}
938	}
939	return (mhead);
940}
941
942/*
943 * Kernel module glue
944 */
945static int
946krpc_modevent(module_t mod, int type, void *data)
947{
948	int error = 0;
949
950	switch (type) {
951	case MOD_LOAD:
952		error = rpctls_init();
953		break;
954	case MOD_UNLOAD:
955		/*
956		 * Cannot be unloaded, since the rpctlssd or rpctlscd daemons
957		 * might be performing a rpctls syscall.
958		 */
959		/* FALLTHROUGH */
960	default:
961		error = EOPNOTSUPP;
962	}
963	return (error);
964}
965static moduledata_t krpc_mod = {
966	"krpc",
967	krpc_modevent,
968	NULL,
969};
970DECLARE_MODULE(krpc, krpc_mod, SI_SUB_VFS, SI_ORDER_ANY);
971
972/* So that loader and kldload(2) can find us, wherever we are.. */
973MODULE_VERSION(krpc, 1);
974MODULE_DEPEND(krpc, xdr, 1, 1, 1);
975