rpc_generic.c revision 213103
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: head/sys/rpc/rpc_generic.c 213103 2010-09-24 15:01:45Z attilio $");
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
70struct handle {
71	NCONF_HANDLE *nhandle;
72	int nflag;		/* Whether NETPATH or NETCONFIG */
73	int nettype;
74};
75
76static 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
91struct netid_af {
92	const char	*netid;
93	int		af;
94	int		protocol;
95};
96
97static 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
107struct rpc_createerr rpc_createerr;
108
109/*
110 * Find the appropriate buffer size
111 */
112u_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 */
140u_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 */
165void *
166rpc_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
180int
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 */
228int
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
250struct 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
270char *
271taddr2uaddr(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
280struct netbuf *
281uaddr2taddr(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
290char *
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 (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 (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
349struct 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 (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 (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	}
441out:
442	free(addrstr, M_RPC);
443	return ret;
444}
445
446int
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
463int
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 */
484static int
485getnettype(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 */
507struct 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 */
559void *
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
592failed:
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 */
601struct 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
680void
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
694int
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 */
730enum clnt_stat
731clnt_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 */
775int
776bindresvport(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	}
859out:
860	if (freesa)
861		free(sa, M_SONAME);
862
863	return (error);
864}
865
866/*
867 * Kernel module glue
868 */
869static int
870krpc_modevent(module_t mod, int type, void *data)
871{
872
873	return (0);
874}
875static moduledata_t krpc_mod = {
876	"krpc",
877	krpc_modevent,
878	NULL,
879};
880DECLARE_MODULE(krpc, krpc_mod, SI_SUB_VFS, SI_ORDER_ANY);
881
882/* So that loader and kldload(2) can find us, wherever we are.. */
883MODULE_VERSION(krpc, 1);
884