991 lines
21 KiB
C
991 lines
21 KiB
C
/* $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/rpcsec_tls.h>
|
|
|
|
#include <rpc/rpc_com.h>
|
|
#include <rpc/krpc.h>
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
#include <vm/vm_param.h>
|
|
|
|
extern u_long sb_max_adj; /* not defined in socketvar.h */
|
|
|
|
/* 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(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_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
|
|
uint16_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(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(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_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;
|
|
uint16_t *portp;
|
|
socklen_t salen;
|
|
|
|
if (sa == NULL) {
|
|
CURVNET_SET(so->so_vnet);
|
|
error = so->so_proto->pr_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);
|
|
}
|
|
|
|
/*
|
|
* Make sure an mbuf list is made up entirely of ext_pgs mbufs.
|
|
* This is needed for sosend() when KERN_TLS is being used.
|
|
* (There might also be a performance improvement for certain
|
|
* network interfaces that handle ext_pgs mbufs efficiently.)
|
|
* It expects at least one non-ext_pgs mbuf followed by zero
|
|
* or more ext_pgs mbufs. It does not handle the case where
|
|
* non-ext_pgs mbuf(s) follow ext_pgs ones.
|
|
* It also performs sanity checks on the resultant list.
|
|
* The "mp" argument list is consumed.
|
|
* The "maxextsiz" argument is the upper bound on the data
|
|
* size for each mbuf (usually 16K for KERN_TLS).
|
|
*/
|
|
struct mbuf *
|
|
_rpc_copym_into_ext_pgs(struct mbuf *mp, int maxextsiz)
|
|
{
|
|
struct mbuf *m, *m2, *m3, *mhead;
|
|
int tlen;
|
|
|
|
KASSERT((mp->m_flags & (M_EXT | M_EXTPG)) !=
|
|
(M_EXT | M_EXTPG), ("_rpc_copym_into_ext_pgs:"
|
|
" first mbuf is an ext_pgs"));
|
|
/*
|
|
* Find the last non-ext_pgs mbuf and the total
|
|
* length of the non-ext_pgs mbuf(s).
|
|
* The first mbuf must always be a non-ext_pgs
|
|
* mbuf.
|
|
*/
|
|
tlen = mp->m_len;
|
|
m2 = mp;
|
|
for (m = mp->m_next; m != NULL; m = m->m_next) {
|
|
if ((m->m_flags & M_EXTPG) != 0)
|
|
break;
|
|
tlen += m->m_len;
|
|
m2 = m;
|
|
}
|
|
|
|
/*
|
|
* Copy the non-ext_pgs mbuf(s) into an ext_pgs
|
|
* mbuf list.
|
|
*/
|
|
m2->m_next = NULL;
|
|
mhead = mb_mapped_to_unmapped(mp, tlen, maxextsiz,
|
|
M_WAITOK, &m2);
|
|
|
|
/*
|
|
* Link the ext_pgs list onto the newly copied
|
|
* list and free up the non-ext_pgs mbuf(s).
|
|
*/
|
|
m2->m_next = m;
|
|
m_freem(mp);
|
|
|
|
/*
|
|
* Sanity check the resultant mbuf list. Check for and
|
|
* remove any 0 length mbufs in the list, since the
|
|
* KERN_TLS code does not expect any 0 length mbuf(s)
|
|
* in the list.
|
|
*/
|
|
m3 = NULL;
|
|
m2 = mhead;
|
|
tlen = 0;
|
|
while (m2 != NULL) {
|
|
KASSERT(m2->m_len >= 0, ("_rpc_copym_into_ext_pgs:"
|
|
" negative m_len"));
|
|
KASSERT((m2->m_flags & (M_EXT | M_EXTPG)) ==
|
|
(M_EXT | M_EXTPG), ("_rpc_copym_into_ext_pgs:"
|
|
" non-nomap mbuf in list"));
|
|
if (m2->m_len == 0) {
|
|
if (m3 != NULL)
|
|
m3->m_next = m2->m_next;
|
|
else
|
|
m = m2->m_next;
|
|
m2->m_next = NULL;
|
|
m_free(m2);
|
|
if (m3 != NULL)
|
|
m2 = m3->m_next;
|
|
else
|
|
m2 = m;
|
|
} else {
|
|
MBUF_EXT_PGS_ASSERT_SANITY(m2);
|
|
m3 = m2;
|
|
tlen += m2->m_len;
|
|
m2 = m2->m_next;
|
|
}
|
|
}
|
|
return (mhead);
|
|
}
|
|
|
|
/*
|
|
* Kernel module glue
|
|
*/
|
|
static int
|
|
krpc_modevent(module_t mod, int type, void *data)
|
|
{
|
|
int error = 0;
|
|
|
|
switch (type) {
|
|
case MOD_LOAD:
|
|
error = rpctls_init();
|
|
break;
|
|
case MOD_UNLOAD:
|
|
/*
|
|
* Cannot be unloaded, since the rpctlssd or rpctlscd daemons
|
|
* might be performing a rpctls syscall.
|
|
*/
|
|
/* FALLTHROUGH */
|
|
default:
|
|
error = EOPNOTSUPP;
|
|
}
|
|
return (error);
|
|
}
|
|
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);
|
|
MODULE_DEPEND(krpc, xdr, 1, 1, 1);
|