/* * 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 2005 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" /* * rpc_generic.c, 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> #if defined(sparc) #define _STAT _stat #define _FSTAT _fstat #else /* !sparc */ #define _STAT stat #define _FSTAT fstat #endif /* sparc */ 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); } }