/* $NetBSD: clnt_generic.c,v 1.18 2000/07/06 03:10:34 christos Exp $ */12/*-3* SPDX-License-Identifier: BSD-3-Clause4*5* Copyright (c) 2010, Oracle America, Inc.6* All rights reserved.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions are met:10* - Redistributions of source code must retain the above copyright notice,11* this list of conditions and the following disclaimer.12* - Redistributions in binary form must reproduce the above copyright notice,13* this list of conditions and the following disclaimer in the documentation14* and/or other materials provided with the distribution.15* - Neither the name of the "Oracle America, Inc." nor the names of its16* contributors may be used to endorse or promote products derived17* from this software without specific prior written permission.18*19* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"20* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE21* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE22* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE23* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR24* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF25* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS26* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN27* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)28* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE29* POSSIBILITY OF SUCH DAMAGE.30*/3132/*33* Copyright (c) 1986-1996,1998 by Sun Microsystems, Inc.34* All rights reserved.35*/36#include "namespace.h"37#include "reentrant.h"38#include <sys/types.h>39#include <sys/fcntl.h>40#include <sys/socket.h>41#include <netinet/in.h>42#include <netinet/tcp.h>43#include <stdio.h>44#include <errno.h>45#include <netdb.h>46#include <syslog.h>47#include <rpc/rpc.h>48#include <rpc/nettype.h>49#include <string.h>50#include <stdlib.h>51#include <unistd.h>52#include "un-namespace.h"53#include "rpc_com.h"5455extern bool_t __rpc_is_local_host(const char *);56int __rpc_raise_fd(int);5758#ifndef NETIDLEN59#define NETIDLEN 3260#endif616263/*64* Generic client creation with version checking the value of65* vers_out is set to the highest server supported value66* vers_low <= vers_out <= vers_high AND an error results67* if this can not be done.68*69* It calls clnt_create_vers_timed() with a NULL value for the timeout70* pointer, which indicates that the default timeout should be used.71*/72CLIENT *73clnt_create_vers(const char *hostname, rpcprog_t prog, rpcvers_t *vers_out,74rpcvers_t vers_low, rpcvers_t vers_high, const char *nettype)75{7677return (clnt_create_vers_timed(hostname, prog, vers_out, vers_low,78vers_high, nettype, NULL));79}8081/*82* This the routine has the same definition as clnt_create_vers(),83* except it takes an additional timeout parameter - a pointer to84* a timeval structure. A NULL value for the pointer indicates85* that the default timeout value should be used.86*/87CLIENT *88clnt_create_vers_timed(const char *hostname, rpcprog_t prog,89rpcvers_t *vers_out, rpcvers_t vers_low, rpcvers_t vers_high,90const char *nettype, const struct timeval *tp)91{92CLIENT *clnt;93struct timeval to;94enum clnt_stat rpc_stat;95struct rpc_err rpcerr;9697clnt = clnt_create_timed(hostname, prog, vers_high, nettype, tp);98if (clnt == NULL) {99return (NULL);100}101to.tv_sec = 10;102to.tv_usec = 0;103rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,104(char *)NULL, (xdrproc_t)xdr_void, (char *)NULL, to);105if (rpc_stat == RPC_SUCCESS) {106*vers_out = vers_high;107return (clnt);108}109while (rpc_stat == RPC_PROGVERSMISMATCH && vers_high > vers_low) {110unsigned int minvers, maxvers;111112clnt_geterr(clnt, &rpcerr);113minvers = rpcerr.re_vers.low;114maxvers = rpcerr.re_vers.high;115if (maxvers < vers_high)116vers_high = maxvers;117else118vers_high--;119if (minvers > vers_low)120vers_low = minvers;121if (vers_low > vers_high) {122goto error;123}124CLNT_CONTROL(clnt, CLSET_VERS, (char *)&vers_high);125rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,126(char *)NULL, (xdrproc_t)xdr_void,127(char *)NULL, to);128if (rpc_stat == RPC_SUCCESS) {129*vers_out = vers_high;130return (clnt);131}132}133clnt_geterr(clnt, &rpcerr);134135error:136rpc_createerr.cf_stat = rpc_stat;137rpc_createerr.cf_error = rpcerr;138clnt_destroy(clnt);139return (NULL);140}141142/*143* Top level client creation routine.144* Generic client creation: takes (servers name, program-number, nettype) and145* returns client handle. Default options are set, which the user can146* change using the rpc equivalent of _ioctl()'s.147*148* It tries for all the netids in that particular class of netid until149* it succeeds.150* XXX The error message in the case of failure will be the one151* pertaining to the last create error.152*153* It calls clnt_create_timed() with the default timeout.154*/155CLIENT *156clnt_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,157const char *nettype)158{159160return (clnt_create_timed(hostname, prog, vers, nettype, NULL));161}162163/*164* This the routine has the same definition as clnt_create(),165* except it takes an additional timeout parameter - a pointer to166* a timeval structure. A NULL value for the pointer indicates167* that the default timeout value should be used.168*169* This function calls clnt_tp_create_timed().170*/171CLIENT *172clnt_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,173const char *netclass, const struct timeval *tp)174{175struct netconfig *nconf;176CLIENT *clnt = NULL;177void *handle;178enum clnt_stat save_cf_stat = RPC_SUCCESS;179struct rpc_err save_cf_error;180char nettype_array[NETIDLEN];181char *nettype = &nettype_array[0];182183if (netclass == NULL)184nettype = NULL;185else {186size_t len = strlen(netclass);187if (len >= sizeof (nettype_array)) {188rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;189return (NULL);190}191strcpy(nettype, netclass);192}193194if ((handle = __rpc_setconf((char *)nettype)) == NULL) {195rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;196return (NULL);197}198rpc_createerr.cf_stat = RPC_SUCCESS;199while (clnt == NULL) {200if ((nconf = __rpc_getconf(handle)) == NULL) {201if (rpc_createerr.cf_stat == RPC_SUCCESS)202rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;203break;204}205#ifdef CLNT_DEBUG206printf("trying netid %s\n", nconf->nc_netid);207#endif208clnt = clnt_tp_create_timed(hostname, prog, vers, nconf, tp);209if (clnt)210break;211else212/*213* Since we didn't get a name-to-address214* translation failure here, we remember215* this particular error. The object of216* this is to enable us to return to the217* caller a more-specific error than the218* unhelpful ``Name to address translation219* failed'' which might well occur if we220* merely returned the last error (because221* the local loopbacks are typically the222* last ones in /etc/netconfig and the most223* likely to be unable to translate a host224* name). We also check for a more225* meaningful error than ``unknown host226* name'' for the same reasons.227*/228if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE &&229rpc_createerr.cf_stat != RPC_UNKNOWNHOST) {230save_cf_stat = rpc_createerr.cf_stat;231save_cf_error = rpc_createerr.cf_error;232}233}234235/*236* Attempt to return an error more specific than ``Name to address237* translation failed'' or ``unknown host name''238*/239if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE ||240rpc_createerr.cf_stat == RPC_UNKNOWNHOST) &&241(save_cf_stat != RPC_SUCCESS)) {242rpc_createerr.cf_stat = save_cf_stat;243rpc_createerr.cf_error = save_cf_error;244}245__rpc_endconf(handle);246return (clnt);247}248249/*250* Generic client creation: takes (servers name, program-number, netconf) and251* returns client handle. Default options are set, which the user can252* change using the rpc equivalent of _ioctl()'s : clnt_control()253* It finds out the server address from rpcbind and calls clnt_tli_create().254*255* It calls clnt_tp_create_timed() with the default timeout.256*/257CLIENT *258clnt_tp_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,259const struct netconfig *nconf)260{261262return (clnt_tp_create_timed(hostname, prog, vers, nconf, NULL));263}264265/*266* This has the same definition as clnt_tp_create(), except it267* takes an additional parameter - a pointer to a timeval structure.268* A NULL value for the timeout pointer indicates that the default269* value for the timeout should be used.270*/271CLIENT *272clnt_tp_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,273const struct netconfig *nconf, const struct timeval *tp)274{275struct netbuf *svcaddr; /* servers address */276CLIENT *cl = NULL; /* client handle */277278if (nconf == NULL) {279rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;280return (NULL);281}282283/*284* Get the address of the server285*/286if ((svcaddr = __rpcb_findaddr_timed(prog, vers,287(struct netconfig *)nconf, (char *)hostname,288&cl, (struct timeval *)tp)) == NULL) {289/* appropriate error number is set by rpcbind libraries */290return (NULL);291}292if (cl == NULL) {293cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,294prog, vers, 0, 0);295} else {296/* Reuse the CLIENT handle and change the appropriate fields */297if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) {298if (cl->cl_netid == NULL)299cl->cl_netid = strdup(nconf->nc_netid);300if (cl->cl_tp == NULL)301cl->cl_tp = strdup(nconf->nc_device);302(void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog);303(void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers);304} else {305CLNT_DESTROY(cl);306cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,307prog, vers, 0, 0);308}309}310free(svcaddr->buf);311free(svcaddr);312return (cl);313}314315/*316* Generic client creation: returns client handle.317* Default options are set, which the user can318* change using the rpc equivalent of _ioctl()'s : clnt_control().319* If fd is RPC_ANYFD, it will be opened using nconf.320* It will be bound if not so.321* If sizes are 0; appropriate defaults will be chosen.322*/323CLIENT *324clnt_tli_create(int fd, const struct netconfig *nconf,325struct netbuf *svcaddr, rpcprog_t prog, rpcvers_t vers,326uint sendsz, uint recvsz)327{328CLIENT *cl; /* client handle */329bool_t madefd = FALSE; /* whether fd opened here */330long servtype;331int one = 1;332struct __rpc_sockinfo si;333extern int __rpc_minfd;334335if (fd == RPC_ANYFD) {336if (nconf == NULL) {337rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;338return (NULL);339}340341fd = __rpc_nconf2fd(nconf);342343if (fd == -1)344goto err;345if (fd < __rpc_minfd)346fd = __rpc_raise_fd(fd);347madefd = TRUE;348servtype = nconf->nc_semantics;349if (!__rpc_fd2sockinfo(fd, &si))350goto err;351bindresvport(fd, NULL);352} else {353if (!__rpc_fd2sockinfo(fd, &si))354goto err;355servtype = __rpc_socktype2seman(si.si_socktype);356if (servtype == -1) {357rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;358return (NULL);359}360}361362if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) {363rpc_createerr.cf_stat = RPC_UNKNOWNHOST; /* XXX */364goto err1;365}366367switch (servtype) {368case NC_TPI_COTS:369cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);370break;371case NC_TPI_COTS_ORD:372if (nconf && ((strcmp(nconf->nc_protofmly, "inet") == 0))) {373_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one,374sizeof (one));375}376cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);377break;378case NC_TPI_CLTS:379cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz);380break;381default:382goto err;383}384385if (cl == NULL)386goto err1; /* borrow errors from clnt_dg/vc creates */387if (nconf) {388cl->cl_netid = strdup(nconf->nc_netid);389cl->cl_tp = strdup(nconf->nc_device);390} else {391cl->cl_netid = "";392cl->cl_tp = "";393}394if (madefd) {395(void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);396/* (void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL); */397}398399return (cl);400401err:402rpc_createerr.cf_stat = RPC_SYSTEMERROR;403rpc_createerr.cf_error.re_errno = errno;404err1: if (madefd)405(void)_close(fd);406return (NULL);407}408409/*410* To avoid conflicts with the "magic" file descriptors (0, 1, and 2),411* we try to not use them. The __rpc_raise_fd() routine will dup412* a descriptor to a higher value. If we fail to do it, we continue413* to use the old one (and hope for the best).414*/415int __rpc_minfd = 3;416417int418__rpc_raise_fd(int fd)419{420int nfd;421422if (fd >= __rpc_minfd)423return (fd);424425if ((nfd = _fcntl(fd, F_DUPFD, __rpc_minfd)) == -1)426return (fd);427428if (_fsync(nfd) == -1) {429_close(nfd);430return (fd);431}432433if (_close(fd) == -1) {434/* this is okay, we will syslog an error, then use the new fd */435(void) syslog(LOG_ERR,436"could not close() fd %d; mem & fd leak", fd);437}438439return (nfd);440}441442443