898 lines
19 KiB
C
898 lines
19 KiB
C
|
/* $NetBSD: rpc_generic.c,v 1.24 2010/12/08 02:06:38 joerg Exp $ */
|
||
|
|
||
|
/*
|
||
|
* Sun RPC is a product of Sun Microsystems, Inc. and is provided for
|
||
|
* unrestricted use provided that this legend is included on all tape
|
||
|
* media and as a part of the software program in whole or part. Users
|
||
|
* may copy or modify Sun RPC without charge, but are not authorized
|
||
|
* to license or distribute it to anyone else except as part of a product or
|
||
|
* program developed by the user.
|
||
|
*
|
||
|
* SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
|
||
|
* WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
|
||
|
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
|
||
|
*
|
||
|
* Sun RPC is provided with no support and without any obligation on the
|
||
|
* part of Sun Microsystems, Inc. to assist in its use, correction,
|
||
|
* modification or enhancement.
|
||
|
*
|
||
|
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
|
||
|
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
|
||
|
* OR ANY PART THEREOF.
|
||
|
*
|
||
|
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
|
||
|
* or profits or other special, indirect and consequential damages, even if
|
||
|
* Sun has been advised of the possibility of such damages.
|
||
|
*
|
||
|
* Sun Microsystems, Inc.
|
||
|
* 2550 Garcia Avenue
|
||
|
* Mountain View, California 94043
|
||
|
*/
|
||
|
/*
|
||
|
* 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.24 2010/12/08 02:06:38 joerg 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 <malloc.h>
|
||
|
#include <string.h>
|
||
|
#include <syslog.h>
|
||
|
#include <rpc/nettype.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 __P((char *));
|
||
|
#endif
|
||
|
static int getnettype __P((const char *));
|
||
|
|
||
|
/*
|
||
|
* Cache the result of getrlimit(), so we don't have to do an
|
||
|
* expensive call every time.
|
||
|
*/
|
||
|
int
|
||
|
__rpc_dtbsize()
|
||
|
{
|
||
|
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(af, proto, size)
|
||
|
int af, 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(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
|
||
|
static char *
|
||
|
strlocase(p)
|
||
|
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(nettype)
|
||
|
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(nettype)
|
||
|
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(nettype)
|
||
|
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(vhandle)
|
||
|
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(vhandle)
|
||
|
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(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);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Try all possible transports until
|
||
|
* one succeeds in finding the netconf for the given fd.
|
||
|
*/
|
||
|
struct netconfig *
|
||
|
__rpcgettp(fd)
|
||
|
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:
|
||
|
sinp = nbuf->buf;
|
||
|
if (inet_ntop(af, &sinp->sin_addr, namebuf, 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:
|
||
|
sin6 = nbuf->buf;
|
||
|
if (inet_ntop(af, &sin6->sin6_addr, namebuf6, 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;
|
||
|
struct sockaddr_in *sinp;
|
||
|
#ifdef INET6
|
||
|
struct sockaddr_in6 *sin6;
|
||
|
#endif
|
||
|
struct sockaddr_un *sun;
|
||
|
|
||
|
_DIAGASSERT(uaddr != 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);
|
||
|
ret->len = ret->maxlen = sun->sun_len = SUN_LEN(sun);
|
||
|
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, sizeof(one));
|
||
|
}
|