summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/rpc/rpc_generic.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/rpc/rpc_generic.c')
-rw-r--r--freebsd/sys/rpc/rpc_generic.c888
1 files changed, 888 insertions, 0 deletions
diff --git a/freebsd/sys/rpc/rpc_generic.c b/freebsd/sys/rpc/rpc_generic.c
new file mode 100644
index 00000000..a4d6298b
--- /dev/null
+++ b/freebsd/sys/rpc/rpc_generic.c
@@ -0,0 +1,888 @@
+/* $NetBSD: rpc_generic.c,v 1.4 2000/09/28 09:07:04 kleink Exp $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2009, Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Sun Microsystems, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1986-1991 by Sun Microsystems Inc.
+ */
+
+/* #pragma ident "@(#)rpc_generic.c 1.17 94/04/24 SMI" */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * rpc_generic.c, Miscl routines for RPC.
+ *
+ */
+
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/protosw.h>
+#include <sys/sbuf.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/syslog.h>
+
+#include <net/vnet.h>
+
+#include <rpc/rpc.h>
+#include <rpc/nettype.h>
+#include <rpc/rpcsec_gss.h>
+
+#include <rpc/rpc_com.h>
+
+extern u_long sb_max_adj; /* not defined in socketvar.h */
+
+#if __FreeBSD_version < 700000
+#define strrchr rindex
+#endif
+
+/* Provide an entry point hook for the rpcsec_gss module. */
+struct rpc_gss_entries rpc_gss_entries;
+
+struct handle {
+ NCONF_HANDLE *nhandle;
+ int nflag; /* Whether NETPATH or NETCONFIG */
+ int nettype;
+};
+
+static const struct _rpcnettype {
+ const char *name;
+ const int type;
+} _rpctypelist[] = {
+ { "netpath", _RPC_NETPATH },
+ { "visible", _RPC_VISIBLE },
+ { "circuit_v", _RPC_CIRCUIT_V },
+ { "datagram_v", _RPC_DATAGRAM_V },
+ { "circuit_n", _RPC_CIRCUIT_N },
+ { "datagram_n", _RPC_DATAGRAM_N },
+ { "tcp", _RPC_TCP },
+ { "udp", _RPC_UDP },
+ { 0, _RPC_NONE }
+};
+
+struct netid_af {
+ const char *netid;
+ int af;
+ int protocol;
+};
+
+static const struct netid_af na_cvt[] = {
+ { "udp", AF_INET, IPPROTO_UDP },
+ { "tcp", AF_INET, IPPROTO_TCP },
+#ifdef INET6
+ { "udp6", AF_INET6, IPPROTO_UDP },
+ { "tcp6", AF_INET6, IPPROTO_TCP },
+#endif
+ { "local", AF_LOCAL, 0 }
+};
+
+struct rpc_createerr rpc_createerr;
+
+/*
+ * Find the appropriate buffer size
+ */
+u_int
+/*ARGSUSED*/
+__rpc_get_t_size(int af, int proto, int size)
+{
+ int defsize;
+
+ switch (proto) {
+ case IPPROTO_TCP:
+ defsize = 64 * 1024; /* XXX */
+ break;
+ case IPPROTO_UDP:
+ defsize = UDPMSGSIZE;
+ break;
+ default:
+ defsize = RPC_MAXDATASIZE;
+ break;
+ }
+ if (size == 0)
+ return defsize;
+
+ /* Check whether the value is within the upper max limit */
+ return (size > sb_max_adj ? (u_int)sb_max_adj : (u_int)size);
+}
+
+/*
+ * Find the appropriate address buffer size
+ */
+u_int
+__rpc_get_a_size(af)
+ int af;
+{
+ switch (af) {
+ case AF_INET:
+ return sizeof (struct sockaddr_in);
+#ifdef INET6
+ case AF_INET6:
+ return sizeof (struct sockaddr_in6);
+#endif
+ case AF_LOCAL:
+ return sizeof (struct sockaddr_un);
+ default:
+ break;
+ }
+ return ((u_int)RPC_MAXADDRSIZE);
+}
+
+#if 0
+
+/*
+ * Used to ping the NULL procedure for clnt handle.
+ * Returns NULL if fails, else a non-NULL pointer.
+ */
+void *
+rpc_nullproc(clnt)
+ CLIENT *clnt;
+{
+ struct timeval TIMEOUT = {25, 0};
+
+ if (clnt_call(clnt, NULLPROC, (xdrproc_t) xdr_void, NULL,
+ (xdrproc_t) xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *) clnt);
+}
+
+#endif
+
+int
+__rpc_socket2sockinfo(struct socket *so, struct __rpc_sockinfo *sip)
+{
+ int type, proto;
+ struct sockaddr *sa;
+ sa_family_t family;
+ struct sockopt opt;
+ int error;
+
+ CURVNET_SET(so->so_vnet);
+ error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
+ CURVNET_RESTORE();
+ if (error)
+ return 0;
+
+ sip->si_alen = sa->sa_len;
+ family = sa->sa_family;
+ free(sa, M_SONAME);
+
+ opt.sopt_dir = SOPT_GET;
+ opt.sopt_level = SOL_SOCKET;
+ opt.sopt_name = SO_TYPE;
+ opt.sopt_val = &type;
+ opt.sopt_valsize = sizeof type;
+ opt.sopt_td = NULL;
+ error = sogetopt(so, &opt);
+ if (error)
+ return 0;
+
+ /* XXX */
+ if (family != AF_LOCAL) {
+ if (type == SOCK_STREAM)
+ proto = IPPROTO_TCP;
+ else if (type == SOCK_DGRAM)
+ proto = IPPROTO_UDP;
+ else
+ return 0;
+ } else
+ proto = 0;
+
+ sip->si_af = family;
+ sip->si_proto = proto;
+ sip->si_socktype = type;
+
+ return 1;
+}
+
+/*
+ * Linear search, but the number of entries is small.
+ */
+int
+__rpc_nconf2sockinfo(const struct netconfig *nconf, struct __rpc_sockinfo *sip)
+{
+ int i;
+
+ for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++)
+ if (strcmp(na_cvt[i].netid, nconf->nc_netid) == 0 || (
+ strcmp(nconf->nc_netid, "unix") == 0 &&
+ strcmp(na_cvt[i].netid, "local") == 0)) {
+ sip->si_af = na_cvt[i].af;
+ sip->si_proto = na_cvt[i].protocol;
+ sip->si_socktype =
+ __rpc_seman2socktype((int)nconf->nc_semantics);
+ if (sip->si_socktype == -1)
+ return 0;
+ sip->si_alen = __rpc_get_a_size(sip->si_af);
+ return 1;
+ }
+
+ return 0;
+}
+
+struct socket *
+__rpc_nconf2socket(const struct netconfig *nconf)
+{
+ struct __rpc_sockinfo si;
+ struct socket *so;
+ int error;
+
+ if (!__rpc_nconf2sockinfo(nconf, &si))
+ return 0;
+
+ so = NULL;
+ error = socreate(si.si_af, &so, si.si_socktype, si.si_proto,
+ curthread->td_ucred, curthread);
+
+ if (error)
+ return NULL;
+ else
+ return so;
+}
+
+char *
+taddr2uaddr(const struct netconfig *nconf, const struct netbuf *nbuf)
+{
+ struct __rpc_sockinfo si;
+
+ if (!__rpc_nconf2sockinfo(nconf, &si))
+ return NULL;
+ return __rpc_taddr2uaddr_af(si.si_af, nbuf);
+}
+
+struct netbuf *
+uaddr2taddr(const struct netconfig *nconf, const char *uaddr)
+{
+ struct __rpc_sockinfo si;
+
+ if (!__rpc_nconf2sockinfo(nconf, &si))
+ return NULL;
+ return __rpc_uaddr2taddr_af(si.si_af, uaddr);
+}
+
+char *
+__rpc_taddr2uaddr_af(int af, const struct netbuf *nbuf)
+{
+ char *ret;
+ struct sbuf sb;
+ struct sockaddr_in *sin;
+ struct sockaddr_un *sun;
+ char namebuf[INET_ADDRSTRLEN];
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+ char namebuf6[INET6_ADDRSTRLEN];
+#endif
+ u_int16_t port;
+
+ sbuf_new(&sb, NULL, 0, SBUF_AUTOEXTEND);
+
+ switch (af) {
+ case AF_INET:
+ if (nbuf->len < sizeof(*sin))
+ return NULL;
+ sin = nbuf->buf;
+ if (inet_ntop(af, &sin->sin_addr, namebuf, sizeof namebuf)
+ == NULL)
+ return NULL;
+ port = ntohs(sin->sin_port);
+ if (sbuf_printf(&sb, "%s.%u.%u", namebuf,
+ ((uint32_t)port) >> 8,
+ port & 0xff) < 0)
+ return NULL;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ if (nbuf->len < sizeof(*sin6))
+ return NULL;
+ sin6 = nbuf->buf;
+ if (inet_ntop(af, &sin6->sin6_addr, namebuf6, sizeof namebuf6)
+ == NULL)
+ return NULL;
+ port = ntohs(sin6->sin6_port);
+ if (sbuf_printf(&sb, "%s.%u.%u", namebuf6,
+ ((uint32_t)port) >> 8,
+ port & 0xff) < 0)
+ return NULL;
+ break;
+#endif
+ case AF_LOCAL:
+ sun = nbuf->buf;
+ if (sbuf_printf(&sb, "%.*s", (int)(sun->sun_len -
+ offsetof(struct sockaddr_un, sun_path)),
+ sun->sun_path) < 0)
+ return (NULL);
+ break;
+ default:
+ return NULL;
+ }
+
+ sbuf_finish(&sb);
+ ret = strdup(sbuf_data(&sb), M_RPC);
+ sbuf_delete(&sb);
+
+ return ret;
+}
+
+struct netbuf *
+__rpc_uaddr2taddr_af(int af, const char *uaddr)
+{
+ struct netbuf *ret = NULL;
+ char *addrstr, *p;
+ unsigned port, portlo, porthi;
+ struct sockaddr_in *sin;
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
+ struct sockaddr_un *sun;
+
+ port = 0;
+ sin = NULL;
+
+ if (uaddr == NULL)
+ return NULL;
+
+ addrstr = strdup(uaddr, M_RPC);
+ if (addrstr == NULL)
+ return NULL;
+
+ /*
+ * AF_LOCAL addresses are expected to be absolute
+ * pathnames, anything else will be AF_INET or AF_INET6.
+ */
+ if (*addrstr != '/') {
+ p = strrchr(addrstr, '.');
+ if (p == NULL)
+ goto out;
+ portlo = (unsigned)strtol(p + 1, NULL, 10);
+ *p = '\0';
+
+ p = strrchr(addrstr, '.');
+ if (p == NULL)
+ goto out;
+ porthi = (unsigned)strtol(p + 1, NULL, 10);
+ *p = '\0';
+ port = (porthi << 8) | portlo;
+ }
+
+ ret = (struct netbuf *)malloc(sizeof *ret, M_RPC, M_WAITOK);
+
+ switch (af) {
+ case AF_INET:
+ sin = (struct sockaddr_in *)malloc(sizeof *sin, M_RPC,
+ M_WAITOK);
+ memset(sin, 0, sizeof *sin);
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(port);
+ if (inet_pton(AF_INET, addrstr, &sin->sin_addr) <= 0) {
+ free(sin, M_RPC);
+ free(ret, M_RPC);
+ ret = NULL;
+ goto out;
+ }
+ sin->sin_len = ret->maxlen = ret->len = sizeof *sin;
+ ret->buf = sin;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)malloc(sizeof *sin6, M_RPC,
+ M_WAITOK);
+ memset(sin6, 0, sizeof *sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(port);
+ if (inet_pton(AF_INET6, addrstr, &sin6->sin6_addr) <= 0) {
+ free(sin6, M_RPC);
+ free(ret, M_RPC);
+ ret = NULL;
+ goto out;
+ }
+ sin6->sin6_len = ret->maxlen = ret->len = sizeof *sin6;
+ ret->buf = sin6;
+ break;
+#endif
+ case AF_LOCAL:
+ sun = (struct sockaddr_un *)malloc(sizeof *sun, M_RPC,
+ M_WAITOK);
+ memset(sun, 0, sizeof *sun);
+ sun->sun_family = AF_LOCAL;
+ strncpy(sun->sun_path, addrstr, sizeof(sun->sun_path) - 1);
+ ret->len = ret->maxlen = sun->sun_len = SUN_LEN(sun);
+ ret->buf = sun;
+ break;
+ default:
+ break;
+ }
+out:
+ free(addrstr, M_RPC);
+ return ret;
+}
+
+int
+__rpc_seman2socktype(int semantics)
+{
+ switch (semantics) {
+ case NC_TPI_CLTS:
+ return SOCK_DGRAM;
+ case NC_TPI_COTS_ORD:
+ return SOCK_STREAM;
+ case NC_TPI_RAW:
+ return SOCK_RAW;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+int
+__rpc_socktype2seman(int socktype)
+{
+ switch (socktype) {
+ case SOCK_DGRAM:
+ return NC_TPI_CLTS;
+ case SOCK_STREAM:
+ return NC_TPI_COTS_ORD;
+ case SOCK_RAW:
+ return NC_TPI_RAW;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+/*
+ * Returns the type of the network as defined in <rpc/nettype.h>
+ * If nettype is NULL, it defaults to NETPATH.
+ */
+static int
+getnettype(const char *nettype)
+{
+ int i;
+
+ if ((nettype == NULL) || (nettype[0] == 0)) {
+ return (_RPC_NETPATH); /* Default */
+ }
+
+#if 0
+ nettype = strlocase(nettype);
+#endif
+ for (i = 0; _rpctypelist[i].name; i++)
+ if (strcasecmp(nettype, _rpctypelist[i].name) == 0) {
+ return (_rpctypelist[i].type);
+ }
+ return (_rpctypelist[i].type);
+}
+
+/*
+ * For the given nettype (tcp or udp only), return the first structure found.
+ * This should be freed by calling freenetconfigent()
+ */
+struct netconfig *
+__rpc_getconfip(const char *nettype)
+{
+ char *netid;
+ static char *netid_tcp = (char *) NULL;
+ static char *netid_udp = (char *) NULL;
+ struct netconfig *dummy;
+
+ if (!netid_udp && !netid_tcp) {
+ struct netconfig *nconf;
+ void *confighandle;
+
+ if (!(confighandle = setnetconfig())) {
+ log(LOG_ERR, "rpc: failed to open " NETCONFIG);
+ return (NULL);
+ }
+ while ((nconf = getnetconfig(confighandle)) != NULL) {
+ if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
+ if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
+ netid_tcp = strdup(nconf->nc_netid,
+ M_RPC);
+ } else
+ if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
+ netid_udp = strdup(nconf->nc_netid,
+ M_RPC);
+ }
+ }
+ }
+ endnetconfig(confighandle);
+ }
+ if (strcmp(nettype, "udp") == 0)
+ netid = netid_udp;
+ else if (strcmp(nettype, "tcp") == 0)
+ netid = netid_tcp;
+ else {
+ return (NULL);
+ }
+ if ((netid == NULL) || (netid[0] == 0)) {
+ return (NULL);
+ }
+ dummy = getnetconfigent(netid);
+ return (dummy);
+}
+
+/*
+ * Returns the type of the nettype, which should then be used with
+ * __rpc_getconf().
+ *
+ * For simplicity in the kernel, we don't support the NETPATH
+ * environment variable. We behave as userland would then NETPATH is
+ * unset, i.e. iterate over all visible entries in netconfig.
+ */
+void *
+__rpc_setconf(nettype)
+ const char *nettype;
+{
+ struct handle *handle;
+
+ handle = (struct handle *) malloc(sizeof (struct handle),
+ M_RPC, M_WAITOK);
+ switch (handle->nettype = getnettype(nettype)) {
+ case _RPC_NETPATH:
+ case _RPC_CIRCUIT_N:
+ case _RPC_DATAGRAM_N:
+ if (!(handle->nhandle = setnetconfig()))
+ goto failed;
+ handle->nflag = TRUE;
+ break;
+ case _RPC_VISIBLE:
+ case _RPC_CIRCUIT_V:
+ case _RPC_DATAGRAM_V:
+ case _RPC_TCP:
+ case _RPC_UDP:
+ if (!(handle->nhandle = setnetconfig())) {
+ log(LOG_ERR, "rpc: failed to open " NETCONFIG);
+ goto failed;
+ }
+ handle->nflag = FALSE;
+ break;
+ default:
+ goto failed;
+ }
+
+ return (handle);
+
+failed:
+ free(handle, M_RPC);
+ return (NULL);
+}
+
+/*
+ * Returns the next netconfig struct for the given "net" type.
+ * __rpc_setconf() should have been called previously.
+ */
+struct netconfig *
+__rpc_getconf(void *vhandle)
+{
+ struct handle *handle;
+ struct netconfig *nconf;
+
+ handle = (struct handle *)vhandle;
+ if (handle == NULL) {
+ return (NULL);
+ }
+ for (;;) {
+ if (handle->nflag) {
+ nconf = getnetconfig(handle->nhandle);
+ if (nconf && !(nconf->nc_flag & NC_VISIBLE))
+ continue;
+ } else {
+ nconf = getnetconfig(handle->nhandle);
+ }
+ if (nconf == NULL)
+ break;
+ if ((nconf->nc_semantics != NC_TPI_CLTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS_ORD))
+ continue;
+ switch (handle->nettype) {
+ case _RPC_VISIBLE:
+ if (!(nconf->nc_flag & NC_VISIBLE))
+ continue;
+ /* FALLTHROUGH */
+ case _RPC_NETPATH: /* Be happy */
+ break;
+ case _RPC_CIRCUIT_V:
+ if (!(nconf->nc_flag & NC_VISIBLE))
+ continue;
+ /* FALLTHROUGH */
+ case _RPC_CIRCUIT_N:
+ if ((nconf->nc_semantics != NC_TPI_COTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS_ORD))
+ continue;
+ break;
+ case _RPC_DATAGRAM_V:
+ if (!(nconf->nc_flag & NC_VISIBLE))
+ continue;
+ /* FALLTHROUGH */
+ case _RPC_DATAGRAM_N:
+ if (nconf->nc_semantics != NC_TPI_CLTS)
+ continue;
+ break;
+ case _RPC_TCP:
+ if (((nconf->nc_semantics != NC_TPI_COTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS_ORD)) ||
+ (strcmp(nconf->nc_protofmly, NC_INET)
+#ifdef INET6
+ && strcmp(nconf->nc_protofmly, NC_INET6))
+#else
+ )
+#endif
+ ||
+ strcmp(nconf->nc_proto, NC_TCP))
+ continue;
+ break;
+ case _RPC_UDP:
+ if ((nconf->nc_semantics != NC_TPI_CLTS) ||
+ (strcmp(nconf->nc_protofmly, NC_INET)
+#ifdef INET6
+ && strcmp(nconf->nc_protofmly, NC_INET6))
+#else
+ )
+#endif
+ ||
+ strcmp(nconf->nc_proto, NC_UDP))
+ continue;
+ break;
+ }
+ break;
+ }
+ return (nconf);
+}
+
+void
+__rpc_endconf(vhandle)
+ void * vhandle;
+{
+ struct handle *handle;
+
+ handle = (struct handle *) vhandle;
+ if (handle == NULL) {
+ return;
+ }
+ endnetconfig(handle->nhandle);
+ free(handle, M_RPC);
+}
+
+int
+__rpc_sockisbound(struct socket *so)
+{
+ struct sockaddr *sa;
+ int error, bound;
+
+ CURVNET_SET(so->so_vnet);
+ error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
+ CURVNET_RESTORE();
+ if (error)
+ return (0);
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ bound = (((struct sockaddr_in *) sa)->sin_port != 0);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ bound = (((struct sockaddr_in6 *) sa)->sin6_port != 0);
+ break;
+#endif
+ case AF_LOCAL:
+ /* XXX check this */
+ bound = (((struct sockaddr_un *) sa)->sun_path[0] != '\0');
+ break;
+ default:
+ bound = FALSE;
+ break;
+ }
+
+ free(sa, M_SONAME);
+
+ return bound;
+}
+
+/*
+ * Implement XDR-style API for RPC call.
+ */
+enum clnt_stat
+clnt_call_private(
+ CLIENT *cl, /* client handle */
+ struct rpc_callextra *ext, /* call metadata */
+ rpcproc_t proc, /* procedure number */
+ xdrproc_t xargs, /* xdr routine for args */
+ void *argsp, /* pointer to args */
+ xdrproc_t xresults, /* xdr routine for results */
+ void *resultsp, /* pointer to results */
+ struct timeval utimeout) /* seconds to wait before giving up */
+{
+ XDR xdrs;
+ struct mbuf *mreq;
+ struct mbuf *mrep;
+ enum clnt_stat stat;
+
+ mreq = m_getcl(M_WAITOK, MT_DATA, 0);
+
+ xdrmbuf_create(&xdrs, mreq, XDR_ENCODE);
+ if (!xargs(&xdrs, argsp)) {
+ m_freem(mreq);
+ return (RPC_CANTENCODEARGS);
+ }
+ XDR_DESTROY(&xdrs);
+
+ stat = CLNT_CALL_MBUF(cl, ext, proc, mreq, &mrep, utimeout);
+ m_freem(mreq);
+
+ if (stat == RPC_SUCCESS) {
+ xdrmbuf_create(&xdrs, mrep, XDR_DECODE);
+ if (!xresults(&xdrs, resultsp)) {
+ XDR_DESTROY(&xdrs);
+ return (RPC_CANTDECODERES);
+ }
+ XDR_DESTROY(&xdrs);
+ }
+
+ return (stat);
+}
+
+/*
+ * Bind a socket to a privileged IP port
+ */
+int
+bindresvport(struct socket *so, struct sockaddr *sa)
+{
+ int old, error, af;
+ bool_t freesa = FALSE;
+ struct sockaddr_in *sin;
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
+ struct sockopt opt;
+ int proto, portrange, portlow;
+ u_int16_t *portp;
+ socklen_t salen;
+
+ if (sa == NULL) {
+ CURVNET_SET(so->so_vnet);
+ error = so->so_proto->pr_usrreqs->pru_sockaddr(so, &sa);
+ CURVNET_RESTORE();
+ if (error)
+ return (error);
+ freesa = TRUE;
+ af = sa->sa_family;
+ salen = sa->sa_len;
+ memset(sa, 0, sa->sa_len);
+ } else {
+ af = sa->sa_family;
+ salen = sa->sa_len;
+ }
+
+ switch (af) {
+ case AF_INET:
+ proto = IPPROTO_IP;
+ portrange = IP_PORTRANGE;
+ portlow = IP_PORTRANGE_LOW;
+ sin = (struct sockaddr_in *)sa;
+ portp = &sin->sin_port;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ proto = IPPROTO_IPV6;
+ portrange = IPV6_PORTRANGE;
+ portlow = IPV6_PORTRANGE_LOW;
+ sin6 = (struct sockaddr_in6 *)sa;
+ portp = &sin6->sin6_port;
+ break;
+#endif
+ default:
+ return (EPFNOSUPPORT);
+ }
+
+ sa->sa_family = af;
+ sa->sa_len = salen;
+
+ if (*portp == 0) {
+ bzero(&opt, sizeof(opt));
+ opt.sopt_dir = SOPT_GET;
+ opt.sopt_level = proto;
+ opt.sopt_name = portrange;
+ opt.sopt_val = &old;
+ opt.sopt_valsize = sizeof(old);
+ error = sogetopt(so, &opt);
+ if (error) {
+ goto out;
+ }
+
+ opt.sopt_dir = SOPT_SET;
+ opt.sopt_val = &portlow;
+ error = sosetopt(so, &opt);
+ if (error)
+ goto out;
+ }
+
+ error = sobind(so, sa, curthread);
+
+ if (*portp == 0) {
+ if (error) {
+ opt.sopt_dir = SOPT_SET;
+ opt.sopt_val = &old;
+ sosetopt(so, &opt);
+ }
+ }
+out:
+ if (freesa)
+ free(sa, M_SONAME);
+
+ return (error);
+}
+
+/*
+ * Kernel module glue
+ */
+static int
+krpc_modevent(module_t mod, int type, void *data)
+{
+
+ return (0);
+}
+static moduledata_t krpc_mod = {
+ "krpc",
+ krpc_modevent,
+ NULL,
+};
+DECLARE_MODULE(krpc, krpc_mod, SI_SUB_VFS, SI_ORDER_ANY);
+
+/* So that loader and kldload(2) can find us, wherever we are.. */
+MODULE_VERSION(krpc, 1);