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