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