174462Salfred/*	$NetBSD: clnt_generic.c,v 1.18 2000/07/06 03:10:34 christos Exp $	*/
274462Salfred
3261057Smav/*-
4261057Smav * Copyright (c) 2010, Oracle America, Inc.
5261057Smav * All rights reserved.
699775Salfred *
7261057Smav * Redistribution and use in source and binary forms, with or without
8261057Smav * modification, are permitted provided that the following conditions are met:
9261057Smav * - Redistributions of source code must retain the above copyright notice,
10261057Smav *   this list of conditions and the following disclaimer.
11261057Smav * - Redistributions in binary form must reproduce the above copyright notice,
12261057Smav *   this list of conditions and the following disclaimer in the documentation
13261057Smav *   and/or other materials provided with the distribution.
14261057Smav * - Neither the name of the "Oracle America, Inc." nor the names of its
15261057Smav *   contributors may be used to endorse or promote products derived
16261057Smav *   from this software without specific prior written permission.
1799775Salfred *
18261057Smav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19261057Smav * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20261057Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21261057Smav * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22261057Smav * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23261057Smav * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24261057Smav * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25261057Smav * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26261057Smav * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27261057Smav * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28261057Smav * POSSIBILITY OF SUCH DAMAGE.
291901Swollman */
301901Swollman
3199775Salfred/* #ident	"@(#)clnt_generic.c	1.40	99/04/21 SMI" */
3274462Salfred
331901Swollman#if defined(LIBC_SCCS) && !defined(lint)
34136581Sobrienstatic char *sccsid2 = "from: @(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI";
35136581Sobrienstatic char *sccsid = "from: @(#)clnt_generic.c	2.2 88/08/01 4.0 RPCSRC";
361901Swollman#endif
3792990Sobrien#include <sys/cdefs.h>
3892990Sobrien__FBSDID("$FreeBSD$");
391901Swollman
401901Swollman/*
4199775Salfred * Copyright (c) 1986-1996,1998 by Sun Microsystems, Inc.
4299775Salfred * All rights reserved.
431901Swollman */
4475094Siedowse#include "namespace.h"
4574462Salfred#include "reentrant.h"
4674462Salfred#include <sys/types.h>
4799775Salfred#include <sys/fcntl.h>
481901Swollman#include <sys/socket.h>
4974462Salfred#include <netinet/in.h>
5074462Salfred#include <netinet/tcp.h>
5174462Salfred#include <stdio.h>
5271579Sdeischen#include <errno.h>
531901Swollman#include <netdb.h>
5499775Salfred#include <syslog.h>
5574462Salfred#include <rpc/rpc.h>
5674462Salfred#include <rpc/nettype.h>
5711666Sphk#include <string.h>
5874462Salfred#include <stdlib.h>
5974462Salfred#include <unistd.h>
6074462Salfred#include "un-namespace.h"
6174462Salfred#include "rpc_com.h"
621901Swollman
6399775Salfredextern bool_t __rpc_is_local_host(const char *);
6499775Salfredint __rpc_raise_fd(int);
6599775Salfred
6699775Salfred#ifndef NETIDLEN
6799775Salfred#define	NETIDLEN 32
6899775Salfred#endif
6999775Salfred
7099775Salfred
711901Swollman/*
7274462Salfred * Generic client creation with version checking the value of
7374462Salfred * vers_out is set to the highest server supported value
7474462Salfred * vers_low <= vers_out <= vers_high  AND an error results
7574462Salfred * if this can not be done.
7699775Salfred *
7799775Salfred * It calls clnt_create_vers_timed() with a NULL value for the timeout
7899775Salfred * pointer, which indicates that the default timeout should be used.
7974462Salfred */
8074462SalfredCLIENT *
8199775Salfredclnt_create_vers(const char *hostname, rpcprog_t prog, rpcvers_t *vers_out,
8299775Salfred	rpcvers_t vers_low, rpcvers_t vers_high, const char *nettype)
8374462Salfred{
8499775Salfred
8599775Salfred	return (clnt_create_vers_timed(hostname, prog, vers_out, vers_low,
8699775Salfred				vers_high, nettype, NULL));
8799775Salfred}
8899775Salfred
8999775Salfred/*
9099775Salfred * This the routine has the same definition as clnt_create_vers(),
9199775Salfred * except it takes an additional timeout parameter - a pointer to
9299775Salfred * a timeval structure.  A NULL value for the pointer indicates
9399775Salfred * that the default timeout value should be used.
9499775Salfred */
9599775SalfredCLIENT *
9699775Salfredclnt_create_vers_timed(const char *hostname, rpcprog_t prog,
9799775Salfred    rpcvers_t *vers_out, rpcvers_t vers_low, rpcvers_t vers_high,
9899775Salfred    const char *nettype, const struct timeval *tp)
9999775Salfred{
10074462Salfred	CLIENT *clnt;
10174462Salfred	struct timeval to;
10274462Salfred	enum clnt_stat rpc_stat;
10374462Salfred	struct rpc_err rpcerr;
10474462Salfred
10599775Salfred	clnt = clnt_create_timed(hostname, prog, vers_high, nettype, tp);
10674462Salfred	if (clnt == NULL) {
10774462Salfred		return (NULL);
10874462Salfred	}
10974462Salfred	to.tv_sec = 10;
11074462Salfred	to.tv_usec = 0;
11199775Salfred	rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
11299775Salfred			(char *)NULL, (xdrproc_t)xdr_void, (char *)NULL, to);
11374462Salfred	if (rpc_stat == RPC_SUCCESS) {
11474462Salfred		*vers_out = vers_high;
11574462Salfred		return (clnt);
11674462Salfred	}
11799775Salfred	while (rpc_stat == RPC_PROGVERSMISMATCH && vers_high > vers_low) {
11899775Salfred		unsigned int minvers, maxvers;
11974462Salfred
12074462Salfred		clnt_geterr(clnt, &rpcerr);
12174462Salfred		minvers = rpcerr.re_vers.low;
12274462Salfred		maxvers = rpcerr.re_vers.high;
12374462Salfred		if (maxvers < vers_high)
12499775Salfred			vers_high = maxvers;
12599775Salfred		else
12699775Salfred			vers_high--;
12774462Salfred		if (minvers > vers_low)
12899775Salfred			vers_low = minvers;
12974462Salfred		if (vers_low > vers_high) {
13074462Salfred			goto error;
13174462Salfred		}
13299775Salfred		CLNT_CONTROL(clnt, CLSET_VERS, (char *)&vers_high);
13399775Salfred		rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
13499775Salfred				(char *)NULL, (xdrproc_t)xdr_void,
13599775Salfred				(char *)NULL, to);
13674462Salfred		if (rpc_stat == RPC_SUCCESS) {
13774462Salfred			*vers_out = vers_high;
13874462Salfred			return (clnt);
13974462Salfred		}
14074462Salfred	}
14174462Salfred	clnt_geterr(clnt, &rpcerr);
14274462Salfred
14374462Salfrederror:
14474462Salfred	rpc_createerr.cf_stat = rpc_stat;
14574462Salfred	rpc_createerr.cf_error = rpcerr;
14674462Salfred	clnt_destroy(clnt);
14774462Salfred	return (NULL);
14874462Salfred}
14974462Salfred
15074462Salfred/*
15174462Salfred * Top level client creation routine.
15274462Salfred * Generic client creation: takes (servers name, program-number, nettype) and
1538870Srgrimes * returns client handle. Default options are set, which the user can
15471579Sdeischen * change using the rpc equivalent of _ioctl()'s.
15574462Salfred *
15674462Salfred * It tries for all the netids in that particular class of netid until
15774462Salfred * it succeeds.
15874462Salfred * XXX The error message in the case of failure will be the one
15974462Salfred * pertaining to the last create error.
16074462Salfred *
16199775Salfred * It calls clnt_create_timed() with the default timeout.
1621901Swollman */
1631901SwollmanCLIENT *
16499775Salfredclnt_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,
16599775Salfred    const char *nettype)
1661901Swollman{
16799775Salfred
16899775Salfred	return (clnt_create_timed(hostname, prog, vers, nettype, NULL));
16999775Salfred}
17099775Salfred
17199775Salfred/*
17299775Salfred * This the routine has the same definition as clnt_create(),
17399775Salfred * except it takes an additional timeout parameter - a pointer to
17499775Salfred * a timeval structure.  A NULL value for the pointer indicates
17599775Salfred * that the default timeout value should be used.
17699775Salfred *
17799775Salfred * This function calls clnt_tp_create_timed().
17899775Salfred */
17999775SalfredCLIENT *
18099775Salfredclnt_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
18199775Salfred    const char *netclass, const struct timeval *tp)
18299775Salfred{
18374462Salfred	struct netconfig *nconf;
18474462Salfred	CLIENT *clnt = NULL;
18574462Salfred	void *handle;
18674462Salfred	enum clnt_stat	save_cf_stat = RPC_SUCCESS;
18774462Salfred	struct rpc_err	save_cf_error;
18899775Salfred	char nettype_array[NETIDLEN];
18999775Salfred	char *nettype = &nettype_array[0];
1901901Swollman
19199775Salfred	if (netclass == NULL)
19299775Salfred		nettype = NULL;
19399775Salfred	else {
19499775Salfred		size_t len = strlen(netclass);
19599775Salfred		if (len >= sizeof (nettype_array)) {
19699775Salfred			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
19799775Salfred			return (NULL);
19899775Salfred		}
19999775Salfred		strcpy(nettype, netclass);
20099775Salfred	}
20126221Swpaul
20299775Salfred	if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
20374462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
2041901Swollman		return (NULL);
2051901Swollman	}
20674462Salfred	rpc_createerr.cf_stat = RPC_SUCCESS;
20774462Salfred	while (clnt == NULL) {
20874462Salfred		if ((nconf = __rpc_getconf(handle)) == NULL) {
20974462Salfred			if (rpc_createerr.cf_stat == RPC_SUCCESS)
21074462Salfred				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
21174462Salfred			break;
21274462Salfred		}
21374462Salfred#ifdef CLNT_DEBUG
21474462Salfred		printf("trying netid %s\n", nconf->nc_netid);
21574462Salfred#endif
21699775Salfred		clnt = clnt_tp_create_timed(hostname, prog, vers, nconf, tp);
21774462Salfred		if (clnt)
21874462Salfred			break;
21974462Salfred		else
22074462Salfred			/*
22174462Salfred			 *	Since we didn't get a name-to-address
22274462Salfred			 *	translation failure here, we remember
22374462Salfred			 *	this particular error.  The object of
22474462Salfred			 *	this is to enable us to return to the
22574462Salfred			 *	caller a more-specific error than the
22674462Salfred			 *	unhelpful ``Name to address translation
22774462Salfred			 *	failed'' which might well occur if we
22874462Salfred			 *	merely returned the last error (because
22974462Salfred			 *	the local loopbacks are typically the
23074462Salfred			 *	last ones in /etc/netconfig and the most
23174462Salfred			 *	likely to be unable to translate a host
23299775Salfred			 *	name).  We also check for a more
23399775Salfred			 *	meaningful error than ``unknown host
23499775Salfred			 *	name'' for the same reasons.
23574462Salfred			 */
23699775Salfred			if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE &&
23799775Salfred			    rpc_createerr.cf_stat != RPC_UNKNOWNHOST) {
23874462Salfred				save_cf_stat = rpc_createerr.cf_stat;
23974462Salfred				save_cf_error = rpc_createerr.cf_error;
24074462Salfred			}
2411901Swollman	}
24274462Salfred
24374462Salfred	/*
24474462Salfred	 *	Attempt to return an error more specific than ``Name to address
24599775Salfred	 *	translation failed'' or ``unknown host name''
24674462Salfred	 */
24799775Salfred	if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE ||
24899775Salfred				rpc_createerr.cf_stat == RPC_UNKNOWNHOST) &&
24999775Salfred					(save_cf_stat != RPC_SUCCESS)) {
25074462Salfred		rpc_createerr.cf_stat = save_cf_stat;
25174462Salfred		rpc_createerr.cf_error = save_cf_error;
25274462Salfred	}
25374462Salfred	__rpc_endconf(handle);
25474462Salfred	return (clnt);
25574462Salfred}
25674462Salfred
25774462Salfred/*
25874462Salfred * Generic client creation: takes (servers name, program-number, netconf) and
25974462Salfred * returns client handle. Default options are set, which the user can
26074462Salfred * change using the rpc equivalent of _ioctl()'s : clnt_control()
26199775Salfred * It finds out the server address from rpcbind and calls clnt_tli_create().
26299775Salfred *
26399775Salfred * It calls clnt_tp_create_timed() with the default timeout.
26474462Salfred */
26574462SalfredCLIENT *
26699775Salfredclnt_tp_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,
26799775Salfred    const struct netconfig *nconf)
26874462Salfred{
26999775Salfred
27099775Salfred	return (clnt_tp_create_timed(hostname, prog, vers, nconf, NULL));
27199775Salfred}
27299775Salfred
27399775Salfred/*
27499775Salfred * This has the same definition as clnt_tp_create(), except it
27599775Salfred * takes an additional parameter - a pointer to a timeval structure.
27699775Salfred * A NULL value for the timeout pointer indicates that the default
27799775Salfred * value for the timeout should be used.
27899775Salfred */
27999775SalfredCLIENT *
28099775Salfredclnt_tp_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
28199775Salfred    const struct netconfig *nconf, const struct timeval *tp)
28299775Salfred{
28374462Salfred	struct netbuf *svcaddr;			/* servers address */
28474462Salfred	CLIENT *cl = NULL;			/* client handle */
28574462Salfred
28674462Salfred	if (nconf == NULL) {
2871901Swollman		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
2881901Swollman		return (NULL);
2891901Swollman	}
29074462Salfred
29174462Salfred	/*
29274462Salfred	 * Get the address of the server
29374462Salfred	 */
29499775Salfred	if ((svcaddr = __rpcb_findaddr_timed(prog, vers,
29599775Salfred			(struct netconfig *)nconf, (char *)hostname,
29699775Salfred			&cl, (struct timeval *)tp)) == NULL) {
29774462Salfred		/* appropriate error number is set by rpcbind libraries */
29874462Salfred		return (NULL);
29974462Salfred	}
30074462Salfred	if (cl == NULL) {
30174462Salfred		cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
30274462Salfred					prog, vers, 0, 0);
30374462Salfred	} else {
30474462Salfred		/* Reuse the CLIENT handle and change the appropriate fields */
30574462Salfred		if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) {
30674462Salfred			if (cl->cl_netid == NULL)
30774462Salfred				cl->cl_netid = strdup(nconf->nc_netid);
30874462Salfred			if (cl->cl_tp == NULL)
30974462Salfred				cl->cl_tp = strdup(nconf->nc_device);
31074462Salfred			(void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog);
31174462Salfred			(void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers);
31274462Salfred		} else {
31374462Salfred			CLNT_DESTROY(cl);
31474462Salfred			cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
31574462Salfred					prog, vers, 0, 0);
3161901Swollman		}
31774462Salfred	}
31874462Salfred	free(svcaddr->buf);
31974462Salfred	free(svcaddr);
32074462Salfred	return (cl);
32174462Salfred}
32274462Salfred
32374462Salfred/*
32474462Salfred * Generic client creation:  returns client handle.
32574462Salfred * Default options are set, which the user can
32674462Salfred * change using the rpc equivalent of _ioctl()'s : clnt_control().
32774462Salfred * If fd is RPC_ANYFD, it will be opened using nconf.
32874462Salfred * It will be bound if not so.
32974462Salfred * If sizes are 0; appropriate defaults will be chosen.
33074462Salfred */
33174462SalfredCLIENT *
33299775Salfredclnt_tli_create(int fd, const struct netconfig *nconf,
33399775Salfred	struct netbuf *svcaddr, rpcprog_t prog, rpcvers_t vers,
33499775Salfred	uint sendsz, uint recvsz)
33574462Salfred{
33674462Salfred	CLIENT *cl;			/* client handle */
33774462Salfred	bool_t madefd = FALSE;		/* whether fd opened here */
33874462Salfred	long servtype;
33974462Salfred	int one = 1;
34074462Salfred	struct __rpc_sockinfo si;
34199775Salfred	extern int __rpc_minfd;
34274462Salfred
34374462Salfred	if (fd == RPC_ANYFD) {
34474462Salfred		if (nconf == NULL) {
34574462Salfred			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
3461901Swollman			return (NULL);
3471901Swollman		}
34874462Salfred
34974462Salfred		fd = __rpc_nconf2fd(nconf);
35074462Salfred
35174462Salfred		if (fd == -1)
35274462Salfred			goto err;
35399775Salfred		if (fd < __rpc_minfd)
35499775Salfred			fd = __rpc_raise_fd(fd);
35574462Salfred		madefd = TRUE;
35674462Salfred		servtype = nconf->nc_semantics;
35774462Salfred		if (!__rpc_fd2sockinfo(fd, &si))
35874462Salfred			goto err;
35974462Salfred		bindresvport(fd, NULL);
36074462Salfred	} else {
36174462Salfred		if (!__rpc_fd2sockinfo(fd, &si))
36274462Salfred			goto err;
36374462Salfred		servtype = __rpc_socktype2seman(si.si_socktype);
36474462Salfred		if (servtype == -1) {
36574462Salfred			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
36699775Salfred			return (NULL);
36774462Salfred		}
36874462Salfred	}
36974462Salfred
37074462Salfred	if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) {
37174462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNHOST;	/* XXX */
37274462Salfred		goto err1;
37374462Salfred	}
37474462Salfred
37574462Salfred	switch (servtype) {
37699775Salfred	case NC_TPI_COTS:
37799775Salfred		cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
37899775Salfred		break;
37974462Salfred	case NC_TPI_COTS_ORD:
38099775Salfred		if (nconf && ((strcmp(nconf->nc_protofmly, "inet") == 0))) {
38174462Salfred			_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one,
38274462Salfred			    sizeof (one));
38399775Salfred		}
38499775Salfred		cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
3851901Swollman		break;
38674462Salfred	case NC_TPI_CLTS:
38774462Salfred		cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz);
38874462Salfred		break;
3891901Swollman	default:
39074462Salfred		goto err;
3911901Swollman	}
39274462Salfred
39374462Salfred	if (cl == NULL)
39474462Salfred		goto err1; /* borrow errors from clnt_dg/vc creates */
39574462Salfred	if (nconf) {
39674462Salfred		cl->cl_netid = strdup(nconf->nc_netid);
39774462Salfred		cl->cl_tp = strdup(nconf->nc_device);
39874462Salfred	} else {
39974462Salfred		cl->cl_netid = "";
40074462Salfred		cl->cl_tp = "";
40174462Salfred	}
40274462Salfred	if (madefd) {
40374462Salfred		(void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
40499775Salfred/*		(void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL);  */
40574462Salfred	};
40674462Salfred
40774462Salfred	return (cl);
40874462Salfred
40974462Salfrederr:
41074462Salfred	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
41174462Salfred	rpc_createerr.cf_error.re_errno = errno;
41274462Salfrederr1:	if (madefd)
41374462Salfred		(void)_close(fd);
41474462Salfred	return (NULL);
4151901Swollman}
41699775Salfred
41799775Salfred/*
41899775Salfred *  To avoid conflicts with the "magic" file descriptors (0, 1, and 2),
41999775Salfred *  we try to not use them.  The __rpc_raise_fd() routine will dup
42099775Salfred *  a descriptor to a higher value.  If we fail to do it, we continue
42199775Salfred *  to use the old one (and hope for the best).
42299775Salfred */
42399775Salfredint __rpc_minfd = 3;
42499775Salfred
42599775Salfredint
42699775Salfred__rpc_raise_fd(int fd)
42799775Salfred{
42899775Salfred	int nfd;
42999775Salfred
43099775Salfred	if (fd >= __rpc_minfd)
43199775Salfred		return (fd);
43299775Salfred
43399775Salfred	if ((nfd = _fcntl(fd, F_DUPFD, __rpc_minfd)) == -1)
43499775Salfred		return (fd);
43599775Salfred
43699775Salfred	if (_fsync(nfd) == -1) {
43799775Salfred		_close(nfd);
43899775Salfred		return (fd);
43999775Salfred	}
44099775Salfred
44199775Salfred	if (_close(fd) == -1) {
44299775Salfred		/* this is okay, we will syslog an error, then use the new fd */
44399775Salfred		(void) syslog(LOG_ERR,
44499775Salfred			"could not close() fd %d; mem & fd leak", fd);
44599775Salfred	}
44699775Salfred
44799775Salfred	return (nfd);
44899775Salfred}
449