xref: /freebsd/sys/rpc/rpc_generic.c (revision aa0a1e58)
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 extern	u_long sb_max_adj;	/* not defined in socketvar.h */
67 
68 #if __FreeBSD_version < 700000
69 #define strrchr rindex
70 #endif
71 
72 struct handle {
73 	NCONF_HANDLE *nhandle;
74 	int nflag;		/* Whether NETPATH or NETCONFIG */
75 	int nettype;
76 };
77 
78 static const struct _rpcnettype {
79 	const char *name;
80 	const int type;
81 } _rpctypelist[] = {
82 	{ "netpath", _RPC_NETPATH },
83 	{ "visible", _RPC_VISIBLE },
84 	{ "circuit_v", _RPC_CIRCUIT_V },
85 	{ "datagram_v", _RPC_DATAGRAM_V },
86 	{ "circuit_n", _RPC_CIRCUIT_N },
87 	{ "datagram_n", _RPC_DATAGRAM_N },
88 	{ "tcp", _RPC_TCP },
89 	{ "udp", _RPC_UDP },
90 	{ 0, _RPC_NONE }
91 };
92 
93 struct netid_af {
94 	const char	*netid;
95 	int		af;
96 	int		protocol;
97 };
98 
99 static const struct netid_af na_cvt[] = {
100 	{ "udp",  AF_INET,  IPPROTO_UDP },
101 	{ "tcp",  AF_INET,  IPPROTO_TCP },
102 #ifdef INET6
103 	{ "udp6", AF_INET6, IPPROTO_UDP },
104 	{ "tcp6", AF_INET6, IPPROTO_TCP },
105 #endif
106 	{ "local", AF_LOCAL, 0 }
107 };
108 
109 struct rpc_createerr rpc_createerr;
110 
111 /*
112  * Find the appropriate buffer size
113  */
114 u_int
115 /*ARGSUSED*/
116 __rpc_get_t_size(int af, int proto, int size)
117 {
118 	int defsize;
119 
120 	switch (proto) {
121 	case IPPROTO_TCP:
122 		defsize = 64 * 1024;	/* XXX */
123 		break;
124 	case IPPROTO_UDP:
125 		defsize = UDPMSGSIZE;
126 		break;
127 	default:
128 		defsize = RPC_MAXDATASIZE;
129 		break;
130 	}
131 	if (size == 0)
132 		return defsize;
133 
134 	/* Check whether the value is within the upper max limit */
135 	return (size > sb_max_adj ? (u_int)sb_max_adj : (u_int)size);
136 }
137 
138 /*
139  * Find the appropriate address buffer size
140  */
141 u_int
142 __rpc_get_a_size(af)
143 	int af;
144 {
145 	switch (af) {
146 	case AF_INET:
147 		return sizeof (struct sockaddr_in);
148 #ifdef INET6
149 	case AF_INET6:
150 		return sizeof (struct sockaddr_in6);
151 #endif
152 	case AF_LOCAL:
153 		return sizeof (struct sockaddr_un);
154 	default:
155 		break;
156 	}
157 	return ((u_int)RPC_MAXADDRSIZE);
158 }
159 
160 #if 0
161 
162 /*
163  * Used to ping the NULL procedure for clnt handle.
164  * Returns NULL if fails, else a non-NULL pointer.
165  */
166 void *
167 rpc_nullproc(clnt)
168 	CLIENT *clnt;
169 {
170 	struct timeval TIMEOUT = {25, 0};
171 
172 	if (clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void, NULL,
173 		(xdrproc_t) xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
174 		return (NULL);
175 	}
176 	return ((void *) clnt);
177 }
178 
179 #endif
180 
181 int
182 __rpc_socket2sockinfo(struct socket *so, struct __rpc_sockinfo *sip)
183 {
184 	int type, proto;
185 	struct sockaddr *sa;
186 	sa_family_t family;
187 	struct sockopt opt;
188 	int error;
189 
190 	CURVNET_SET(so->so_vnet);
191 	error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
192 	CURVNET_RESTORE();
193 	if (error)
194 		return 0;
195 
196 	sip->si_alen = sa->sa_len;
197 	family = sa->sa_family;
198 	free(sa, M_SONAME);
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  */
231 int
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 
253 struct 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 
273 char *
274 taddr2uaddr(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 
283 struct netbuf *
284 uaddr2taddr(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 
293 char *
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 	u_int16_t port;
306 
307 	sbuf_new(&sb, NULL, 0, SBUF_AUTOEXTEND);
308 
309 	switch (af) {
310 	case AF_INET:
311 		sin = nbuf->buf;
312 		if (inet_ntop(af, &sin->sin_addr, namebuf, sizeof namebuf)
313 		    == NULL)
314 			return NULL;
315 		port = ntohs(sin->sin_port);
316 		if (sbuf_printf(&sb, "%s.%u.%u", namebuf,
317 			((uint32_t)port) >> 8,
318 			port & 0xff) < 0)
319 			return NULL;
320 		break;
321 #ifdef INET6
322 	case AF_INET6:
323 		sin6 = nbuf->buf;
324 		if (inet_ntop(af, &sin6->sin6_addr, namebuf6, sizeof namebuf6)
325 		    == NULL)
326 			return NULL;
327 		port = ntohs(sin6->sin6_port);
328 		if (sbuf_printf(&sb, "%s.%u.%u", namebuf6,
329 			((uint32_t)port) >> 8,
330 			port & 0xff) < 0)
331 			return NULL;
332 		break;
333 #endif
334 	case AF_LOCAL:
335 		sun = nbuf->buf;
336 		if (sbuf_printf(&sb, "%.*s", (int)(sun->sun_len -
337 			    offsetof(struct sockaddr_un, sun_path)),
338 			sun->sun_path) < 0)
339 			return (NULL);
340 		break;
341 	default:
342 		return NULL;
343 	}
344 
345 	sbuf_finish(&sb);
346 	ret = strdup(sbuf_data(&sb), M_RPC);
347 	sbuf_delete(&sb);
348 
349 	return ret;
350 }
351 
352 struct netbuf *
353 __rpc_uaddr2taddr_af(int af, const char *uaddr)
354 {
355 	struct netbuf *ret = NULL;
356 	char *addrstr, *p;
357 	unsigned port, portlo, porthi;
358 	struct sockaddr_in *sin;
359 #ifdef INET6
360 	struct sockaddr_in6 *sin6;
361 #endif
362 	struct sockaddr_un *sun;
363 
364 	port = 0;
365 	sin = NULL;
366 	addrstr = strdup(uaddr, M_RPC);
367 	if (addrstr == NULL)
368 		return NULL;
369 
370 	/*
371 	 * AF_LOCAL addresses are expected to be absolute
372 	 * pathnames, anything else will be AF_INET or AF_INET6.
373 	 */
374 	if (*addrstr != '/') {
375 		p = strrchr(addrstr, '.');
376 		if (p == NULL)
377 			goto out;
378 		portlo = (unsigned)strtol(p + 1, NULL, 10);
379 		*p = '\0';
380 
381 		p = strrchr(addrstr, '.');
382 		if (p == NULL)
383 			goto out;
384 		porthi = (unsigned)strtol(p + 1, NULL, 10);
385 		*p = '\0';
386 		port = (porthi << 8) | portlo;
387 	}
388 
389 	ret = (struct netbuf *)malloc(sizeof *ret, M_RPC, M_WAITOK);
390 	if (ret == NULL)
391 		goto out;
392 
393 	switch (af) {
394 	case AF_INET:
395 		sin = (struct sockaddr_in *)malloc(sizeof *sin, M_RPC,
396 		    M_WAITOK);
397 		if (sin == NULL)
398 			goto out;
399 		memset(sin, 0, sizeof *sin);
400 		sin->sin_family = AF_INET;
401 		sin->sin_port = htons(port);
402 		if (inet_pton(AF_INET, addrstr, &sin->sin_addr) <= 0) {
403 			free(sin, M_RPC);
404 			free(ret, M_RPC);
405 			ret = NULL;
406 			goto out;
407 		}
408 		sin->sin_len = ret->maxlen = ret->len = sizeof *sin;
409 		ret->buf = sin;
410 		break;
411 #ifdef INET6
412 	case AF_INET6:
413 		sin6 = (struct sockaddr_in6 *)malloc(sizeof *sin6, M_RPC,
414 		    M_WAITOK);
415 		if (sin6 == NULL)
416 			goto out;
417 		memset(sin6, 0, sizeof *sin6);
418 		sin6->sin6_family = AF_INET6;
419 		sin6->sin6_port = htons(port);
420 		if (inet_pton(AF_INET6, addrstr, &sin6->sin6_addr) <= 0) {
421 			free(sin6, M_RPC);
422 			free(ret, M_RPC);
423 			ret = NULL;
424 			goto out;
425 		}
426 		sin6->sin6_len = ret->maxlen = ret->len = sizeof *sin6;
427 		ret->buf = sin6;
428 		break;
429 #endif
430 	case AF_LOCAL:
431 		sun = (struct sockaddr_un *)malloc(sizeof *sun, M_RPC,
432 		    M_WAITOK);
433 		if (sun == NULL)
434 			goto out;
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 	}
444 out:
445 	free(addrstr, M_RPC);
446 	return ret;
447 }
448 
449 int
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 
466 int
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  */
487 static int
488 getnettype(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  */
510 struct 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  */
562 void *
563 __rpc_setconf(nettype)
564 	const char *nettype;
565 {
566 	struct handle *handle;
567 
568 	handle = (struct handle *) malloc(sizeof (struct handle),
569 	    M_RPC, M_WAITOK);
570 	switch (handle->nettype = getnettype(nettype)) {
571 	case _RPC_NETPATH:
572 	case _RPC_CIRCUIT_N:
573 	case _RPC_DATAGRAM_N:
574 		if (!(handle->nhandle = setnetconfig()))
575 			goto failed;
576 		handle->nflag = TRUE;
577 		break;
578 	case _RPC_VISIBLE:
579 	case _RPC_CIRCUIT_V:
580 	case _RPC_DATAGRAM_V:
581 	case _RPC_TCP:
582 	case _RPC_UDP:
583 		if (!(handle->nhandle = setnetconfig())) {
584 		        log(LOG_ERR, "rpc: failed to open " NETCONFIG);
585 			goto failed;
586 		}
587 		handle->nflag = FALSE;
588 		break;
589 	default:
590 		goto failed;
591 	}
592 
593 	return (handle);
594 
595 failed:
596 	free(handle, M_RPC);
597 	return (NULL);
598 }
599 
600 /*
601  * Returns the next netconfig struct for the given "net" type.
602  * __rpc_setconf() should have been called previously.
603  */
604 struct netconfig *
605 __rpc_getconf(void *vhandle)
606 {
607 	struct handle *handle;
608 	struct netconfig *nconf;
609 
610 	handle = (struct handle *)vhandle;
611 	if (handle == NULL) {
612 		return (NULL);
613 	}
614 	for (;;) {
615 		if (handle->nflag) {
616 			nconf = getnetconfig(handle->nhandle);
617 			if (nconf && !(nconf->nc_flag & NC_VISIBLE))
618 				continue;
619 		} else {
620 			nconf = getnetconfig(handle->nhandle);
621 		}
622 		if (nconf == NULL)
623 			break;
624 		if ((nconf->nc_semantics != NC_TPI_CLTS) &&
625 			(nconf->nc_semantics != NC_TPI_COTS) &&
626 			(nconf->nc_semantics != NC_TPI_COTS_ORD))
627 			continue;
628 		switch (handle->nettype) {
629 		case _RPC_VISIBLE:
630 			if (!(nconf->nc_flag & NC_VISIBLE))
631 				continue;
632 			/* FALLTHROUGH */
633 		case _RPC_NETPATH:	/* Be happy */
634 			break;
635 		case _RPC_CIRCUIT_V:
636 			if (!(nconf->nc_flag & NC_VISIBLE))
637 				continue;
638 			/* FALLTHROUGH */
639 		case _RPC_CIRCUIT_N:
640 			if ((nconf->nc_semantics != NC_TPI_COTS) &&
641 				(nconf->nc_semantics != NC_TPI_COTS_ORD))
642 				continue;
643 			break;
644 		case _RPC_DATAGRAM_V:
645 			if (!(nconf->nc_flag & NC_VISIBLE))
646 				continue;
647 			/* FALLTHROUGH */
648 		case _RPC_DATAGRAM_N:
649 			if (nconf->nc_semantics != NC_TPI_CLTS)
650 				continue;
651 			break;
652 		case _RPC_TCP:
653 			if (((nconf->nc_semantics != NC_TPI_COTS) &&
654 				(nconf->nc_semantics != NC_TPI_COTS_ORD)) ||
655 				(strcmp(nconf->nc_protofmly, NC_INET)
656 #ifdef INET6
657 				 && strcmp(nconf->nc_protofmly, NC_INET6))
658 #else
659 				)
660 #endif
661 				||
662 				strcmp(nconf->nc_proto, NC_TCP))
663 				continue;
664 			break;
665 		case _RPC_UDP:
666 			if ((nconf->nc_semantics != NC_TPI_CLTS) ||
667 				(strcmp(nconf->nc_protofmly, NC_INET)
668 #ifdef INET6
669 				&& strcmp(nconf->nc_protofmly, NC_INET6))
670 #else
671 				)
672 #endif
673 				||
674 				strcmp(nconf->nc_proto, NC_UDP))
675 				continue;
676 			break;
677 		}
678 		break;
679 	}
680 	return (nconf);
681 }
682 
683 void
684 __rpc_endconf(vhandle)
685 	void * vhandle;
686 {
687 	struct handle *handle;
688 
689 	handle = (struct handle *) vhandle;
690 	if (handle == NULL) {
691 		return;
692 	}
693 	endnetconfig(handle->nhandle);
694 	free(handle, M_RPC);
695 }
696 
697 int
698 __rpc_sockisbound(struct socket *so)
699 {
700 	struct sockaddr *sa;
701 	int error, bound;
702 
703 	error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
704 	if (error)
705 		return (0);
706 
707 	switch (sa->sa_family) {
708 		case AF_INET:
709 			bound = (((struct sockaddr_in *) sa)->sin_port != 0);
710 			break;
711 #ifdef INET6
712 		case AF_INET6:
713 			bound = (((struct sockaddr_in6 *) sa)->sin6_port != 0);
714 			break;
715 #endif
716 		case AF_LOCAL:
717 			/* XXX check this */
718 			bound = (((struct sockaddr_un *) sa)->sun_path[0] != '\0');
719 			break;
720 		default:
721 			bound = FALSE;
722 			break;
723 	}
724 
725 	free(sa, M_SONAME);
726 
727 	return bound;
728 }
729 
730 /*
731  * Implement XDR-style API for RPC call.
732  */
733 enum clnt_stat
734 clnt_call_private(
735 	CLIENT		*cl,		/* client handle */
736 	struct rpc_callextra *ext,	/* call metadata */
737 	rpcproc_t	proc,		/* procedure number */
738 	xdrproc_t	xargs,		/* xdr routine for args */
739 	void		*argsp,		/* pointer to args */
740 	xdrproc_t	xresults,	/* xdr routine for results */
741 	void		*resultsp,	/* pointer to results */
742 	struct timeval	utimeout)	/* seconds to wait before giving up */
743 {
744 	XDR xdrs;
745 	struct mbuf *mreq;
746 	struct mbuf *mrep;
747 	enum clnt_stat stat;
748 
749 	MGET(mreq, M_WAIT, MT_DATA);
750 	MCLGET(mreq, M_WAIT);
751 	mreq->m_len = 0;
752 
753 	xdrmbuf_create(&xdrs, mreq, XDR_ENCODE);
754 	if (!xargs(&xdrs, argsp)) {
755 		m_freem(mreq);
756 		return (RPC_CANTENCODEARGS);
757 	}
758 	XDR_DESTROY(&xdrs);
759 
760 	stat = CLNT_CALL_MBUF(cl, ext, proc, mreq, &mrep, utimeout);
761 	m_freem(mreq);
762 
763 	if (stat == RPC_SUCCESS) {
764 		xdrmbuf_create(&xdrs, mrep, XDR_DECODE);
765 		if (!xresults(&xdrs, resultsp)) {
766 			XDR_DESTROY(&xdrs);
767 			return (RPC_CANTDECODERES);
768 		}
769 		XDR_DESTROY(&xdrs);
770 	}
771 
772 	return (stat);
773 }
774 
775 /*
776  * Bind a socket to a privileged IP port
777  */
778 int
779 bindresvport(struct socket *so, struct sockaddr *sa)
780 {
781 	int old, error, af;
782 	bool_t freesa = FALSE;
783 	struct sockaddr_in *sin;
784 #ifdef INET6
785 	struct sockaddr_in6 *sin6;
786 #endif
787 	struct sockopt opt;
788 	int proto, portrange, portlow;
789 	u_int16_t *portp;
790 	socklen_t salen;
791 
792 	if (sa == NULL) {
793 		error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
794 		if (error)
795 			return (error);
796 		freesa = TRUE;
797 		af = sa->sa_family;
798 		salen = sa->sa_len;
799 		memset(sa, 0, sa->sa_len);
800 	} else {
801 		af = sa->sa_family;
802 		salen = sa->sa_len;
803 	}
804 
805 	switch (af) {
806 	case AF_INET:
807 		proto = IPPROTO_IP;
808 		portrange = IP_PORTRANGE;
809 		portlow = IP_PORTRANGE_LOW;
810 		sin = (struct sockaddr_in *)sa;
811 		portp = &sin->sin_port;
812 		break;
813 #ifdef INET6
814 	case AF_INET6:
815 		proto = IPPROTO_IPV6;
816 		portrange = IPV6_PORTRANGE;
817 		portlow = IPV6_PORTRANGE_LOW;
818 		sin6 = (struct sockaddr_in6 *)sa;
819 		portp = &sin6->sin6_port;
820 		break;
821 #endif
822 	default:
823 		return (EPFNOSUPPORT);
824 	}
825 
826 	sa->sa_family = af;
827 	sa->sa_len = salen;
828 
829 	if (*portp == 0) {
830 		bzero(&opt, sizeof(opt));
831 		opt.sopt_dir = SOPT_GET;
832 		opt.sopt_level = proto;
833 		opt.sopt_name = portrange;
834 		opt.sopt_val = &old;
835 		opt.sopt_valsize = sizeof(old);
836 		error = sogetopt(so, &opt);
837 		if (error) {
838 			goto out;
839 		}
840 
841 		opt.sopt_dir = SOPT_SET;
842 		opt.sopt_val = &portlow;
843 		error = sosetopt(so, &opt);
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 			sosetopt(so, &opt);
855 		}
856 	}
857 out:
858 	if (freesa)
859 		free(sa, M_SONAME);
860 
861 	return (error);
862 }
863 
864 /*
865  * Kernel module glue
866  */
867 static int
868 krpc_modevent(module_t mod, int type, void *data)
869 {
870 
871 	return (0);
872 }
873 static moduledata_t krpc_mod = {
874 	"krpc",
875 	krpc_modevent,
876 	NULL,
877 };
878 DECLARE_MODULE(krpc, krpc_mod, SI_SUB_VFS, SI_ORDER_ANY);
879 
880 /* So that loader and kldload(2) can find us, wherever we are.. */
881 MODULE_VERSION(krpc, 1);
882