xref: /freebsd/sys/rpc/rpc_generic.c (revision f05cddf9)
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 #include <rpc/rpcsec_gss.h>
64 
65 #include <rpc/rpc_com.h>
66 
67 extern	u_long sb_max_adj;	/* not defined in socketvar.h */
68 
69 #if __FreeBSD_version < 700000
70 #define strrchr rindex
71 #endif
72 
73 /* Provide an entry point hook for the rpcsec_gss module. */
74 struct rpc_gss_entries	rpc_gss_entries;
75 
76 struct handle {
77 	NCONF_HANDLE *nhandle;
78 	int nflag;		/* Whether NETPATH or NETCONFIG */
79 	int nettype;
80 };
81 
82 static 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 
97 struct netid_af {
98 	const char	*netid;
99 	int		af;
100 	int		protocol;
101 };
102 
103 static 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 
113 struct rpc_createerr rpc_createerr;
114 
115 /*
116  * Find the appropriate buffer size
117  */
118 u_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  */
145 u_int
146 __rpc_get_a_size(af)
147 	int af;
148 {
149 	switch (af) {
150 	case AF_INET:
151 		return sizeof (struct sockaddr_in);
152 #ifdef INET6
153 	case AF_INET6:
154 		return sizeof (struct sockaddr_in6);
155 #endif
156 	case AF_LOCAL:
157 		return sizeof (struct sockaddr_un);
158 	default:
159 		break;
160 	}
161 	return ((u_int)RPC_MAXADDRSIZE);
162 }
163 
164 #if 0
165 
166 /*
167  * Used to ping the NULL procedure for clnt handle.
168  * Returns NULL if fails, else a non-NULL pointer.
169  */
170 void *
171 rpc_nullproc(clnt)
172 	CLIENT *clnt;
173 {
174 	struct timeval TIMEOUT = {25, 0};
175 
176 	if (clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void, NULL,
177 		(xdrproc_t) xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
178 		return (NULL);
179 	}
180 	return ((void *) clnt);
181 }
182 
183 #endif
184 
185 int
186 __rpc_socket2sockinfo(struct socket *so, struct __rpc_sockinfo *sip)
187 {
188 	int type, proto;
189 	struct sockaddr *sa;
190 	sa_family_t family;
191 	struct sockopt opt;
192 	int error;
193 
194 	CURVNET_SET(so->so_vnet);
195 	error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
196 	CURVNET_RESTORE();
197 	if (error)
198 		return 0;
199 
200 	sip->si_alen = sa->sa_len;
201 	family = sa->sa_family;
202 	free(sa, M_SONAME);
203 
204 	opt.sopt_dir = SOPT_GET;
205 	opt.sopt_level = SOL_SOCKET;
206 	opt.sopt_name = SO_TYPE;
207 	opt.sopt_val = &type;
208 	opt.sopt_valsize = sizeof type;
209 	opt.sopt_td = NULL;
210 	error = sogetopt(so, &opt);
211 	if (error)
212 		return 0;
213 
214 	/* XXX */
215 	if (family != AF_LOCAL) {
216 		if (type == SOCK_STREAM)
217 			proto = IPPROTO_TCP;
218 		else if (type == SOCK_DGRAM)
219 			proto = IPPROTO_UDP;
220 		else
221 			return 0;
222 	} else
223 		proto = 0;
224 
225 	sip->si_af = family;
226 	sip->si_proto = proto;
227 	sip->si_socktype = type;
228 
229 	return 1;
230 }
231 
232 /*
233  * Linear search, but the number of entries is small.
234  */
235 int
236 __rpc_nconf2sockinfo(const struct netconfig *nconf, struct __rpc_sockinfo *sip)
237 {
238 	int i;
239 
240 	for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++)
241 		if (strcmp(na_cvt[i].netid, nconf->nc_netid) == 0 || (
242 		    strcmp(nconf->nc_netid, "unix") == 0 &&
243 		    strcmp(na_cvt[i].netid, "local") == 0)) {
244 			sip->si_af = na_cvt[i].af;
245 			sip->si_proto = na_cvt[i].protocol;
246 			sip->si_socktype =
247 			    __rpc_seman2socktype((int)nconf->nc_semantics);
248 			if (sip->si_socktype == -1)
249 				return 0;
250 			sip->si_alen = __rpc_get_a_size(sip->si_af);
251 			return 1;
252 		}
253 
254 	return 0;
255 }
256 
257 struct socket *
258 __rpc_nconf2socket(const struct netconfig *nconf)
259 {
260 	struct __rpc_sockinfo si;
261 	struct socket *so;
262 	int error;
263 
264 	if (!__rpc_nconf2sockinfo(nconf, &si))
265 		return 0;
266 
267 	so = NULL;
268 	error =  socreate(si.si_af, &so, si.si_socktype, si.si_proto,
269 	    curthread->td_ucred, curthread);
270 
271 	if (error)
272 		return NULL;
273 	else
274 		return so;
275 }
276 
277 char *
278 taddr2uaddr(const struct netconfig *nconf, const struct netbuf *nbuf)
279 {
280 	struct __rpc_sockinfo si;
281 
282 	if (!__rpc_nconf2sockinfo(nconf, &si))
283 		return NULL;
284 	return __rpc_taddr2uaddr_af(si.si_af, nbuf);
285 }
286 
287 struct netbuf *
288 uaddr2taddr(const struct netconfig *nconf, const char *uaddr)
289 {
290 	struct __rpc_sockinfo si;
291 
292 	if (!__rpc_nconf2sockinfo(nconf, &si))
293 		return NULL;
294 	return __rpc_uaddr2taddr_af(si.si_af, uaddr);
295 }
296 
297 char *
298 __rpc_taddr2uaddr_af(int af, const struct netbuf *nbuf)
299 {
300 	char *ret;
301 	struct sbuf sb;
302 	struct sockaddr_in *sin;
303 	struct sockaddr_un *sun;
304 	char namebuf[INET_ADDRSTRLEN];
305 #ifdef INET6
306 	struct sockaddr_in6 *sin6;
307 	char namebuf6[INET6_ADDRSTRLEN];
308 #endif
309 	u_int16_t port;
310 
311 	sbuf_new(&sb, NULL, 0, SBUF_AUTOEXTEND);
312 
313 	switch (af) {
314 	case AF_INET:
315 		sin = nbuf->buf;
316 		if (inet_ntop(af, &sin->sin_addr, namebuf, sizeof namebuf)
317 		    == NULL)
318 			return NULL;
319 		port = ntohs(sin->sin_port);
320 		if (sbuf_printf(&sb, "%s.%u.%u", namebuf,
321 			((uint32_t)port) >> 8,
322 			port & 0xff) < 0)
323 			return NULL;
324 		break;
325 #ifdef INET6
326 	case AF_INET6:
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 
356 struct 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 	addrstr = strdup(uaddr, M_RPC);
371 	if (addrstr == NULL)
372 		return NULL;
373 
374 	/*
375 	 * AF_LOCAL addresses are expected to be absolute
376 	 * pathnames, anything else will be AF_INET or AF_INET6.
377 	 */
378 	if (*addrstr != '/') {
379 		p = strrchr(addrstr, '.');
380 		if (p == NULL)
381 			goto out;
382 		portlo = (unsigned)strtol(p + 1, NULL, 10);
383 		*p = '\0';
384 
385 		p = strrchr(addrstr, '.');
386 		if (p == NULL)
387 			goto out;
388 		porthi = (unsigned)strtol(p + 1, NULL, 10);
389 		*p = '\0';
390 		port = (porthi << 8) | portlo;
391 	}
392 
393 	ret = (struct netbuf *)malloc(sizeof *ret, M_RPC, M_WAITOK);
394 	if (ret == NULL)
395 		goto out;
396 
397 	switch (af) {
398 	case AF_INET:
399 		sin = (struct sockaddr_in *)malloc(sizeof *sin, M_RPC,
400 		    M_WAITOK);
401 		if (sin == NULL)
402 			goto out;
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 		if (sin6 == NULL)
420 			goto out;
421 		memset(sin6, 0, sizeof *sin6);
422 		sin6->sin6_family = AF_INET6;
423 		sin6->sin6_port = htons(port);
424 		if (inet_pton(AF_INET6, addrstr, &sin6->sin6_addr) <= 0) {
425 			free(sin6, M_RPC);
426 			free(ret, M_RPC);
427 			ret = NULL;
428 			goto out;
429 		}
430 		sin6->sin6_len = ret->maxlen = ret->len = sizeof *sin6;
431 		ret->buf = sin6;
432 		break;
433 #endif
434 	case AF_LOCAL:
435 		sun = (struct sockaddr_un *)malloc(sizeof *sun, M_RPC,
436 		    M_WAITOK);
437 		if (sun == NULL)
438 			goto out;
439 		memset(sun, 0, sizeof *sun);
440 		sun->sun_family = AF_LOCAL;
441 		strncpy(sun->sun_path, addrstr, sizeof(sun->sun_path) - 1);
442 		ret->len = ret->maxlen = sun->sun_len = SUN_LEN(sun);
443 		ret->buf = sun;
444 		break;
445 	default:
446 		break;
447 	}
448 out:
449 	free(addrstr, M_RPC);
450 	return ret;
451 }
452 
453 int
454 __rpc_seman2socktype(int semantics)
455 {
456 	switch (semantics) {
457 	case NC_TPI_CLTS:
458 		return SOCK_DGRAM;
459 	case NC_TPI_COTS_ORD:
460 		return SOCK_STREAM;
461 	case NC_TPI_RAW:
462 		return SOCK_RAW;
463 	default:
464 		break;
465 	}
466 
467 	return -1;
468 }
469 
470 int
471 __rpc_socktype2seman(int socktype)
472 {
473 	switch (socktype) {
474 	case SOCK_DGRAM:
475 		return NC_TPI_CLTS;
476 	case SOCK_STREAM:
477 		return NC_TPI_COTS_ORD;
478 	case SOCK_RAW:
479 		return NC_TPI_RAW;
480 	default:
481 		break;
482 	}
483 
484 	return -1;
485 }
486 
487 /*
488  * Returns the type of the network as defined in <rpc/nettype.h>
489  * If nettype is NULL, it defaults to NETPATH.
490  */
491 static int
492 getnettype(const char *nettype)
493 {
494 	int i;
495 
496 	if ((nettype == NULL) || (nettype[0] == 0)) {
497 		return (_RPC_NETPATH);	/* Default */
498 	}
499 
500 #if 0
501 	nettype = strlocase(nettype);
502 #endif
503 	for (i = 0; _rpctypelist[i].name; i++)
504 		if (strcasecmp(nettype, _rpctypelist[i].name) == 0) {
505 			return (_rpctypelist[i].type);
506 		}
507 	return (_rpctypelist[i].type);
508 }
509 
510 /*
511  * For the given nettype (tcp or udp only), return the first structure found.
512  * This should be freed by calling freenetconfigent()
513  */
514 struct netconfig *
515 __rpc_getconfip(const char *nettype)
516 {
517 	char *netid;
518 	static char *netid_tcp = (char *) NULL;
519 	static char *netid_udp = (char *) NULL;
520 	struct netconfig *dummy;
521 
522 	if (!netid_udp && !netid_tcp) {
523 		struct netconfig *nconf;
524 		void *confighandle;
525 
526 		if (!(confighandle = setnetconfig())) {
527 			log(LOG_ERR, "rpc: failed to open " NETCONFIG);
528 			return (NULL);
529 		}
530 		while ((nconf = getnetconfig(confighandle)) != NULL) {
531 			if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
532 				if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
533 					netid_tcp = strdup(nconf->nc_netid,
534 					    M_RPC);
535 				} else
536 				if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
537 					netid_udp = strdup(nconf->nc_netid,
538 					    M_RPC);
539 				}
540 			}
541 		}
542 		endnetconfig(confighandle);
543 	}
544 	if (strcmp(nettype, "udp") == 0)
545 		netid = netid_udp;
546 	else if (strcmp(nettype, "tcp") == 0)
547 		netid = netid_tcp;
548 	else {
549 		return (NULL);
550 	}
551 	if ((netid == NULL) || (netid[0] == 0)) {
552 		return (NULL);
553 	}
554 	dummy = getnetconfigent(netid);
555 	return (dummy);
556 }
557 
558 /*
559  * Returns the type of the nettype, which should then be used with
560  * __rpc_getconf().
561  *
562  * For simplicity in the kernel, we don't support the NETPATH
563  * environment variable. We behave as userland would then NETPATH is
564  * unset, i.e. iterate over all visible entries in netconfig.
565  */
566 void *
567 __rpc_setconf(nettype)
568 	const char *nettype;
569 {
570 	struct handle *handle;
571 
572 	handle = (struct handle *) malloc(sizeof (struct handle),
573 	    M_RPC, M_WAITOK);
574 	switch (handle->nettype = getnettype(nettype)) {
575 	case _RPC_NETPATH:
576 	case _RPC_CIRCUIT_N:
577 	case _RPC_DATAGRAM_N:
578 		if (!(handle->nhandle = setnetconfig()))
579 			goto failed;
580 		handle->nflag = TRUE;
581 		break;
582 	case _RPC_VISIBLE:
583 	case _RPC_CIRCUIT_V:
584 	case _RPC_DATAGRAM_V:
585 	case _RPC_TCP:
586 	case _RPC_UDP:
587 		if (!(handle->nhandle = setnetconfig())) {
588 		        log(LOG_ERR, "rpc: failed to open " NETCONFIG);
589 			goto failed;
590 		}
591 		handle->nflag = FALSE;
592 		break;
593 	default:
594 		goto failed;
595 	}
596 
597 	return (handle);
598 
599 failed:
600 	free(handle, M_RPC);
601 	return (NULL);
602 }
603 
604 /*
605  * Returns the next netconfig struct for the given "net" type.
606  * __rpc_setconf() should have been called previously.
607  */
608 struct netconfig *
609 __rpc_getconf(void *vhandle)
610 {
611 	struct handle *handle;
612 	struct netconfig *nconf;
613 
614 	handle = (struct handle *)vhandle;
615 	if (handle == NULL) {
616 		return (NULL);
617 	}
618 	for (;;) {
619 		if (handle->nflag) {
620 			nconf = getnetconfig(handle->nhandle);
621 			if (nconf && !(nconf->nc_flag & NC_VISIBLE))
622 				continue;
623 		} else {
624 			nconf = getnetconfig(handle->nhandle);
625 		}
626 		if (nconf == NULL)
627 			break;
628 		if ((nconf->nc_semantics != NC_TPI_CLTS) &&
629 			(nconf->nc_semantics != NC_TPI_COTS) &&
630 			(nconf->nc_semantics != NC_TPI_COTS_ORD))
631 			continue;
632 		switch (handle->nettype) {
633 		case _RPC_VISIBLE:
634 			if (!(nconf->nc_flag & NC_VISIBLE))
635 				continue;
636 			/* FALLTHROUGH */
637 		case _RPC_NETPATH:	/* Be happy */
638 			break;
639 		case _RPC_CIRCUIT_V:
640 			if (!(nconf->nc_flag & NC_VISIBLE))
641 				continue;
642 			/* FALLTHROUGH */
643 		case _RPC_CIRCUIT_N:
644 			if ((nconf->nc_semantics != NC_TPI_COTS) &&
645 				(nconf->nc_semantics != NC_TPI_COTS_ORD))
646 				continue;
647 			break;
648 		case _RPC_DATAGRAM_V:
649 			if (!(nconf->nc_flag & NC_VISIBLE))
650 				continue;
651 			/* FALLTHROUGH */
652 		case _RPC_DATAGRAM_N:
653 			if (nconf->nc_semantics != NC_TPI_CLTS)
654 				continue;
655 			break;
656 		case _RPC_TCP:
657 			if (((nconf->nc_semantics != NC_TPI_COTS) &&
658 				(nconf->nc_semantics != NC_TPI_COTS_ORD)) ||
659 				(strcmp(nconf->nc_protofmly, NC_INET)
660 #ifdef INET6
661 				 && strcmp(nconf->nc_protofmly, NC_INET6))
662 #else
663 				)
664 #endif
665 				||
666 				strcmp(nconf->nc_proto, NC_TCP))
667 				continue;
668 			break;
669 		case _RPC_UDP:
670 			if ((nconf->nc_semantics != NC_TPI_CLTS) ||
671 				(strcmp(nconf->nc_protofmly, NC_INET)
672 #ifdef INET6
673 				&& strcmp(nconf->nc_protofmly, NC_INET6))
674 #else
675 				)
676 #endif
677 				||
678 				strcmp(nconf->nc_proto, NC_UDP))
679 				continue;
680 			break;
681 		}
682 		break;
683 	}
684 	return (nconf);
685 }
686 
687 void
688 __rpc_endconf(vhandle)
689 	void * vhandle;
690 {
691 	struct handle *handle;
692 
693 	handle = (struct handle *) vhandle;
694 	if (handle == NULL) {
695 		return;
696 	}
697 	endnetconfig(handle->nhandle);
698 	free(handle, M_RPC);
699 }
700 
701 int
702 __rpc_sockisbound(struct socket *so)
703 {
704 	struct sockaddr *sa;
705 	int error, bound;
706 
707 	error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
708 	if (error)
709 		return (0);
710 
711 	switch (sa->sa_family) {
712 		case AF_INET:
713 			bound = (((struct sockaddr_in *) sa)->sin_port != 0);
714 			break;
715 #ifdef INET6
716 		case AF_INET6:
717 			bound = (((struct sockaddr_in6 *) sa)->sin6_port != 0);
718 			break;
719 #endif
720 		case AF_LOCAL:
721 			/* XXX check this */
722 			bound = (((struct sockaddr_un *) sa)->sun_path[0] != '\0');
723 			break;
724 		default:
725 			bound = FALSE;
726 			break;
727 	}
728 
729 	free(sa, M_SONAME);
730 
731 	return bound;
732 }
733 
734 /*
735  * Implement XDR-style API for RPC call.
736  */
737 enum clnt_stat
738 clnt_call_private(
739 	CLIENT		*cl,		/* client handle */
740 	struct rpc_callextra *ext,	/* call metadata */
741 	rpcproc_t	proc,		/* procedure number */
742 	xdrproc_t	xargs,		/* xdr routine for args */
743 	void		*argsp,		/* pointer to args */
744 	xdrproc_t	xresults,	/* xdr routine for results */
745 	void		*resultsp,	/* pointer to results */
746 	struct timeval	utimeout)	/* seconds to wait before giving up */
747 {
748 	XDR xdrs;
749 	struct mbuf *mreq;
750 	struct mbuf *mrep;
751 	enum clnt_stat stat;
752 
753 	mreq = m_getcl(M_WAITOK, MT_DATA, 0);
754 
755 	xdrmbuf_create(&xdrs, mreq, XDR_ENCODE);
756 	if (!xargs(&xdrs, argsp)) {
757 		m_freem(mreq);
758 		return (RPC_CANTENCODEARGS);
759 	}
760 	XDR_DESTROY(&xdrs);
761 
762 	stat = CLNT_CALL_MBUF(cl, ext, proc, mreq, &mrep, utimeout);
763 	m_freem(mreq);
764 
765 	if (stat == RPC_SUCCESS) {
766 		xdrmbuf_create(&xdrs, mrep, XDR_DECODE);
767 		if (!xresults(&xdrs, resultsp)) {
768 			XDR_DESTROY(&xdrs);
769 			return (RPC_CANTDECODERES);
770 		}
771 		XDR_DESTROY(&xdrs);
772 	}
773 
774 	return (stat);
775 }
776 
777 /*
778  * Bind a socket to a privileged IP port
779  */
780 int
781 bindresvport(struct socket *so, struct sockaddr *sa)
782 {
783 	int old, error, af;
784 	bool_t freesa = FALSE;
785 	struct sockaddr_in *sin;
786 #ifdef INET6
787 	struct sockaddr_in6 *sin6;
788 #endif
789 	struct sockopt opt;
790 	int proto, portrange, portlow;
791 	u_int16_t *portp;
792 	socklen_t salen;
793 
794 	if (sa == NULL) {
795 		error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
796 		if (error)
797 			return (error);
798 		freesa = TRUE;
799 		af = sa->sa_family;
800 		salen = sa->sa_len;
801 		memset(sa, 0, sa->sa_len);
802 	} else {
803 		af = sa->sa_family;
804 		salen = sa->sa_len;
805 	}
806 
807 	switch (af) {
808 	case AF_INET:
809 		proto = IPPROTO_IP;
810 		portrange = IP_PORTRANGE;
811 		portlow = IP_PORTRANGE_LOW;
812 		sin = (struct sockaddr_in *)sa;
813 		portp = &sin->sin_port;
814 		break;
815 #ifdef INET6
816 	case AF_INET6:
817 		proto = IPPROTO_IPV6;
818 		portrange = IPV6_PORTRANGE;
819 		portlow = IPV6_PORTRANGE_LOW;
820 		sin6 = (struct sockaddr_in6 *)sa;
821 		portp = &sin6->sin6_port;
822 		break;
823 #endif
824 	default:
825 		return (EPFNOSUPPORT);
826 	}
827 
828 	sa->sa_family = af;
829 	sa->sa_len = salen;
830 
831 	if (*portp == 0) {
832 		bzero(&opt, sizeof(opt));
833 		opt.sopt_dir = SOPT_GET;
834 		opt.sopt_level = proto;
835 		opt.sopt_name = portrange;
836 		opt.sopt_val = &old;
837 		opt.sopt_valsize = sizeof(old);
838 		error = sogetopt(so, &opt);
839 		if (error) {
840 			goto out;
841 		}
842 
843 		opt.sopt_dir = SOPT_SET;
844 		opt.sopt_val = &portlow;
845 		error = sosetopt(so, &opt);
846 		if (error)
847 			goto out;
848 	}
849 
850 	error = sobind(so, sa, curthread);
851 
852 	if (*portp == 0) {
853 		if (error) {
854 			opt.sopt_dir = SOPT_SET;
855 			opt.sopt_val = &old;
856 			sosetopt(so, &opt);
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