905 lines
19 KiB
C
905 lines
19 KiB
C
/* $NetBSD: rpc_generic.c,v 1.30 2017/05/03 21:39:27 christos Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2010, Oracle America, Inc.
|
|
*
|
|
* 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 the "Oracle America, 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" */
|
|
|
|
/*
|
|
* rpc_generic.c, Miscl routines for RPC.
|
|
*
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#if defined(LIBC_SCCS) && !defined(lint)
|
|
__RCSID("$NetBSD: rpc_generic.c,v 1.30 2017/05/03 21:39:27 christos Exp $");
|
|
#endif
|
|
|
|
#include "namespace.h"
|
|
#include "reentrant.h"
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/resource.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <arpa/inet.h>
|
|
#include <rpc/rpc.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <netdb.h>
|
|
#include <netconfig.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <rpc/nettype.h>
|
|
|
|
#include "svc_fdset.h"
|
|
#include "rpc_internal.h"
|
|
|
|
#ifdef __weak_alias
|
|
__weak_alias(taddr2uaddr,_taddr2uaddr)
|
|
__weak_alias(uaddr2taddr,_uaddr2taddr)
|
|
#endif
|
|
|
|
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 }
|
|
};
|
|
|
|
#if 0
|
|
static char *strlocase(char *);
|
|
#endif
|
|
static int getnettype(const char *);
|
|
|
|
/*
|
|
* Cache the result of getrlimit(), so we don't have to do an
|
|
* expensive call every time.
|
|
*/
|
|
int
|
|
__rpc_dtbsize(void)
|
|
{
|
|
static int tbsize;
|
|
struct rlimit rl;
|
|
|
|
if (tbsize) {
|
|
return (tbsize);
|
|
}
|
|
if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
|
|
return (tbsize = (int)rl.rlim_max);
|
|
}
|
|
/*
|
|
* Something wrong. I'll try to save face by returning a
|
|
* pessimistic number.
|
|
*/
|
|
return (32);
|
|
}
|
|
|
|
|
|
/*
|
|
* Find the appropriate buffer size
|
|
*/
|
|
u_int
|
|
/*ARGSUSED*/
|
|
__rpc_get_t_size(
|
|
int af,
|
|
int proto,
|
|
int size) /* Size requested */
|
|
{
|
|
int maxsize, defsize;
|
|
|
|
maxsize = 256 * 1024; /* XXX */
|
|
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 > maxsize ? (u_int)maxsize : (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
|
|
static char *
|
|
strlocase(char *p)
|
|
{
|
|
char *t = p;
|
|
|
|
_DIAGASSERT(p != NULL);
|
|
|
|
for (; *p; p++)
|
|
if (isupper(*p))
|
|
*p = tolower(*p);
|
|
return (t);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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()
|
|
*/
|
|
|
|
#ifdef _REENTRANT
|
|
static thread_key_t tcp_key, udp_key;
|
|
static once_t __rpc_getconfigp_once = ONCE_INITIALIZER;
|
|
|
|
static void
|
|
__rpc_getconfigp_setup(void)
|
|
{
|
|
|
|
thr_keycreate(&tcp_key, free);
|
|
thr_keycreate(&udp_key, free);
|
|
}
|
|
#endif
|
|
|
|
struct netconfig *
|
|
__rpc_getconfip(const char *nettype)
|
|
{
|
|
char *netid;
|
|
char *netid_tcp = NULL;
|
|
char *netid_udp = NULL;
|
|
static char *netid_tcp_main;
|
|
static char *netid_udp_main;
|
|
struct netconfig *dummy;
|
|
#ifdef _REENTRANT
|
|
if (__isthreaded == 0) {
|
|
netid_udp = netid_udp_main;
|
|
netid_tcp = netid_tcp_main;
|
|
} else {
|
|
thr_once(&__rpc_getconfigp_once, __rpc_getconfigp_setup);
|
|
netid_tcp = thr_getspecific(tcp_key);
|
|
netid_udp = thr_getspecific(udp_key);
|
|
}
|
|
#else
|
|
netid_udp = netid_udp_main;
|
|
netid_tcp = netid_tcp_main;
|
|
#endif
|
|
|
|
_DIAGASSERT(nettype != NULL);
|
|
|
|
if (!netid_udp && !netid_tcp) {
|
|
struct netconfig *nconf;
|
|
void *confighandle;
|
|
|
|
if (!(confighandle = setnetconfig())) {
|
|
syslog (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);
|
|
if (netid_tcp == NULL)
|
|
return NULL;
|
|
#ifdef _REENTRANT
|
|
if (__isthreaded == 0)
|
|
netid_tcp_main = netid_tcp;
|
|
else
|
|
thr_setspecific(tcp_key,
|
|
(void *) netid_tcp);
|
|
#else
|
|
netid_tcp_main = netid_tcp;
|
|
#endif
|
|
} else
|
|
if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
|
|
netid_udp = strdup(nconf->nc_netid);
|
|
if (netid_udp == NULL)
|
|
return NULL;
|
|
#ifdef _REENTRANT
|
|
if (__isthreaded == 0)
|
|
netid_udp_main = netid_udp;
|
|
else
|
|
thr_setspecific(udp_key,
|
|
(void *) netid_udp);
|
|
#else
|
|
netid_udp_main = netid_udp;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
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().
|
|
*/
|
|
void *
|
|
__rpc_setconf(const char *nettype)
|
|
{
|
|
struct handle *handle;
|
|
|
|
/* nettype may be NULL; getnettype() supports that */
|
|
|
|
handle = malloc(sizeof(*handle));
|
|
if (handle == NULL) {
|
|
return (NULL);
|
|
}
|
|
switch (handle->nettype = getnettype(nettype)) {
|
|
case _RPC_NETPATH:
|
|
case _RPC_CIRCUIT_N:
|
|
case _RPC_DATAGRAM_N:
|
|
if (!(handle->nhandle = setnetpath())) {
|
|
free(handle);
|
|
return (NULL);
|
|
}
|
|
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())) {
|
|
syslog (LOG_ERR, "rpc: failed to open " NETCONFIG);
|
|
free(handle);
|
|
return (NULL);
|
|
}
|
|
handle->nflag = FALSE;
|
|
break;
|
|
default:
|
|
free(handle);
|
|
return (NULL);
|
|
}
|
|
|
|
return (handle);
|
|
}
|
|
|
|
/*
|
|
* 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 = getnetpath(handle->nhandle);
|
|
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;
|
|
}
|
|
if (handle->nflag) {
|
|
endnetpath(handle->nhandle);
|
|
} else {
|
|
endnetconfig(handle->nhandle);
|
|
}
|
|
free(handle);
|
|
}
|
|
|
|
/*
|
|
* Used to ping the NULL procedure for clnt handle.
|
|
* Returns NULL if fails, else a non-NULL pointer.
|
|
*/
|
|
void *
|
|
rpc_nullproc(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);
|
|
}
|
|
|
|
/*
|
|
* Try all possible transports until
|
|
* one succeeds in finding the netconf for the given fd.
|
|
*/
|
|
struct netconfig *
|
|
__rpcgettp(int fd)
|
|
{
|
|
const char *netid;
|
|
struct __rpc_sockinfo si;
|
|
|
|
if (!__rpc_fd2sockinfo(fd, &si))
|
|
return NULL;
|
|
|
|
if (!__rpc_sockinfo2netid(&si, &netid))
|
|
return NULL;
|
|
|
|
return getnetconfigent(__UNCONST(netid));
|
|
}
|
|
|
|
int
|
|
__rpc_fd2sockinfo(int fd, struct __rpc_sockinfo *sip)
|
|
{
|
|
socklen_t len;
|
|
int type, proto;
|
|
struct sockaddr_storage ss;
|
|
|
|
_DIAGASSERT(sip != NULL);
|
|
|
|
len = sizeof ss;
|
|
if (getsockname(fd, (struct sockaddr *)(void *)&ss, &len) < 0)
|
|
return 0;
|
|
sip->si_alen = len;
|
|
|
|
len = sizeof type;
|
|
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len) < 0)
|
|
return 0;
|
|
|
|
/* XXX */
|
|
if (ss.ss_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 = ss.ss_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)
|
|
{
|
|
size_t i;
|
|
|
|
_DIAGASSERT(nconf != NULL);
|
|
_DIAGASSERT(sip != NULL);
|
|
|
|
for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++)
|
|
if (!strcmp(na_cvt[i].netid, nconf->nc_netid)) {
|
|
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;
|
|
}
|
|
|
|
int
|
|
__rpc_nconf2fd(const struct netconfig *nconf)
|
|
{
|
|
struct __rpc_sockinfo si;
|
|
|
|
_DIAGASSERT(nconf != NULL);
|
|
|
|
if (!__rpc_nconf2sockinfo(nconf, &si))
|
|
return 0;
|
|
|
|
return socket(si.si_af, si.si_socktype, si.si_proto);
|
|
}
|
|
|
|
int
|
|
__rpc_sockinfo2netid(struct __rpc_sockinfo *sip, const char **netid)
|
|
{
|
|
size_t i;
|
|
|
|
_DIAGASSERT(sip != NULL);
|
|
/* netid may be NULL */
|
|
|
|
for (i = 0; i < (sizeof na_cvt) / (sizeof (struct netid_af)); i++)
|
|
if (na_cvt[i].af == sip->si_af &&
|
|
na_cvt[i].protocol == sip->si_proto) {
|
|
if (netid)
|
|
*netid = na_cvt[i].netid;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *
|
|
taddr2uaddr(const struct netconfig *nconf, const struct netbuf *nbuf)
|
|
{
|
|
struct __rpc_sockinfo si;
|
|
|
|
_DIAGASSERT(nconf != NULL);
|
|
_DIAGASSERT(nbuf != NULL);
|
|
|
|
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;
|
|
|
|
_DIAGASSERT(nconf != NULL);
|
|
_DIAGASSERT(uaddr != NULL);
|
|
|
|
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 sockaddr_in *sinp;
|
|
struct sockaddr_un *sun;
|
|
char namebuf[INET_ADDRSTRLEN];
|
|
#ifdef INET6
|
|
struct sockaddr_in6 *sin6;
|
|
char namebuf6[INET6_ADDRSTRLEN];
|
|
#endif
|
|
u_int16_t port;
|
|
|
|
_DIAGASSERT(nbuf != NULL);
|
|
|
|
switch (af) {
|
|
case AF_INET:
|
|
if (nbuf->len < sizeof(*sinp)) {
|
|
return NULL;
|
|
}
|
|
sinp = nbuf->buf;
|
|
if (inet_ntop(af, &sinp->sin_addr, namebuf,
|
|
(socklen_t)sizeof namebuf) == NULL)
|
|
return NULL;
|
|
port = ntohs(sinp->sin_port);
|
|
if (asprintf(&ret, "%s.%u.%u", namebuf, ((u_int32_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,
|
|
(socklen_t)sizeof namebuf6) == NULL)
|
|
return NULL;
|
|
port = ntohs(sin6->sin6_port);
|
|
if (asprintf(&ret, "%s.%u.%u", namebuf6, ((u_int32_t)port) >> 8,
|
|
port & 0xff) < 0)
|
|
return NULL;
|
|
break;
|
|
#endif
|
|
case AF_LOCAL:
|
|
sun = nbuf->buf;
|
|
sun->sun_path[sizeof(sun->sun_path) - 1] = '\0'; /* safety */
|
|
ret = strdup(sun->sun_path);
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct netbuf *
|
|
__rpc_uaddr2taddr_af(int af, const char *uaddr)
|
|
{
|
|
struct netbuf *ret = NULL;
|
|
char *addrstr, *p;
|
|
unsigned port, portlo, porthi;
|
|
size_t len;
|
|
struct sockaddr_in *sinp;
|
|
#ifdef INET6
|
|
struct sockaddr_in6 *sin6;
|
|
#endif
|
|
struct sockaddr_un *sun;
|
|
|
|
if (uaddr == NULL)
|
|
return NULL;
|
|
|
|
addrstr = strdup(uaddr);
|
|
if (addrstr == NULL)
|
|
return NULL;
|
|
|
|
/*
|
|
* AF_LOCAL addresses are expected to be absolute
|
|
* pathnames, anything else will be AF_INET or AF_INET6.
|
|
*/
|
|
port = 0;
|
|
if (*addrstr != '/') {
|
|
p = strrchr(addrstr, '.');
|
|
if (p == NULL)
|
|
goto out;
|
|
portlo = (unsigned)atoi(p + 1);
|
|
*p = '\0';
|
|
|
|
p = strrchr(addrstr, '.');
|
|
if (p == NULL)
|
|
goto out;
|
|
porthi = (unsigned)atoi(p + 1);
|
|
*p = '\0';
|
|
port = (porthi << 8) | portlo;
|
|
}
|
|
|
|
ret = malloc(sizeof(*ret));
|
|
if (ret == NULL)
|
|
goto out;
|
|
|
|
switch (af) {
|
|
case AF_INET:
|
|
sinp = malloc(sizeof(*sinp));
|
|
if (sinp == NULL)
|
|
goto out;
|
|
memset(sinp, 0, sizeof *sinp);
|
|
sinp->sin_family = AF_INET;
|
|
sinp->sin_port = htons(port);
|
|
if (inet_pton(AF_INET, addrstr, &sinp->sin_addr) <= 0) {
|
|
free(sinp);
|
|
free(ret);
|
|
ret = NULL;
|
|
goto out;
|
|
}
|
|
sinp->sin_len = ret->maxlen = ret->len = sizeof *sinp;
|
|
ret->buf = sinp;
|
|
break;
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
sin6 = malloc(sizeof(*sin6));
|
|
if (sin6 == NULL)
|
|
goto out;
|
|
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);
|
|
free(ret);
|
|
ret = NULL;
|
|
goto out;
|
|
}
|
|
sin6->sin6_len = ret->maxlen = ret->len = sizeof *sin6;
|
|
ret->buf = sin6;
|
|
break;
|
|
#endif
|
|
case AF_LOCAL:
|
|
sun = malloc(sizeof(*sun));
|
|
if (sun == NULL)
|
|
goto out;
|
|
memset(sun, 0, sizeof *sun);
|
|
sun->sun_family = AF_LOCAL;
|
|
strncpy(sun->sun_path, addrstr, sizeof(sun->sun_path) - 1);
|
|
len = SUN_LEN(sun);
|
|
_DIAGASSERT(__type_fit(uint8_t, len));
|
|
ret->len = ret->maxlen = sun->sun_len = (uint8_t)len;
|
|
ret->buf = sun;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
out:
|
|
free(addrstr);
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* XXXX - IPv6 scope IDs can't be handled in universal addresses.
|
|
* Here, we compare the original server address to that of the RPC
|
|
* service we just received back from a call to rpcbind on the remote
|
|
* machine. If they are both "link local" or "site local", copy
|
|
* the scope id of the server address over to the service address.
|
|
*/
|
|
/* ARGSUSED */
|
|
int
|
|
__rpc_fixup_addr(struct netbuf *new, const struct netbuf *svc)
|
|
{
|
|
#ifdef INET6
|
|
struct sockaddr *sa_new, *sa_svc;
|
|
struct sockaddr_in6 *sin6_new, *sin6_svc;
|
|
|
|
_DIAGASSERT(new != NULL);
|
|
_DIAGASSERT(svc != NULL);
|
|
|
|
sa_svc = (struct sockaddr *)svc->buf;
|
|
sa_new = (struct sockaddr *)new->buf;
|
|
|
|
if (sa_new->sa_family == sa_svc->sa_family &&
|
|
sa_new->sa_family == AF_INET6) {
|
|
sin6_new = (struct sockaddr_in6 *)new->buf;
|
|
sin6_svc = (struct sockaddr_in6 *)svc->buf;
|
|
|
|
if ((IN6_IS_ADDR_LINKLOCAL(&sin6_new->sin6_addr) &&
|
|
IN6_IS_ADDR_LINKLOCAL(&sin6_svc->sin6_addr)) ||
|
|
(IN6_IS_ADDR_SITELOCAL(&sin6_new->sin6_addr) &&
|
|
IN6_IS_ADDR_SITELOCAL(&sin6_svc->sin6_addr))) {
|
|
sin6_new->sin6_scope_id = sin6_svc->sin6_scope_id;
|
|
}
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
__rpc_sockisbound(int fd)
|
|
{
|
|
struct sockaddr_storage ss;
|
|
socklen_t slen;
|
|
|
|
slen = sizeof (struct sockaddr_storage);
|
|
if (getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0)
|
|
return 0;
|
|
|
|
switch (ss.ss_family) {
|
|
case AF_INET:
|
|
return (((struct sockaddr_in *)
|
|
(void *)&ss)->sin_port != 0);
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
return (((struct sockaddr_in6 *)
|
|
(void *)&ss)->sin6_port != 0);
|
|
#endif
|
|
case AF_LOCAL:
|
|
/* XXX check this */
|
|
return (((struct sockaddr_un *)
|
|
(void *)&ss)->sun_path[0] != '\0');
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* For TCP transport, Host Requirements RFCs mandate
|
|
* Nagle (RFC-896) processing. But for RPC, Nagle
|
|
* processing adds adds unwanted latency to the last,
|
|
* partial TCP segment of each RPC message. See:
|
|
* R. W. Scheifler and J. Gettys, The X Window System,
|
|
* ACM Transactions on Graphics 16:8 (Aug. 1983), pp. 57-69.
|
|
* So for TCP transport, disable Nagle via TCP_NODELAY.
|
|
* XXX: moral equivalent for non-TCP protocols?
|
|
*/
|
|
int
|
|
__rpc_setnodelay(int fd, const struct __rpc_sockinfo *si)
|
|
{
|
|
int one = 1;
|
|
if (si->si_proto != IPPROTO_TCP)
|
|
return 0;
|
|
return setsockopt(fd, si->si_proto, TCP_NODELAY, &one,
|
|
(socklen_t)sizeof(one));
|
|
}
|