xref: /freebsd/sys/rpc/rpc_generic.c (revision 568f01f6c3e51ef4b24abe532237081a35e585f6)
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 /* #pragma ident	"@(#)rpc_generic.c	1.17	94/04/24 SMI" */
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39 
40 /*
41  * rpc_generic.c, Miscl routines for RPC.
42  *
43  */
44 
45 #include "opt_inet6.h"
46 
47 #include <sys/param.h>
48 #include <sys/kernel.h>
49 #include <sys/malloc.h>
50 #include <sys/mbuf.h>
51 #include <sys/module.h>
52 #include <sys/proc.h>
53 #include <sys/protosw.h>
54 #include <sys/sbuf.h>
55 #include <sys/systm.h>
56 #include <sys/socket.h>
57 #include <sys/socketvar.h>
58 #include <sys/syslog.h>
59 
60 #include <net/vnet.h>
61 
62 #include <rpc/rpc.h>
63 #include <rpc/nettype.h>
64 #include <rpc/rpcsec_gss.h>
65 
66 #include <rpc/rpc_com.h>
67 
68 extern	u_long sb_max_adj;	/* not defined in socketvar.h */
69 
70 /* Provide an entry point hook for the rpcsec_gss module. */
71 struct rpc_gss_entries	rpc_gss_entries;
72 
73 struct handle {
74 	NCONF_HANDLE *nhandle;
75 	int nflag;		/* Whether NETPATH or NETCONFIG */
76 	int nettype;
77 };
78 
79 static const struct _rpcnettype {
80 	const char *name;
81 	const int type;
82 } _rpctypelist[] = {
83 	{ "netpath", _RPC_NETPATH },
84 	{ "visible", _RPC_VISIBLE },
85 	{ "circuit_v", _RPC_CIRCUIT_V },
86 	{ "datagram_v", _RPC_DATAGRAM_V },
87 	{ "circuit_n", _RPC_CIRCUIT_N },
88 	{ "datagram_n", _RPC_DATAGRAM_N },
89 	{ "tcp", _RPC_TCP },
90 	{ "udp", _RPC_UDP },
91 	{ 0, _RPC_NONE }
92 };
93 
94 struct netid_af {
95 	const char	*netid;
96 	int		af;
97 	int		protocol;
98 };
99 
100 static const struct netid_af na_cvt[] = {
101 	{ "udp",  AF_INET,  IPPROTO_UDP },
102 	{ "tcp",  AF_INET,  IPPROTO_TCP },
103 #ifdef INET6
104 	{ "udp6", AF_INET6, IPPROTO_UDP },
105 	{ "tcp6", AF_INET6, IPPROTO_TCP },
106 #endif
107 	{ "local", AF_LOCAL, 0 }
108 };
109 
110 struct rpc_createerr rpc_createerr;
111 
112 /*
113  * Find the appropriate buffer size
114  */
115 u_int
116 /*ARGSUSED*/
117 __rpc_get_t_size(int af, int proto, int size)
118 {
119 	int defsize;
120 
121 	switch (proto) {
122 	case IPPROTO_TCP:
123 		defsize = 64 * 1024;	/* XXX */
124 		break;
125 	case IPPROTO_UDP:
126 		defsize = UDPMSGSIZE;
127 		break;
128 	default:
129 		defsize = RPC_MAXDATASIZE;
130 		break;
131 	}
132 	if (size == 0)
133 		return defsize;
134 
135 	/* Check whether the value is within the upper max limit */
136 	return (size > sb_max_adj ? (u_int)sb_max_adj : (u_int)size);
137 }
138 
139 /*
140  * Find the appropriate address buffer size
141  */
142 u_int
143 __rpc_get_a_size(af)
144 	int af;
145 {
146 	switch (af) {
147 	case AF_INET:
148 		return sizeof (struct sockaddr_in);
149 #ifdef INET6
150 	case AF_INET6:
151 		return sizeof (struct sockaddr_in6);
152 #endif
153 	case AF_LOCAL:
154 		return sizeof (struct sockaddr_un);
155 	default:
156 		break;
157 	}
158 	return ((u_int)RPC_MAXADDRSIZE);
159 }
160 
161 #if 0
162 
163 /*
164  * Used to ping the NULL procedure for clnt handle.
165  * Returns NULL if fails, else a non-NULL pointer.
166  */
167 void *
168 rpc_nullproc(clnt)
169 	CLIENT *clnt;
170 {
171 	struct timeval TIMEOUT = {25, 0};
172 
173 	if (clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void, NULL,
174 		(xdrproc_t) xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
175 		return (NULL);
176 	}
177 	return ((void *) clnt);
178 }
179 
180 #endif
181 
182 int
183 __rpc_socket2sockinfo(struct socket *so, struct __rpc_sockinfo *sip)
184 {
185 	int type, proto;
186 	struct sockaddr *sa;
187 	sa_family_t family;
188 	struct sockopt opt;
189 	int error;
190 
191 	CURVNET_SET(so->so_vnet);
192 	error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
193 	CURVNET_RESTORE();
194 	if (error)
195 		return 0;
196 
197 	sip->si_alen = sa->sa_len;
198 	family = sa->sa_family;
199 	free(sa, M_SONAME);
200 
201 	opt.sopt_dir = SOPT_GET;
202 	opt.sopt_level = SOL_SOCKET;
203 	opt.sopt_name = SO_TYPE;
204 	opt.sopt_val = &type;
205 	opt.sopt_valsize = sizeof type;
206 	opt.sopt_td = NULL;
207 	error = sogetopt(so, &opt);
208 	if (error)
209 		return 0;
210 
211 	/* XXX */
212 	if (family != AF_LOCAL) {
213 		if (type == SOCK_STREAM)
214 			proto = IPPROTO_TCP;
215 		else if (type == SOCK_DGRAM)
216 			proto = IPPROTO_UDP;
217 		else
218 			return 0;
219 	} else
220 		proto = 0;
221 
222 	sip->si_af = family;
223 	sip->si_proto = proto;
224 	sip->si_socktype = type;
225 
226 	return 1;
227 }
228 
229 /*
230  * Linear search, but the number of entries is small.
231  */
232 int
233 __rpc_nconf2sockinfo(const struct netconfig *nconf, struct __rpc_sockinfo *sip)
234 {
235 	int i;
236 
237 	for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++)
238 		if (strcmp(na_cvt[i].netid, nconf->nc_netid) == 0 || (
239 		    strcmp(nconf->nc_netid, "unix") == 0 &&
240 		    strcmp(na_cvt[i].netid, "local") == 0)) {
241 			sip->si_af = na_cvt[i].af;
242 			sip->si_proto = na_cvt[i].protocol;
243 			sip->si_socktype =
244 			    __rpc_seman2socktype((int)nconf->nc_semantics);
245 			if (sip->si_socktype == -1)
246 				return 0;
247 			sip->si_alen = __rpc_get_a_size(sip->si_af);
248 			return 1;
249 		}
250 
251 	return 0;
252 }
253 
254 struct socket *
255 __rpc_nconf2socket(const struct netconfig *nconf)
256 {
257 	struct __rpc_sockinfo si;
258 	struct socket *so;
259 	int error;
260 
261 	if (!__rpc_nconf2sockinfo(nconf, &si))
262 		return 0;
263 
264 	so = NULL;
265 	error =  socreate(si.si_af, &so, si.si_socktype, si.si_proto,
266 	    curthread->td_ucred, curthread);
267 
268 	if (error)
269 		return NULL;
270 	else
271 		return so;
272 }
273 
274 char *
275 taddr2uaddr(const struct netconfig *nconf, const struct netbuf *nbuf)
276 {
277 	struct __rpc_sockinfo si;
278 
279 	if (!__rpc_nconf2sockinfo(nconf, &si))
280 		return NULL;
281 	return __rpc_taddr2uaddr_af(si.si_af, nbuf);
282 }
283 
284 struct netbuf *
285 uaddr2taddr(const struct netconfig *nconf, const char *uaddr)
286 {
287 	struct __rpc_sockinfo si;
288 
289 	if (!__rpc_nconf2sockinfo(nconf, &si))
290 		return NULL;
291 	return __rpc_uaddr2taddr_af(si.si_af, uaddr);
292 }
293 
294 char *
295 __rpc_taddr2uaddr_af(int af, const struct netbuf *nbuf)
296 {
297 	char *ret;
298 	struct sbuf sb;
299 	struct sockaddr_in *sin;
300 	struct sockaddr_un *sun;
301 	char namebuf[INET_ADDRSTRLEN];
302 #ifdef INET6
303 	struct sockaddr_in6 *sin6;
304 	char namebuf6[INET6_ADDRSTRLEN];
305 #endif
306 	u_int16_t port;
307 
308 	sbuf_new(&sb, NULL, 0, SBUF_AUTOEXTEND);
309 
310 	switch (af) {
311 	case AF_INET:
312 		if (nbuf->len < sizeof(*sin))
313 			return NULL;
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 		if (nbuf->len < sizeof(*sin6))
327 			return NULL;
328 		sin6 = nbuf->buf;
329 		if (inet_ntop(af, &sin6->sin6_addr, namebuf6, sizeof namebuf6)
330 		    == NULL)
331 			return NULL;
332 		port = ntohs(sin6->sin6_port);
333 		if (sbuf_printf(&sb, "%s.%u.%u", namebuf6,
334 			((uint32_t)port) >> 8,
335 			port & 0xff) < 0)
336 			return NULL;
337 		break;
338 #endif
339 	case AF_LOCAL:
340 		sun = nbuf->buf;
341 		if (sbuf_printf(&sb, "%.*s", (int)(sun->sun_len -
342 			    offsetof(struct sockaddr_un, sun_path)),
343 			sun->sun_path) < 0)
344 			return (NULL);
345 		break;
346 	default:
347 		return NULL;
348 	}
349 
350 	sbuf_finish(&sb);
351 	ret = strdup(sbuf_data(&sb), M_RPC);
352 	sbuf_delete(&sb);
353 
354 	return ret;
355 }
356 
357 struct netbuf *
358 __rpc_uaddr2taddr_af(int af, const char *uaddr)
359 {
360 	struct netbuf *ret = NULL;
361 	char *addrstr, *p;
362 	unsigned port, portlo, porthi;
363 	struct sockaddr_in *sin;
364 #ifdef INET6
365 	struct sockaddr_in6 *sin6;
366 #endif
367 	struct sockaddr_un *sun;
368 
369 	port = 0;
370 	sin = NULL;
371 
372 	if (uaddr == NULL)
373 		return NULL;
374 
375 	addrstr = strdup(uaddr, M_RPC);
376 	if (addrstr == NULL)
377 		return NULL;
378 
379 	/*
380 	 * AF_LOCAL addresses are expected to be absolute
381 	 * pathnames, anything else will be AF_INET or AF_INET6.
382 	 */
383 	if (*addrstr != '/') {
384 		p = strrchr(addrstr, '.');
385 		if (p == NULL)
386 			goto out;
387 		portlo = (unsigned)strtol(p + 1, NULL, 10);
388 		*p = '\0';
389 
390 		p = strrchr(addrstr, '.');
391 		if (p == NULL)
392 			goto out;
393 		porthi = (unsigned)strtol(p + 1, NULL, 10);
394 		*p = '\0';
395 		port = (porthi << 8) | portlo;
396 	}
397 
398 	ret = (struct netbuf *)malloc(sizeof *ret, M_RPC, M_WAITOK);
399 
400 	switch (af) {
401 	case AF_INET:
402 		sin = (struct sockaddr_in *)malloc(sizeof *sin, M_RPC,
403 		    M_WAITOK);
404 		memset(sin, 0, sizeof *sin);
405 		sin->sin_family = AF_INET;
406 		sin->sin_port = htons(port);
407 		if (inet_pton(AF_INET, addrstr, &sin->sin_addr) <= 0) {
408 			free(sin, M_RPC);
409 			free(ret, M_RPC);
410 			ret = NULL;
411 			goto out;
412 		}
413 		sin->sin_len = ret->maxlen = ret->len = sizeof *sin;
414 		ret->buf = sin;
415 		break;
416 #ifdef INET6
417 	case AF_INET6:
418 		sin6 = (struct sockaddr_in6 *)malloc(sizeof *sin6, M_RPC,
419 		    M_WAITOK);
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 		memset(sun, 0, sizeof *sun);
437 		sun->sun_family = AF_LOCAL;
438 		strncpy(sun->sun_path, addrstr, sizeof(sun->sun_path) - 1);
439 		ret->len = ret->maxlen = sun->sun_len = SUN_LEN(sun);
440 		ret->buf = sun;
441 		break;
442 	default:
443 		break;
444 	}
445 out:
446 	free(addrstr, M_RPC);
447 	return ret;
448 }
449 
450 int
451 __rpc_seman2socktype(int semantics)
452 {
453 	switch (semantics) {
454 	case NC_TPI_CLTS:
455 		return SOCK_DGRAM;
456 	case NC_TPI_COTS_ORD:
457 		return SOCK_STREAM;
458 	case NC_TPI_RAW:
459 		return SOCK_RAW;
460 	default:
461 		break;
462 	}
463 
464 	return -1;
465 }
466 
467 int
468 __rpc_socktype2seman(int socktype)
469 {
470 	switch (socktype) {
471 	case SOCK_DGRAM:
472 		return NC_TPI_CLTS;
473 	case SOCK_STREAM:
474 		return NC_TPI_COTS_ORD;
475 	case SOCK_RAW:
476 		return NC_TPI_RAW;
477 	default:
478 		break;
479 	}
480 
481 	return -1;
482 }
483 
484 /*
485  * Returns the type of the network as defined in <rpc/nettype.h>
486  * If nettype is NULL, it defaults to NETPATH.
487  */
488 static int
489 getnettype(const char *nettype)
490 {
491 	int i;
492 
493 	if ((nettype == NULL) || (nettype[0] == 0)) {
494 		return (_RPC_NETPATH);	/* Default */
495 	}
496 
497 #if 0
498 	nettype = strlocase(nettype);
499 #endif
500 	for (i = 0; _rpctypelist[i].name; i++)
501 		if (strcasecmp(nettype, _rpctypelist[i].name) == 0) {
502 			return (_rpctypelist[i].type);
503 		}
504 	return (_rpctypelist[i].type);
505 }
506 
507 /*
508  * For the given nettype (tcp or udp only), return the first structure found.
509  * This should be freed by calling freenetconfigent()
510  */
511 struct netconfig *
512 __rpc_getconfip(const char *nettype)
513 {
514 	char *netid;
515 	static char *netid_tcp = (char *) NULL;
516 	static char *netid_udp = (char *) NULL;
517 	struct netconfig *dummy;
518 
519 	if (!netid_udp && !netid_tcp) {
520 		struct netconfig *nconf;
521 		void *confighandle;
522 
523 		if (!(confighandle = setnetconfig())) {
524 			log(LOG_ERR, "rpc: failed to open " NETCONFIG);
525 			return (NULL);
526 		}
527 		while ((nconf = getnetconfig(confighandle)) != NULL) {
528 			if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
529 				if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
530 					netid_tcp = strdup(nconf->nc_netid,
531 					    M_RPC);
532 				} else
533 				if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
534 					netid_udp = strdup(nconf->nc_netid,
535 					    M_RPC);
536 				}
537 			}
538 		}
539 		endnetconfig(confighandle);
540 	}
541 	if (strcmp(nettype, "udp") == 0)
542 		netid = netid_udp;
543 	else if (strcmp(nettype, "tcp") == 0)
544 		netid = netid_tcp;
545 	else {
546 		return (NULL);
547 	}
548 	if ((netid == NULL) || (netid[0] == 0)) {
549 		return (NULL);
550 	}
551 	dummy = getnetconfigent(netid);
552 	return (dummy);
553 }
554 
555 /*
556  * Returns the type of the nettype, which should then be used with
557  * __rpc_getconf().
558  *
559  * For simplicity in the kernel, we don't support the NETPATH
560  * environment variable. We behave as userland would then NETPATH is
561  * unset, i.e. iterate over all visible entries in netconfig.
562  */
563 void *
564 __rpc_setconf(nettype)
565 	const char *nettype;
566 {
567 	struct handle *handle;
568 
569 	handle = (struct handle *) malloc(sizeof (struct handle),
570 	    M_RPC, M_WAITOK);
571 	switch (handle->nettype = getnettype(nettype)) {
572 	case _RPC_NETPATH:
573 	case _RPC_CIRCUIT_N:
574 	case _RPC_DATAGRAM_N:
575 		if (!(handle->nhandle = setnetconfig()))
576 			goto failed;
577 		handle->nflag = TRUE;
578 		break;
579 	case _RPC_VISIBLE:
580 	case _RPC_CIRCUIT_V:
581 	case _RPC_DATAGRAM_V:
582 	case _RPC_TCP:
583 	case _RPC_UDP:
584 		if (!(handle->nhandle = setnetconfig())) {
585 		        log(LOG_ERR, "rpc: failed to open " NETCONFIG);
586 			goto failed;
587 		}
588 		handle->nflag = FALSE;
589 		break;
590 	default:
591 		goto failed;
592 	}
593 
594 	return (handle);
595 
596 failed:
597 	free(handle, M_RPC);
598 	return (NULL);
599 }
600 
601 /*
602  * Returns the next netconfig struct for the given "net" type.
603  * __rpc_setconf() should have been called previously.
604  */
605 struct netconfig *
606 __rpc_getconf(void *vhandle)
607 {
608 	struct handle *handle;
609 	struct netconfig *nconf;
610 
611 	handle = (struct handle *)vhandle;
612 	if (handle == NULL) {
613 		return (NULL);
614 	}
615 	for (;;) {
616 		if (handle->nflag) {
617 			nconf = getnetconfig(handle->nhandle);
618 			if (nconf && !(nconf->nc_flag & NC_VISIBLE))
619 				continue;
620 		} else {
621 			nconf = getnetconfig(handle->nhandle);
622 		}
623 		if (nconf == NULL)
624 			break;
625 		if ((nconf->nc_semantics != NC_TPI_CLTS) &&
626 			(nconf->nc_semantics != NC_TPI_COTS) &&
627 			(nconf->nc_semantics != NC_TPI_COTS_ORD))
628 			continue;
629 		switch (handle->nettype) {
630 		case _RPC_VISIBLE:
631 			if (!(nconf->nc_flag & NC_VISIBLE))
632 				continue;
633 			/* FALLTHROUGH */
634 		case _RPC_NETPATH:	/* Be happy */
635 			break;
636 		case _RPC_CIRCUIT_V:
637 			if (!(nconf->nc_flag & NC_VISIBLE))
638 				continue;
639 			/* FALLTHROUGH */
640 		case _RPC_CIRCUIT_N:
641 			if ((nconf->nc_semantics != NC_TPI_COTS) &&
642 				(nconf->nc_semantics != NC_TPI_COTS_ORD))
643 				continue;
644 			break;
645 		case _RPC_DATAGRAM_V:
646 			if (!(nconf->nc_flag & NC_VISIBLE))
647 				continue;
648 			/* FALLTHROUGH */
649 		case _RPC_DATAGRAM_N:
650 			if (nconf->nc_semantics != NC_TPI_CLTS)
651 				continue;
652 			break;
653 		case _RPC_TCP:
654 			if (((nconf->nc_semantics != NC_TPI_COTS) &&
655 				(nconf->nc_semantics != NC_TPI_COTS_ORD)) ||
656 				(strcmp(nconf->nc_protofmly, NC_INET)
657 #ifdef INET6
658 				 && strcmp(nconf->nc_protofmly, NC_INET6))
659 #else
660 				)
661 #endif
662 				||
663 				strcmp(nconf->nc_proto, NC_TCP))
664 				continue;
665 			break;
666 		case _RPC_UDP:
667 			if ((nconf->nc_semantics != NC_TPI_CLTS) ||
668 				(strcmp(nconf->nc_protofmly, NC_INET)
669 #ifdef INET6
670 				&& strcmp(nconf->nc_protofmly, NC_INET6))
671 #else
672 				)
673 #endif
674 				||
675 				strcmp(nconf->nc_proto, NC_UDP))
676 				continue;
677 			break;
678 		}
679 		break;
680 	}
681 	return (nconf);
682 }
683 
684 void
685 __rpc_endconf(vhandle)
686 	void * vhandle;
687 {
688 	struct handle *handle;
689 
690 	handle = (struct handle *) vhandle;
691 	if (handle == NULL) {
692 		return;
693 	}
694 	endnetconfig(handle->nhandle);
695 	free(handle, M_RPC);
696 }
697 
698 int
699 __rpc_sockisbound(struct socket *so)
700 {
701 	struct sockaddr *sa;
702 	int error, bound;
703 
704 	CURVNET_SET(so->so_vnet);
705 	error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
706 	CURVNET_RESTORE();
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  */
736 enum clnt_stat
737 clnt_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  */
779 int
780 bindresvport(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 		CURVNET_SET(so->so_vnet);
795 		error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
796 		CURVNET_RESTORE();
797 		if (error)
798 			return (error);
799 		freesa = TRUE;
800 		af = sa->sa_family;
801 		salen = sa->sa_len;
802 		memset(sa, 0, sa->sa_len);
803 	} else {
804 		af = sa->sa_family;
805 		salen = sa->sa_len;
806 	}
807 
808 	switch (af) {
809 	case AF_INET:
810 		proto = IPPROTO_IP;
811 		portrange = IP_PORTRANGE;
812 		portlow = IP_PORTRANGE_LOW;
813 		sin = (struct sockaddr_in *)sa;
814 		portp = &sin->sin_port;
815 		break;
816 #ifdef INET6
817 	case AF_INET6:
818 		proto = IPPROTO_IPV6;
819 		portrange = IPV6_PORTRANGE;
820 		portlow = IPV6_PORTRANGE_LOW;
821 		sin6 = (struct sockaddr_in6 *)sa;
822 		portp = &sin6->sin6_port;
823 		break;
824 #endif
825 	default:
826 		return (EPFNOSUPPORT);
827 	}
828 
829 	sa->sa_family = af;
830 	sa->sa_len = salen;
831 
832 	if (*portp == 0) {
833 		bzero(&opt, sizeof(opt));
834 		opt.sopt_dir = SOPT_GET;
835 		opt.sopt_level = proto;
836 		opt.sopt_name = portrange;
837 		opt.sopt_val = &old;
838 		opt.sopt_valsize = sizeof(old);
839 		error = sogetopt(so, &opt);
840 		if (error) {
841 			goto out;
842 		}
843 
844 		opt.sopt_dir = SOPT_SET;
845 		opt.sopt_val = &portlow;
846 		error = sosetopt(so, &opt);
847 		if (error)
848 			goto out;
849 	}
850 
851 	error = sobind(so, sa, curthread);
852 
853 	if (*portp == 0) {
854 		if (error) {
855 			opt.sopt_dir = SOPT_SET;
856 			opt.sopt_val = &old;
857 			sosetopt(so, &opt);
858 		}
859 	}
860 out:
861 	if (freesa)
862 		free(sa, M_SONAME);
863 
864 	return (error);
865 }
866 
867 /*
868  * Kernel module glue
869  */
870 static int
871 krpc_modevent(module_t mod, int type, void *data)
872 {
873 
874 	return (0);
875 }
876 static moduledata_t krpc_mod = {
877 	"krpc",
878 	krpc_modevent,
879 	NULL,
880 };
881 DECLARE_MODULE(krpc, krpc_mod, SI_SUB_VFS, SI_ORDER_ANY);
882 
883 /* So that loader and kldload(2) can find us, wherever we are.. */
884 MODULE_VERSION(krpc, 1);
885