rpc_generic.c revision e8031f0a8ed0e45c6d8847c5e09424e66fd34a4b
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Portions of this source code were derived from Berkeley
* 4.3 BSD under license from the Regents of the University of
* California.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Miscl routines for RPC.
*/
#include "mt.h"
#include "rpc_mt.h"
#include <stdio.h>
#include <sys/types.h>
#include <rpc/rpc.h>
#include <rpc/nettype.h>
#include <sys/param.h>
#include <sys/mkdev.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <sys/resource.h>
#include <netconfig.h>
#include <malloc.h>
#include <syslog.h>
#include <string.h>
#include <sys/systeminfo.h>
#include <netdir.h>
#include <netdb.h>
struct handle {
NCONF_HANDLE *nhandle;
int nflag; /* Whether NETPATH or NETCONFIG */
int nettype;
};
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,
"local", _RPC_LOCAL,
"door", _RPC_DOOR,
"door_local", _RPC_DOOR_LOCAL,
"door_netpath", _RPC_DOOR_NETPATH,
0, _RPC_NONE
};
/*
* Cache the result of getrlimit(), so we don't have to do an
* expensive call every time. Since many old programs assume
* it will not return more than 1024 and use svc_fdset, return
* maximum of FD_SETSIZE.
*/
int
__rpc_dtbsize(void)
{
static int tbsize;
struct rlimit rl;
if (tbsize)
return (tbsize);
if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
tbsize = rl.rlim_max;
/*
* backward compatibility; too many places
* this function is called assuming it returns
* maximum of 1024.
*/
if (tbsize > FD_SETSIZE)
tbsize = FD_SETSIZE;
return (tbsize);
}
/*
* Something wrong. I'll try to save face by returning a
* pessimistic number.
*/
return (32);
}
/*
* Find the appropriate buffer size
*/
uint_t
__rpc_get_t_size(
t_scalar_t size, /* Size requested */
t_scalar_t bufsize) /* Supported by the transport */
{
if (bufsize == -2) /* transfer of data unsupported */
return ((uint_t)0);
if (size == 0) {
if ((bufsize == -1) || (bufsize == 0)) {
/*
* bufsize == -1 : No limit on the size
* bufsize == 0 : Concept of tsdu foreign. Choose
* a value.
*/
return ((uint_t)RPC_MAXDATASIZE);
}
return ((uint_t)bufsize);
}
if ((bufsize == -1) || (bufsize == 0))
return ((uint_t)size);
/* Check whether the value is within the upper max limit */
return (size > bufsize ? (uint_t)bufsize : (uint_t)size);
}
/*
* Find the appropriate address buffer size
*/
uint_t
__rpc_get_a_size(
t_scalar_t size) /* normally tinfo.addr */
{
if (size >= 0)
return ((uint_t)size);
if (size <= -2)
return ((uint_t)0);
/*
* (size == -1) No limit on the size. we impose a limit here.
*/
return ((uint_t)RPC_MAXADDRSIZE);
}
/*
* 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] == NULL))
return (_RPC_NETPATH); /* Default */
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(char *nettype)
{
char *netid;
char *netid_tcp = NULL;
char *netid_udp = NULL;
static char *netid_tcp_main;
static char *netid_udp_main;
static pthread_key_t tcp_key, udp_key;
int main_thread;
extern mutex_t tsd_lock;
if ((main_thread = thr_main())) {
netid_udp = netid_udp_main;
netid_tcp = netid_tcp_main;
} else {
if (tcp_key == 0) {
(void) mutex_lock(&tsd_lock);
if (tcp_key == 0)
(void) pthread_key_create(&tcp_key, free);
(void) mutex_unlock(&tsd_lock);
}
netid_tcp = pthread_getspecific(tcp_key);
if (udp_key == 0) {
(void) mutex_lock(&tsd_lock);
if (udp_key == 0)
(void) pthread_key_create(&udp_key, free);
(void) mutex_unlock(&tsd_lock);
}
netid_udp = pthread_getspecific(udp_key);
}
if (!netid_udp && !netid_tcp) {
struct netconfig *nconf;
void *confighandle;
if (!(confighandle = setnetconfig()))
return (NULL);
while (nconf = getnetconfig(confighandle)) {
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) {
syslog(LOG_ERR,
"__rpc_getconfip : "
"strdup failed");
return (NULL);
}
if (main_thread)
netid_tcp_main = netid_tcp;
else
(void) pthread_setspecific(
tcp_key,
(void *)netid_tcp);
} else
if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
netid_udp = strdup(nconf->nc_netid);
if (netid_udp == NULL) {
syslog(LOG_ERR,
"__rpc_getconfip : "
"strdup failed");
return (NULL);
}
if (main_thread)
netid_udp_main = netid_udp;
else
(void) pthread_setspecific(
udp_key,
(void *)netid_udp);
}
}
}
(void) 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] == NULL))
return (NULL);
return (getnetconfigent(netid));
}
/*
* Returns the type of the nettype, which should then be used with
* __rpc_getconf().
*/
void *
__rpc_setconf(char *nettype)
{
struct handle *handle;
handle = malloc(sizeof (struct handle));
if (handle == NULL)
return (NULL);
switch (handle->nettype = getnettype(nettype)) {
case _RPC_DOOR_NETPATH:
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:
case _RPC_LOCAL:
case _RPC_DOOR_LOCAL:
if (!(handle->nhandle = setnetconfig())) {
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_DOOR_NETPATH:
/*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) &&
strcmp(nconf->nc_protofmly, NC_INET6)) ||
strcmp(nconf->nc_proto, NC_TCP))
continue;
break;
case _RPC_UDP:
if ((nconf->nc_semantics != NC_TPI_CLTS) ||
(strcmp(nconf->nc_protofmly, NC_INET) &&
strcmp(nconf->nc_protofmly, NC_INET6)) ||
strcmp(nconf->nc_proto, NC_UDP))
continue;
break;
case _RPC_LOCAL:
case _RPC_DOOR_LOCAL:
if (!(nconf->nc_flag & NC_VISIBLE))
continue;
if (strcmp(nconf->nc_protofmly, NC_LOOPBACK))
continue;
break;
}
break;
}
return (nconf);
}
void
__rpc_endconf(void *vhandle)
{
struct handle *handle;
handle = (struct handle *)vhandle;
if (handle == NULL)
return;
if (handle->nflag) {
(void) endnetpath(handle->nhandle);
} else {
(void) 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);
}
/*
* Given a fd, find the transport device it is using and return the
* netconf entry corresponding to it.
* Note: It assumes servtpe parameter is 0 when uninitialized.
* That is true for xprt->xp_type field.
*/
struct netconfig *
__rpcfd_to_nconf(int fd, int servtype)
{
struct stat statbuf;
void *hndl;
struct netconfig *nconf, *newnconf = NULL;
major_t fdmajor;
struct t_info tinfo;
if (fstat(fd, &statbuf) == -1)
return (NULL);
fdmajor = major(statbuf.st_rdev);
if (servtype == 0) {
if (t_getinfo(fd, &tinfo) == -1) {
char errorstr[100];
__tli_sys_strerror(errorstr, sizeof (errorstr),
t_errno, errno);
(void) syslog(LOG_ERR, "__rpcfd_to_nconf : %s : %s",
"could not get transport information",
errorstr);
return (NULL);
}
servtype = tinfo.servtype;
}
hndl = setnetconfig();
if (hndl == NULL)
return (NULL);
/*
* Go through all transports listed in /etc/netconfig looking for
* transport device in use on fd.
* - Match on service type first
* - if that succeeds, match on major numbers (used for new local
* transport code that is self cloning)
* - if that fails, assume transport device uses clone driver
* and try match the fdmajor with minor number of device path
* which will be the major number of transport device since it
* uses the clone driver.
*/
while (nconf = getnetconfig(hndl)) {
if (__rpc_matchserv(servtype, nconf->nc_semantics) == TRUE) {
if (!stat(nconf->nc_device, &statbuf)) {
if (fdmajor == major(statbuf.st_rdev))
break; /* self cloning driver ? */
if (fdmajor == minor(statbuf.st_rdev))
break; /* clone driver! */
}
}
}
if (nconf)
newnconf = getnetconfigent(nconf->nc_netid);
(void) endnetconfig(hndl);
return (newnconf);
}
int
__rpc_matchserv(int servtype, unsigned int nc_semantics)
{
switch (servtype) {
case T_COTS:
if (nc_semantics == NC_TPI_COTS)
return (TRUE);
break;
case T_COTS_ORD:
if (nc_semantics == NC_TPI_COTS_ORD)
return (TRUE);
break;
case T_CLTS:
if (nc_semantics == NC_TPI_CLTS)
return (TRUE);
break;
default:
/* FALSE! */
break;
}
return (FALSE);
}
/*
* Routines for RPC/Doors support.
*/
extern bool_t __inet_netdir_is_my_host(const char *);
bool_t
__rpc_is_local_host(const char *host)
{
char buf[MAXHOSTNAMELEN + 1];
if (host == NULL || strcmp(host, "localhost") == 0 ||
strcmp(host, HOST_SELF) == 0 ||
strcmp(host, HOST_SELF_CONNECT) == 0 ||
strlen(host) == 0)
return (TRUE);
if (sysinfo(SI_HOSTNAME, buf, sizeof (buf)) < 0)
return (FALSE);
if (strcmp(host, buf) == 0)
return (TRUE);
return (__inet_netdir_is_my_host(host));
}
bool_t
__rpc_try_doors(const char *nettype, bool_t *try_others)
{
switch (getnettype(nettype)) {
case _RPC_DOOR:
*try_others = FALSE;
return (TRUE);
case _RPC_DOOR_LOCAL:
case _RPC_DOOR_NETPATH:
*try_others = TRUE;
return (TRUE);
default:
*try_others = TRUE;
return (FALSE);
}
}