174462Salfred/*	$NetBSD: clnt_generic.c,v 1.18 2000/07/06 03:10:34 christos Exp $	*/
274462Salfred
31901Swollman/*
499775Salfred * The contents of this file are subject to the Sun Standards
599775Salfred * License Version 1.0 the (the "License";) You may not use
699775Salfred * this file except in compliance with the License.  You may
799775Salfred * obtain a copy of the License at lib/libc/rpc/LICENSE
899775Salfred *
999775Salfred * Software distributed under the License is distributed on
1099775Salfred * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
1199775Salfred * express or implied.  See the License for the specific
1299775Salfred * language governing rights and limitations under the License.
1399775Salfred *
1499775Salfred * The Original Code is Copyright 1998 by Sun Microsystems, Inc
1599775Salfred *
1699775Salfred * The Initial Developer of the Original Code is:  Sun
1799775Salfred * Microsystems, Inc.
1899775Salfred *
1999775Salfred * All Rights Reserved.
2099775Salfred *
211901Swollman * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
221901Swollman * unrestricted use provided that this legend is included on all tape
231901Swollman * media and as a part of the software program in whole or part.  Users
241901Swollman * may copy or modify Sun RPC without charge, but are not authorized
251901Swollman * to license or distribute it to anyone else except as part of a product or
261901Swollman * program developed by the user.
278870Srgrimes *
281901Swollman * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
291901Swollman * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
301901Swollman * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
318870Srgrimes *
321901Swollman * Sun RPC is provided with no support and without any obligation on the
331901Swollman * part of Sun Microsystems, Inc. to assist in its use, correction,
341901Swollman * modification or enhancement.
351901Swollman *
361901Swollman * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
371901Swollman * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
381901Swollman * OR ANY PART THEREOF.
398870Srgrimes *
401901Swollman * In no event will Sun Microsystems, Inc. be liable for any lost revenue
411901Swollman * or profits or other special, indirect and consequential damages, even if
421901Swollman * Sun has been advised of the possibility of such damages.
438870Srgrimes *
441901Swollman * Sun Microsystems, Inc.
451901Swollman * 2550 Garcia Avenue
461901Swollman * Mountain View, California  94043
471901Swollman */
481901Swollman
4999775Salfred/* #ident	"@(#)clnt_generic.c	1.40	99/04/21 SMI" */
5074462Salfred
511901Swollman#if defined(LIBC_SCCS) && !defined(lint)
52136581Sobrienstatic char *sccsid2 = "from: @(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI";
53136581Sobrienstatic char *sccsid = "from: @(#)clnt_generic.c	2.2 88/08/01 4.0 RPCSRC";
541901Swollman#endif
5592990Sobrien#include <sys/cdefs.h>
5692990Sobrien__FBSDID("$FreeBSD$");
571901Swollman
581901Swollman/*
5999775Salfred * Copyright (c) 1986-1996,1998 by Sun Microsystems, Inc.
6099775Salfred * All rights reserved.
611901Swollman */
6275094Siedowse#include "namespace.h"
6374462Salfred#include "reentrant.h"
6474462Salfred#include <sys/types.h>
6599775Salfred#include <sys/fcntl.h>
661901Swollman#include <sys/socket.h>
6774462Salfred#include <netinet/in.h>
6874462Salfred#include <netinet/tcp.h>
6974462Salfred#include <stdio.h>
7071579Sdeischen#include <errno.h>
711901Swollman#include <netdb.h>
7299775Salfred#include <syslog.h>
7374462Salfred#include <rpc/rpc.h>
7474462Salfred#include <rpc/nettype.h>
7511666Sphk#include <string.h>
7674462Salfred#include <stdlib.h>
7774462Salfred#include <unistd.h>
7874462Salfred#include "un-namespace.h"
7974462Salfred#include "rpc_com.h"
801901Swollman
8199775Salfredextern bool_t __rpc_is_local_host(const char *);
8299775Salfredint __rpc_raise_fd(int);
8399775Salfred
8499775Salfred#ifndef NETIDLEN
8599775Salfred#define	NETIDLEN 32
8699775Salfred#endif
8799775Salfred
8899775Salfred
891901Swollman/*
9074462Salfred * Generic client creation with version checking the value of
9174462Salfred * vers_out is set to the highest server supported value
9274462Salfred * vers_low <= vers_out <= vers_high  AND an error results
9374462Salfred * if this can not be done.
9499775Salfred *
9599775Salfred * It calls clnt_create_vers_timed() with a NULL value for the timeout
9699775Salfred * pointer, which indicates that the default timeout should be used.
9774462Salfred */
9874462SalfredCLIENT *
9999775Salfredclnt_create_vers(const char *hostname, rpcprog_t prog, rpcvers_t *vers_out,
10099775Salfred	rpcvers_t vers_low, rpcvers_t vers_high, const char *nettype)
10174462Salfred{
10299775Salfred
10399775Salfred	return (clnt_create_vers_timed(hostname, prog, vers_out, vers_low,
10499775Salfred				vers_high, nettype, NULL));
10599775Salfred}
10699775Salfred
10799775Salfred/*
10899775Salfred * This the routine has the same definition as clnt_create_vers(),
10999775Salfred * except it takes an additional timeout parameter - a pointer to
11099775Salfred * a timeval structure.  A NULL value for the pointer indicates
11199775Salfred * that the default timeout value should be used.
11299775Salfred */
11399775SalfredCLIENT *
11499775Salfredclnt_create_vers_timed(const char *hostname, rpcprog_t prog,
11599775Salfred    rpcvers_t *vers_out, rpcvers_t vers_low, rpcvers_t vers_high,
11699775Salfred    const char *nettype, const struct timeval *tp)
11799775Salfred{
11874462Salfred	CLIENT *clnt;
11974462Salfred	struct timeval to;
12074462Salfred	enum clnt_stat rpc_stat;
12174462Salfred	struct rpc_err rpcerr;
12274462Salfred
12399775Salfred	clnt = clnt_create_timed(hostname, prog, vers_high, nettype, tp);
12474462Salfred	if (clnt == NULL) {
12574462Salfred		return (NULL);
12674462Salfred	}
12774462Salfred	to.tv_sec = 10;
12874462Salfred	to.tv_usec = 0;
12999775Salfred	rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
13099775Salfred			(char *)NULL, (xdrproc_t)xdr_void, (char *)NULL, to);
13174462Salfred	if (rpc_stat == RPC_SUCCESS) {
13274462Salfred		*vers_out = vers_high;
13374462Salfred		return (clnt);
13474462Salfred	}
13599775Salfred	while (rpc_stat == RPC_PROGVERSMISMATCH && vers_high > vers_low) {
13699775Salfred		unsigned int minvers, maxvers;
13774462Salfred
13874462Salfred		clnt_geterr(clnt, &rpcerr);
13974462Salfred		minvers = rpcerr.re_vers.low;
14074462Salfred		maxvers = rpcerr.re_vers.high;
14174462Salfred		if (maxvers < vers_high)
14299775Salfred			vers_high = maxvers;
14399775Salfred		else
14499775Salfred			vers_high--;
14574462Salfred		if (minvers > vers_low)
14699775Salfred			vers_low = minvers;
14774462Salfred		if (vers_low > vers_high) {
14874462Salfred			goto error;
14974462Salfred		}
15099775Salfred		CLNT_CONTROL(clnt, CLSET_VERS, (char *)&vers_high);
15199775Salfred		rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
15299775Salfred				(char *)NULL, (xdrproc_t)xdr_void,
15399775Salfred				(char *)NULL, to);
15474462Salfred		if (rpc_stat == RPC_SUCCESS) {
15574462Salfred			*vers_out = vers_high;
15674462Salfred			return (clnt);
15774462Salfred		}
15874462Salfred	}
15974462Salfred	clnt_geterr(clnt, &rpcerr);
16074462Salfred
16174462Salfrederror:
16274462Salfred	rpc_createerr.cf_stat = rpc_stat;
16374462Salfred	rpc_createerr.cf_error = rpcerr;
16474462Salfred	clnt_destroy(clnt);
16574462Salfred	return (NULL);
16674462Salfred}
16774462Salfred
16874462Salfred/*
16974462Salfred * Top level client creation routine.
17074462Salfred * Generic client creation: takes (servers name, program-number, nettype) and
1718870Srgrimes * returns client handle. Default options are set, which the user can
17271579Sdeischen * change using the rpc equivalent of _ioctl()'s.
17374462Salfred *
17474462Salfred * It tries for all the netids in that particular class of netid until
17574462Salfred * it succeeds.
17674462Salfred * XXX The error message in the case of failure will be the one
17774462Salfred * pertaining to the last create error.
17874462Salfred *
17999775Salfred * It calls clnt_create_timed() with the default timeout.
1801901Swollman */
1811901SwollmanCLIENT *
18299775Salfredclnt_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,
18399775Salfred    const char *nettype)
1841901Swollman{
18599775Salfred
18699775Salfred	return (clnt_create_timed(hostname, prog, vers, nettype, NULL));
18799775Salfred}
18899775Salfred
18999775Salfred/*
19099775Salfred * This the routine has the same definition as clnt_create(),
19199775Salfred * except it takes an additional timeout parameter - a pointer to
19299775Salfred * a timeval structure.  A NULL value for the pointer indicates
19399775Salfred * that the default timeout value should be used.
19499775Salfred *
19599775Salfred * This function calls clnt_tp_create_timed().
19699775Salfred */
19799775SalfredCLIENT *
19899775Salfredclnt_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
19999775Salfred    const char *netclass, const struct timeval *tp)
20099775Salfred{
20174462Salfred	struct netconfig *nconf;
20274462Salfred	CLIENT *clnt = NULL;
20374462Salfred	void *handle;
20474462Salfred	enum clnt_stat	save_cf_stat = RPC_SUCCESS;
20574462Salfred	struct rpc_err	save_cf_error;
20699775Salfred	char nettype_array[NETIDLEN];
20799775Salfred	char *nettype = &nettype_array[0];
2081901Swollman
20999775Salfred	if (netclass == NULL)
21099775Salfred		nettype = NULL;
21199775Salfred	else {
21299775Salfred		size_t len = strlen(netclass);
21399775Salfred		if (len >= sizeof (nettype_array)) {
21499775Salfred			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
21599775Salfred			return (NULL);
21699775Salfred		}
21799775Salfred		strcpy(nettype, netclass);
21899775Salfred	}
21926221Swpaul
22099775Salfred	if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
22174462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
2221901Swollman		return (NULL);
2231901Swollman	}
22474462Salfred	rpc_createerr.cf_stat = RPC_SUCCESS;
22574462Salfred	while (clnt == NULL) {
22674462Salfred		if ((nconf = __rpc_getconf(handle)) == NULL) {
22774462Salfred			if (rpc_createerr.cf_stat == RPC_SUCCESS)
22874462Salfred				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
22974462Salfred			break;
23074462Salfred		}
23174462Salfred#ifdef CLNT_DEBUG
23274462Salfred		printf("trying netid %s\n", nconf->nc_netid);
23374462Salfred#endif
23499775Salfred		clnt = clnt_tp_create_timed(hostname, prog, vers, nconf, tp);
23574462Salfred		if (clnt)
23674462Salfred			break;
23774462Salfred		else
23874462Salfred			/*
23974462Salfred			 *	Since we didn't get a name-to-address
24074462Salfred			 *	translation failure here, we remember
24174462Salfred			 *	this particular error.  The object of
24274462Salfred			 *	this is to enable us to return to the
24374462Salfred			 *	caller a more-specific error than the
24474462Salfred			 *	unhelpful ``Name to address translation
24574462Salfred			 *	failed'' which might well occur if we
24674462Salfred			 *	merely returned the last error (because
24774462Salfred			 *	the local loopbacks are typically the
24874462Salfred			 *	last ones in /etc/netconfig and the most
24974462Salfred			 *	likely to be unable to translate a host
25099775Salfred			 *	name).  We also check for a more
25199775Salfred			 *	meaningful error than ``unknown host
25299775Salfred			 *	name'' for the same reasons.
25374462Salfred			 */
25499775Salfred			if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE &&
25599775Salfred			    rpc_createerr.cf_stat != RPC_UNKNOWNHOST) {
25674462Salfred				save_cf_stat = rpc_createerr.cf_stat;
25774462Salfred				save_cf_error = rpc_createerr.cf_error;
25874462Salfred			}
2591901Swollman	}
26074462Salfred
26174462Salfred	/*
26274462Salfred	 *	Attempt to return an error more specific than ``Name to address
26399775Salfred	 *	translation failed'' or ``unknown host name''
26474462Salfred	 */
26599775Salfred	if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE ||
26699775Salfred				rpc_createerr.cf_stat == RPC_UNKNOWNHOST) &&
26799775Salfred					(save_cf_stat != RPC_SUCCESS)) {
26874462Salfred		rpc_createerr.cf_stat = save_cf_stat;
26974462Salfred		rpc_createerr.cf_error = save_cf_error;
27074462Salfred	}
27174462Salfred	__rpc_endconf(handle);
27274462Salfred	return (clnt);
27374462Salfred}
27474462Salfred
27574462Salfred/*
27674462Salfred * Generic client creation: takes (servers name, program-number, netconf) and
27774462Salfred * returns client handle. Default options are set, which the user can
27874462Salfred * change using the rpc equivalent of _ioctl()'s : clnt_control()
27999775Salfred * It finds out the server address from rpcbind and calls clnt_tli_create().
28099775Salfred *
28199775Salfred * It calls clnt_tp_create_timed() with the default timeout.
28274462Salfred */
28374462SalfredCLIENT *
28499775Salfredclnt_tp_create(const char *hostname, rpcprog_t prog, rpcvers_t vers,
28599775Salfred    const struct netconfig *nconf)
28674462Salfred{
28799775Salfred
28899775Salfred	return (clnt_tp_create_timed(hostname, prog, vers, nconf, NULL));
28999775Salfred}
29099775Salfred
29199775Salfred/*
29299775Salfred * This has the same definition as clnt_tp_create(), except it
29399775Salfred * takes an additional parameter - a pointer to a timeval structure.
29499775Salfred * A NULL value for the timeout pointer indicates that the default
29599775Salfred * value for the timeout should be used.
29699775Salfred */
29799775SalfredCLIENT *
29899775Salfredclnt_tp_create_timed(const char *hostname, rpcprog_t prog, rpcvers_t vers,
29999775Salfred    const struct netconfig *nconf, const struct timeval *tp)
30099775Salfred{
30174462Salfred	struct netbuf *svcaddr;			/* servers address */
30274462Salfred	CLIENT *cl = NULL;			/* client handle */
30374462Salfred
30474462Salfred	if (nconf == NULL) {
3051901Swollman		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
3061901Swollman		return (NULL);
3071901Swollman	}
30874462Salfred
30974462Salfred	/*
31074462Salfred	 * Get the address of the server
31174462Salfred	 */
31299775Salfred	if ((svcaddr = __rpcb_findaddr_timed(prog, vers,
31399775Salfred			(struct netconfig *)nconf, (char *)hostname,
31499775Salfred			&cl, (struct timeval *)tp)) == NULL) {
31574462Salfred		/* appropriate error number is set by rpcbind libraries */
31674462Salfred		return (NULL);
31774462Salfred	}
31874462Salfred	if (cl == NULL) {
31974462Salfred		cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
32074462Salfred					prog, vers, 0, 0);
32174462Salfred	} else {
32274462Salfred		/* Reuse the CLIENT handle and change the appropriate fields */
32374462Salfred		if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) {
32474462Salfred			if (cl->cl_netid == NULL)
32574462Salfred				cl->cl_netid = strdup(nconf->nc_netid);
32674462Salfred			if (cl->cl_tp == NULL)
32774462Salfred				cl->cl_tp = strdup(nconf->nc_device);
32874462Salfred			(void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog);
32974462Salfred			(void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers);
33074462Salfred		} else {
33174462Salfred			CLNT_DESTROY(cl);
33274462Salfred			cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
33374462Salfred					prog, vers, 0, 0);
3341901Swollman		}
33574462Salfred	}
33674462Salfred	free(svcaddr->buf);
33774462Salfred	free(svcaddr);
33874462Salfred	return (cl);
33974462Salfred}
34074462Salfred
34174462Salfred/*
34274462Salfred * Generic client creation:  returns client handle.
34374462Salfred * Default options are set, which the user can
34474462Salfred * change using the rpc equivalent of _ioctl()'s : clnt_control().
34574462Salfred * If fd is RPC_ANYFD, it will be opened using nconf.
34674462Salfred * It will be bound if not so.
34774462Salfred * If sizes are 0; appropriate defaults will be chosen.
34874462Salfred */
34974462SalfredCLIENT *
35099775Salfredclnt_tli_create(int fd, const struct netconfig *nconf,
35199775Salfred	struct netbuf *svcaddr, rpcprog_t prog, rpcvers_t vers,
35299775Salfred	uint sendsz, uint recvsz)
35374462Salfred{
35474462Salfred	CLIENT *cl;			/* client handle */
35574462Salfred	bool_t madefd = FALSE;		/* whether fd opened here */
35674462Salfred	long servtype;
35774462Salfred	int one = 1;
35874462Salfred	struct __rpc_sockinfo si;
35999775Salfred	extern int __rpc_minfd;
36074462Salfred
36174462Salfred	if (fd == RPC_ANYFD) {
36274462Salfred		if (nconf == NULL) {
36374462Salfred			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
3641901Swollman			return (NULL);
3651901Swollman		}
36674462Salfred
36774462Salfred		fd = __rpc_nconf2fd(nconf);
36874462Salfred
36974462Salfred		if (fd == -1)
37074462Salfred			goto err;
37199775Salfred		if (fd < __rpc_minfd)
37299775Salfred			fd = __rpc_raise_fd(fd);
37374462Salfred		madefd = TRUE;
37474462Salfred		servtype = nconf->nc_semantics;
37574462Salfred		if (!__rpc_fd2sockinfo(fd, &si))
37674462Salfred			goto err;
37774462Salfred		bindresvport(fd, NULL);
37874462Salfred	} else {
37974462Salfred		if (!__rpc_fd2sockinfo(fd, &si))
38074462Salfred			goto err;
38174462Salfred		servtype = __rpc_socktype2seman(si.si_socktype);
38274462Salfred		if (servtype == -1) {
38374462Salfred			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
38499775Salfred			return (NULL);
38574462Salfred		}
38674462Salfred	}
38774462Salfred
38874462Salfred	if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) {
38974462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNHOST;	/* XXX */
39074462Salfred		goto err1;
39174462Salfred	}
39274462Salfred
39374462Salfred	switch (servtype) {
39499775Salfred	case NC_TPI_COTS:
39599775Salfred		cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
39699775Salfred		break;
39774462Salfred	case NC_TPI_COTS_ORD:
39899775Salfred		if (nconf && ((strcmp(nconf->nc_protofmly, "inet") == 0))) {
39974462Salfred			_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one,
40074462Salfred			    sizeof (one));
40199775Salfred		}
40299775Salfred		cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz);
4031901Swollman		break;
40474462Salfred	case NC_TPI_CLTS:
40574462Salfred		cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz);
40674462Salfred		break;
4071901Swollman	default:
40874462Salfred		goto err;
4091901Swollman	}
41074462Salfred
41174462Salfred	if (cl == NULL)
41274462Salfred		goto err1; /* borrow errors from clnt_dg/vc creates */
41374462Salfred	if (nconf) {
41474462Salfred		cl->cl_netid = strdup(nconf->nc_netid);
41574462Salfred		cl->cl_tp = strdup(nconf->nc_device);
41674462Salfred	} else {
41774462Salfred		cl->cl_netid = "";
41874462Salfred		cl->cl_tp = "";
41974462Salfred	}
42074462Salfred	if (madefd) {
42174462Salfred		(void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
42299775Salfred/*		(void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL);  */
42374462Salfred	};
42474462Salfred
42574462Salfred	return (cl);
42674462Salfred
42774462Salfrederr:
42874462Salfred	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
42974462Salfred	rpc_createerr.cf_error.re_errno = errno;
43074462Salfrederr1:	if (madefd)
43174462Salfred		(void)_close(fd);
43274462Salfred	return (NULL);
4331901Swollman}
43499775Salfred
43599775Salfred/*
43699775Salfred *  To avoid conflicts with the "magic" file descriptors (0, 1, and 2),
43799775Salfred *  we try to not use them.  The __rpc_raise_fd() routine will dup
43899775Salfred *  a descriptor to a higher value.  If we fail to do it, we continue
43999775Salfred *  to use the old one (and hope for the best).
44099775Salfred */
44199775Salfredint __rpc_minfd = 3;
44299775Salfred
44399775Salfredint
44499775Salfred__rpc_raise_fd(int fd)
44599775Salfred{
44699775Salfred	int nfd;
44799775Salfred
44899775Salfred	if (fd >= __rpc_minfd)
44999775Salfred		return (fd);
45099775Salfred
45199775Salfred	if ((nfd = _fcntl(fd, F_DUPFD, __rpc_minfd)) == -1)
45299775Salfred		return (fd);
45399775Salfred
45499775Salfred	if (_fsync(nfd) == -1) {
45599775Salfred		_close(nfd);
45699775Salfred		return (fd);
45799775Salfred	}
45899775Salfred
45999775Salfred	if (_close(fd) == -1) {
46099775Salfred		/* this is okay, we will syslog an error, then use the new fd */
46199775Salfred		(void) syslog(LOG_ERR,
46299775Salfred			"could not close() fd %d; mem & fd leak", fd);
46399775Salfred	}
46499775Salfred
46599775Salfred	return (nfd);
46699775Salfred}
467