174462Salfred/*	$NetBSD: rpcb_clnt.c,v 1.6 2000/07/16 06:41:43 itojun Exp $	*/
274462Salfred
3261046Smav/*-
4261046Smav * Copyright (c) 2010, Oracle America, Inc.
5261046Smav * All rights reserved.
699775Salfred *
7261046Smav * Redistribution and use in source and binary forms, with or without
8261046Smav * modification, are permitted provided that the following conditions are met:
9261046Smav * - Redistributions of source code must retain the above copyright notice,
10261046Smav *   this list of conditions and the following disclaimer.
11261046Smav * - Redistributions in binary form must reproduce the above copyright notice,
12261046Smav *   this list of conditions and the following disclaimer in the documentation
13261046Smav *   and/or other materials provided with the distribution.
14261046Smav * - Neither the name of the "Oracle America, Inc." nor the names of its
15261046Smav *   contributors may be used to endorse or promote products derived
16261046Smav *   from this software without specific prior written permission.
1799775Salfred *
18261046Smav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19261046Smav * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20261046Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21261046Smav * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22261046Smav * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23261046Smav * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24261046Smav * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25261046Smav * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26261046Smav * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27261046Smav * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28261046Smav * POSSIBILITY OF SUCH DAMAGE.
2974462Salfred */
3074462Salfred
3174462Salfred/* #ident	"@(#)rpcb_clnt.c	1.27	94/04/24 SMI" */
3274462Salfred
3374462Salfred
34136581Sobrien#if defined(LIBC_SCCS) && !defined(lint)
3574462Salfredstatic char sccsid[] = "@(#)rpcb_clnt.c 1.30 89/06/21 Copyr 1988 Sun Micro";
3674462Salfred#endif
3792990Sobrien#include <sys/cdefs.h>
3892990Sobrien__FBSDID("$FreeBSD: stable/10/lib/libc/rpc/rpcb_clnt.c 320325 2017-06-25 05:46:03Z delphij $");
3974462Salfred
4074462Salfred/*
4174462Salfred * rpcb_clnt.c
4274462Salfred * interface to rpcbind rpc service.
4374462Salfred */
4474462Salfred
4575094Siedowse#include "namespace.h"
4674462Salfred#include "reentrant.h"
4774462Salfred#include <sys/types.h>
4874462Salfred#include <sys/socket.h>
4974462Salfred#include <sys/un.h>
5074462Salfred#include <sys/utsname.h>
5174462Salfred#include <rpc/rpc.h>
5274462Salfred#include <rpc/rpcb_prot.h>
5374462Salfred#include <rpc/nettype.h>
5474462Salfred#include <netconfig.h>
5574462Salfred#ifdef PORTMAP
5674462Salfred#include <netinet/in.h>		/* FOR IPPROTO_TCP/UDP definitions */
5774462Salfred#include <rpc/pmap_prot.h>
5874462Salfred#endif				/* PORTMAP */
5974462Salfred#include <stdio.h>
6074462Salfred#include <errno.h>
6174462Salfred#include <stdlib.h>
6274462Salfred#include <string.h>
6374462Salfred#include <unistd.h>
6474462Salfred#include <netdb.h>
6574462Salfred#include <syslog.h>
6674462Salfred#include "un-namespace.h"
6774462Salfred
6874462Salfred#include "rpc_com.h"
69156090Sdeischen#include "mt_misc.h"
7074462Salfred
7174462Salfredstatic struct timeval tottimeout = { 60, 0 };
7274462Salfredstatic const struct timeval rmttimeout = { 3, 0 };
7399775Salfredstatic struct timeval rpcbrmttime = { 15, 0 };
7474462Salfred
7592905Sobrienextern bool_t xdr_wrapstring(XDR *, char **);
7674462Salfred
7774462Salfredstatic const char nullstring[] = "\000";
7874462Salfred
7974462Salfred#define	CACHESIZE 6
8074462Salfred
8174462Salfredstruct address_cache {
8274462Salfred	char *ac_host;
8374462Salfred	char *ac_netid;
8474462Salfred	char *ac_uaddr;
8574462Salfred	struct netbuf *ac_taddr;
8674462Salfred	struct address_cache *ac_next;
8774462Salfred};
8874462Salfred
8974462Salfredstatic struct address_cache *front;
9074462Salfredstatic int cachesize;
9174462Salfred
9274462Salfred#define	CLCR_GET_RPCB_TIMEOUT	1
9374462Salfred#define	CLCR_SET_RPCB_TIMEOUT	2
9474462Salfred
9574462Salfred
9674462Salfredextern int __rpc_lowvers;
9774462Salfred
9892905Sobrienstatic struct address_cache *check_cache(const char *, const char *);
9992905Sobrienstatic void delete_cache(struct netbuf *);
10092941Sobrienstatic void add_cache(const char *, const char *, struct netbuf *, char *);
10192941Sobrienstatic CLIENT *getclnthandle(const char *, const struct netconfig *, char **);
10292905Sobrienstatic CLIENT *local_rpcb(void);
10392941Sobrienstatic struct netbuf *got_entry(rpcb_entry_list_ptr, const struct netconfig *);
10474462Salfred
10574462Salfred/*
10674462Salfred * This routine adjusts the timeout used for calls to the remote rpcbind.
10774462Salfred * Also, this routine can be used to set the use of portmapper version 2
10874462Salfred * only when doing rpc_broadcasts
10974462Salfred * These are private routines that may not be provided in future releases.
11074462Salfred */
11174462Salfredbool_t
112309489Sngie__rpc_control(int request, void *info)
11374462Salfred{
11474462Salfred	switch (request) {
11574462Salfred	case CLCR_GET_RPCB_TIMEOUT:
11674462Salfred		*(struct timeval *)info = tottimeout;
11774462Salfred		break;
11874462Salfred	case CLCR_SET_RPCB_TIMEOUT:
11974462Salfred		tottimeout = *(struct timeval *)info;
12074462Salfred		break;
12174462Salfred	case CLCR_SET_LOWVERS:
12274462Salfred		__rpc_lowvers = *(int *)info;
12374462Salfred		break;
12474462Salfred	case CLCR_GET_LOWVERS:
12574462Salfred		*(int *)info = __rpc_lowvers;
12674462Salfred		break;
12774462Salfred	default:
12874462Salfred		return (FALSE);
12974462Salfred	}
13074462Salfred	return (TRUE);
13174462Salfred}
13274462Salfred
13374462Salfred/*
13474462Salfred *	It might seem that a reader/writer lock would be more reasonable here.
13574462Salfred *	However because getclnthandle(), the only user of the cache functions,
13674462Salfred *	may do a delete_cache() operation if a check_cache() fails to return an
13774462Salfred *	address useful to clnt_tli_create(), we may as well use a mutex.
13874462Salfred */
13974462Salfred/*
14074462Salfred * As it turns out, if the cache lock is *not* a reader/writer lock, we will
14174462Salfred * block all clnt_create's if we are trying to connect to a host that's down,
14274462Salfred * since the lock will be held all during that time.
14374462Salfred */
14474462Salfred
14574462Salfred/*
14674462Salfred * The routines check_cache(), add_cache(), delete_cache() manage the
14774462Salfred * cache of rpcbind addresses for (host, netid).
14874462Salfred */
14974462Salfred
15074462Salfredstatic struct address_cache *
151309489Sngiecheck_cache(const char *host, const char *netid)
15274462Salfred{
15374462Salfred	struct address_cache *cptr;
15474462Salfred
15574462Salfred	/* READ LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
15674462Salfred
15774462Salfred	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
15874462Salfred		if (!strcmp(cptr->ac_host, host) &&
15974462Salfred		    !strcmp(cptr->ac_netid, netid)) {
16074462Salfred#ifdef ND_DEBUG
16174462Salfred			fprintf(stderr, "Found cache entry for %s: %s\n",
16274462Salfred				host, netid);
16374462Salfred#endif
16474462Salfred			return (cptr);
16574462Salfred		}
16674462Salfred	}
16774462Salfred	return ((struct address_cache *) NULL);
16874462Salfred}
16974462Salfred
17074462Salfredstatic void
171309489Sngiedelete_cache(struct netbuf *addr)
17274462Salfred{
17374462Salfred	struct address_cache *cptr, *prevptr = NULL;
17474462Salfred
17574462Salfred	/* WRITE LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
17674462Salfred	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
17774462Salfred		if (!memcmp(cptr->ac_taddr->buf, addr->buf, addr->len)) {
17874462Salfred			free(cptr->ac_host);
17974462Salfred			free(cptr->ac_netid);
18074462Salfred			free(cptr->ac_taddr->buf);
18174462Salfred			free(cptr->ac_taddr);
182290899Sngie			free(cptr->ac_uaddr);
18374462Salfred			if (prevptr)
18474462Salfred				prevptr->ac_next = cptr->ac_next;
18574462Salfred			else
18674462Salfred				front = cptr->ac_next;
18774462Salfred			free(cptr);
18874462Salfred			cachesize--;
18974462Salfred			break;
19074462Salfred		}
19174462Salfred		prevptr = cptr;
19274462Salfred	}
19374462Salfred}
19474462Salfred
19574462Salfredstatic void
196309487Sngieadd_cache(const char *host, const char *netid, struct netbuf *taddr,
197309487Sngie    char *uaddr)
19874462Salfred{
19974462Salfred	struct address_cache  *ad_cache, *cptr, *prevptr;
20074462Salfred
20174462Salfred	ad_cache = (struct address_cache *)
20274462Salfred			malloc(sizeof (struct address_cache));
20374462Salfred	if (!ad_cache) {
20474462Salfred		return;
20574462Salfred	}
20674462Salfred	ad_cache->ac_host = strdup(host);
20774462Salfred	ad_cache->ac_netid = strdup(netid);
20874462Salfred	ad_cache->ac_uaddr = uaddr ? strdup(uaddr) : NULL;
20974462Salfred	ad_cache->ac_taddr = (struct netbuf *)malloc(sizeof (struct netbuf));
21074462Salfred	if (!ad_cache->ac_host || !ad_cache->ac_netid || !ad_cache->ac_taddr ||
21174462Salfred		(uaddr && !ad_cache->ac_uaddr)) {
212162193Smbr		goto out;
21374462Salfred	}
21474462Salfred	ad_cache->ac_taddr->len = ad_cache->ac_taddr->maxlen = taddr->len;
21574462Salfred	ad_cache->ac_taddr->buf = (char *) malloc(taddr->len);
21674462Salfred	if (ad_cache->ac_taddr->buf == NULL) {
217162193Smbrout:
218290899Sngie		free(ad_cache->ac_host);
219290899Sngie		free(ad_cache->ac_netid);
220290899Sngie		free(ad_cache->ac_uaddr);
221290899Sngie		free(ad_cache->ac_taddr);
222162193Smbr		free(ad_cache);
22374462Salfred		return;
22474462Salfred	}
22574462Salfred	memcpy(ad_cache->ac_taddr->buf, taddr->buf, taddr->len);
22674462Salfred#ifdef ND_DEBUG
22774462Salfred	fprintf(stderr, "Added to cache: %s : %s\n", host, netid);
22874462Salfred#endif
22974462Salfred
23074462Salfred/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  cptr */
23174462Salfred
23274462Salfred	rwlock_wrlock(&rpcbaddr_cache_lock);
23374462Salfred	if (cachesize < CACHESIZE) {
23474462Salfred		ad_cache->ac_next = front;
23574462Salfred		front = ad_cache;
23674462Salfred		cachesize++;
23774462Salfred	} else {
23874462Salfred		/* Free the last entry */
23974462Salfred		cptr = front;
24074462Salfred		prevptr = NULL;
24174462Salfred		while (cptr->ac_next) {
24274462Salfred			prevptr = cptr;
24374462Salfred			cptr = cptr->ac_next;
24474462Salfred		}
24574462Salfred
24674462Salfred#ifdef ND_DEBUG
24774462Salfred		fprintf(stderr, "Deleted from cache: %s : %s\n",
24874462Salfred			cptr->ac_host, cptr->ac_netid);
24974462Salfred#endif
25074462Salfred		free(cptr->ac_host);
25174462Salfred		free(cptr->ac_netid);
25274462Salfred		free(cptr->ac_taddr->buf);
25374462Salfred		free(cptr->ac_taddr);
254290899Sngie		free(cptr->ac_uaddr);
25574462Salfred
25674462Salfred		if (prevptr) {
25774462Salfred			prevptr->ac_next = NULL;
25874462Salfred			ad_cache->ac_next = front;
25974462Salfred			front = ad_cache;
26074462Salfred		} else {
26174462Salfred			front = ad_cache;
26274462Salfred			ad_cache->ac_next = NULL;
26374462Salfred		}
26474462Salfred		free(cptr);
26574462Salfred	}
26674462Salfred	rwlock_unlock(&rpcbaddr_cache_lock);
26774462Salfred}
26874462Salfred
26974462Salfred/*
27074462Salfred * This routine will return a client handle that is connected to the
27181069Siedowse * rpcbind. If targaddr is non-NULL, the "universal address" of the
27281069Siedowse * host will be stored in *targaddr; the caller is responsible for
27381069Siedowse * freeing this string.
27481069Siedowse * On error, returns NULL and free's everything.
27574462Salfred */
27674462Salfredstatic CLIENT *
277309489Sngiegetclnthandle(const char *host, const struct netconfig *nconf, char **targaddr)
27874462Salfred{
27974462Salfred	CLIENT *client;
28074462Salfred	struct netbuf *addr, taddr;
28174462Salfred	struct netbuf addr_to_delete;
28274462Salfred	struct __rpc_sockinfo si;
28374462Salfred	struct addrinfo hints, *res, *tres;
28474462Salfred	struct address_cache *ad_cache;
28574462Salfred	char *tmpaddr;
28674462Salfred
28774462Salfred/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  ad_cache */
28874462Salfred
28974462Salfred	/* Get the address of the rpcbind.  Check cache first */
29079726Siedowse	client = NULL;
29174462Salfred	addr_to_delete.len = 0;
29274462Salfred	rwlock_rdlock(&rpcbaddr_cache_lock);
29390269Salfred	ad_cache = NULL;
29490269Salfred	if (host != NULL)
29590269Salfred		ad_cache = check_cache(host, nconf->nc_netid);
29674462Salfred	if (ad_cache != NULL) {
29774462Salfred		addr = ad_cache->ac_taddr;
29874462Salfred		client = clnt_tli_create(RPC_ANYFD, nconf, addr,
29974462Salfred		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
30074462Salfred		if (client != NULL) {
30174462Salfred			if (targaddr)
30281069Siedowse				*targaddr = strdup(ad_cache->ac_uaddr);
30374462Salfred			rwlock_unlock(&rpcbaddr_cache_lock);
30474462Salfred			return (client);
30574462Salfred		}
30674462Salfred		addr_to_delete.len = addr->len;
30774462Salfred		addr_to_delete.buf = (char *)malloc(addr->len);
30874462Salfred		if (addr_to_delete.buf == NULL) {
30974462Salfred			addr_to_delete.len = 0;
31074462Salfred		} else {
31174462Salfred			memcpy(addr_to_delete.buf, addr->buf, addr->len);
31274462Salfred		}
31374462Salfred	}
31474462Salfred	rwlock_unlock(&rpcbaddr_cache_lock);
31574462Salfred	if (addr_to_delete.len != 0) {
31674462Salfred		/*
31774462Salfred		 * Assume this may be due to cache data being
31874462Salfred		 *  outdated
31974462Salfred		 */
32074462Salfred		rwlock_wrlock(&rpcbaddr_cache_lock);
32174462Salfred		delete_cache(&addr_to_delete);
32274462Salfred		rwlock_unlock(&rpcbaddr_cache_lock);
32374462Salfred		free(addr_to_delete.buf);
32474462Salfred	}
32574462Salfred	if (!__rpc_nconf2sockinfo(nconf, &si)) {
32674462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
32774462Salfred		return NULL;
32874462Salfred	}
32974462Salfred
33074462Salfred	memset(&hints, 0, sizeof hints);
33174462Salfred	hints.ai_family = si.si_af;
33274462Salfred	hints.ai_socktype = si.si_socktype;
33374462Salfred	hints.ai_protocol = si.si_proto;
33474462Salfred
33574462Salfred#ifdef CLNT_DEBUG
33674462Salfred	printf("trying netid %s family %d proto %d socktype %d\n",
33774462Salfred	    nconf->nc_netid, si.si_af, si.si_proto, si.si_socktype);
33874462Salfred#endif
33974462Salfred
34090269Salfred	if (nconf->nc_protofmly != NULL && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
34190269Salfred		client = local_rpcb();
34290269Salfred		if (! client) {
34390269Salfred#ifdef ND_DEBUG
34490269Salfred			clnt_pcreateerror("rpcbind clnt interface");
34590269Salfred#endif
34690269Salfred			return (NULL);
34790269Salfred		} else {
34890269Salfred			struct sockaddr_un sun;
349172259Smatteo			if (targaddr) {
350172259Smatteo			    *targaddr = malloc(sizeof(sun.sun_path));
351172259Smatteo			    if (*targaddr == NULL) {
352172259Smatteo				CLNT_DESTROY(client);
353172259Smatteo				return (NULL);
354172259Smatteo			    }
355172259Smatteo			    strncpy(*targaddr, _PATH_RPCBINDSOCK,
356172259Smatteo				sizeof(sun.sun_path));
357172259Smatteo			}
35890269Salfred			return (client);
35990269Salfred		}
36090269Salfred	} else {
36190269Salfred		if (getaddrinfo(host, "sunrpc", &hints, &res) != 0) {
36290269Salfred			rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
36390269Salfred			return NULL;
36490269Salfred		}
36574462Salfred	}
36674462Salfred
36774462Salfred	for (tres = res; tres != NULL; tres = tres->ai_next) {
36874462Salfred		taddr.buf = tres->ai_addr;
36974462Salfred		taddr.len = taddr.maxlen = tres->ai_addrlen;
37074462Salfred
37174462Salfred#ifdef ND_DEBUG
37274462Salfred		{
37374462Salfred			char *ua;
37474462Salfred
37574462Salfred			ua = taddr2uaddr(nconf, &taddr);
37674462Salfred			fprintf(stderr, "Got it [%s]\n", ua);
37774462Salfred			free(ua);
37874462Salfred		}
37974462Salfred#endif
38074462Salfred
38174462Salfred#ifdef ND_DEBUG
38274462Salfred		{
38374462Salfred			int i;
38474462Salfred
38574462Salfred			fprintf(stderr, "\tnetbuf len = %d, maxlen = %d\n",
38674462Salfred				taddr.len, taddr.maxlen);
38774462Salfred			fprintf(stderr, "\tAddress is ");
38874462Salfred			for (i = 0; i < taddr.len; i++)
38974462Salfred				fprintf(stderr, "%u.", ((char *)(taddr.buf))[i]);
39074462Salfred			fprintf(stderr, "\n");
39174462Salfred		}
39274462Salfred#endif
39374462Salfred		client = clnt_tli_create(RPC_ANYFD, nconf, &taddr,
39474462Salfred		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
39574462Salfred#ifdef ND_DEBUG
39674462Salfred		if (! client) {
39774462Salfred			clnt_pcreateerror("rpcbind clnt interface");
39874462Salfred		}
39974462Salfred#endif
40074462Salfred
40174462Salfred		if (client) {
40274462Salfred			tmpaddr = targaddr ? taddr2uaddr(nconf, &taddr) : NULL;
40374462Salfred			add_cache(host, nconf->nc_netid, &taddr, tmpaddr);
40474462Salfred			if (targaddr)
40574462Salfred				*targaddr = tmpaddr;
40674462Salfred			break;
40774462Salfred		}
40874462Salfred	}
40990269Salfred	if (res)
41090269Salfred		freeaddrinfo(res);
41174462Salfred	return (client);
41274462Salfred}
41374462Salfred
41474462Salfred/* XXX */
41574462Salfred#define IN4_LOCALHOST_STRING	"127.0.0.1"
41674462Salfred#define IN6_LOCALHOST_STRING	"::1"
41774462Salfred
41874462Salfred/*
41974462Salfred * This routine will return a client handle that is connected to the local
42074462Salfred * rpcbind. Returns NULL on error and free's everything.
42174462Salfred */
42274462Salfredstatic CLIENT *
423309487Sngielocal_rpcb(void)
42474462Salfred{
42574462Salfred	CLIENT *client;
42674462Salfred	static struct netconfig *loopnconf;
42774462Salfred	static char *hostname;
42874462Salfred	int sock;
42974462Salfred	size_t tsize;
43074462Salfred	struct netbuf nbuf;
43174462Salfred	struct sockaddr_un sun;
43274462Salfred
43374462Salfred	/*
43474462Salfred	 * Try connecting to the local rpcbind through a local socket
43574462Salfred	 * first. If this doesn't work, try all transports defined in
43674462Salfred	 * the netconfig file.
43774462Salfred	 */
43874462Salfred	memset(&sun, 0, sizeof sun);
43974462Salfred	sock = _socket(AF_LOCAL, SOCK_STREAM, 0);
44074462Salfred	if (sock < 0)
44174462Salfred		goto try_nconf;
44274462Salfred	sun.sun_family = AF_LOCAL;
44374462Salfred	strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
44474462Salfred	nbuf.len = sun.sun_len = SUN_LEN(&sun);
44574462Salfred	nbuf.maxlen = sizeof (struct sockaddr_un);
44674462Salfred	nbuf.buf = &sun;
44774462Salfred
44874462Salfred	tsize = __rpc_get_t_size(AF_LOCAL, 0, 0);
44974462Salfred	client = clnt_vc_create(sock, &nbuf, (rpcprog_t)RPCBPROG,
45074462Salfred	    (rpcvers_t)RPCBVERS, tsize, tsize);
45174462Salfred
45290735Siedowse	if (client != NULL) {
45390735Siedowse		/* Mark the socket to be closed in destructor */
45490735Siedowse		(void) CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
45574462Salfred		return client;
45690735Siedowse	}
45774462Salfred
45890735Siedowse	/* Nobody needs this socket anymore; free the descriptor. */
45990735Siedowse	_close(sock);
46090735Siedowse
46174462Salfredtry_nconf:
46274462Salfred
46374462Salfred/* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */
46474462Salfred	mutex_lock(&loopnconf_lock);
46574462Salfred	if (loopnconf == NULL) {
46674462Salfred		struct netconfig *nconf, *tmpnconf = NULL;
46774462Salfred		void *nc_handle;
46874462Salfred		int fd;
46974462Salfred
47074462Salfred		nc_handle = setnetconfig();
47174462Salfred		if (nc_handle == NULL) {
47274462Salfred			/* fails to open netconfig file */
47374462Salfred			syslog (LOG_ERR, "rpc: failed to open " NETCONFIG);
47474462Salfred			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
47574462Salfred			mutex_unlock(&loopnconf_lock);
47674462Salfred			return (NULL);
47774462Salfred		}
47874462Salfred		while ((nconf = getnetconfig(nc_handle)) != NULL) {
47974462Salfred#ifdef INET6
48074462Salfred			if ((strcmp(nconf->nc_protofmly, NC_INET6) == 0 ||
48174462Salfred#else
48274462Salfred			if ((
48374462Salfred#endif
48474462Salfred			     strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
48574462Salfred			    (nconf->nc_semantics == NC_TPI_COTS ||
48674462Salfred			     nconf->nc_semantics == NC_TPI_COTS_ORD)) {
48774462Salfred				fd = __rpc_nconf2fd(nconf);
48874462Salfred				/*
48974462Salfred				 * Can't create a socket, assume that
49074462Salfred				 * this family isn't configured in the kernel.
49174462Salfred				 */
49274462Salfred				if (fd < 0)
49374462Salfred					continue;
49474462Salfred				_close(fd);
49574462Salfred				tmpnconf = nconf;
49674462Salfred				if (!strcmp(nconf->nc_protofmly, NC_INET))
49774462Salfred					hostname = IN4_LOCALHOST_STRING;
49874462Salfred				else
49974462Salfred					hostname = IN6_LOCALHOST_STRING;
50074462Salfred			}
50174462Salfred		}
50274462Salfred		if (tmpnconf == NULL) {
503320325Sdelphij			endnetconfig(nc_handle);
50474462Salfred			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
50574462Salfred			mutex_unlock(&loopnconf_lock);
50674462Salfred			return (NULL);
50774462Salfred		}
50874462Salfred		loopnconf = getnetconfigent(tmpnconf->nc_netid);
50974462Salfred		/* loopnconf is never freed */
510320325Sdelphij		endnetconfig(nc_handle);
51174462Salfred	}
51274462Salfred	mutex_unlock(&loopnconf_lock);
51374462Salfred	client = getclnthandle(hostname, loopnconf, NULL);
51474462Salfred	return (client);
51574462Salfred}
51674462Salfred
51774462Salfred/*
51874462Salfred * Set a mapping between program, version and address.
51974462Salfred * Calls the rpcbind service to do the mapping.
520309489Sngie *
521309489Sngie * nconf   - Network structure of transport
522309489Sngie * address - Services netconfig address
52374462Salfred */
52474462Salfredbool_t
525309489Sngierpcb_set(rpcprog_t program, rpcvers_t version, const struct netconfig *nconf,
526309489Sngie    const struct netbuf *address)
52774462Salfred{
52874462Salfred	CLIENT *client;
52974462Salfred	bool_t rslt = FALSE;
53074462Salfred	RPCB parms;
53174462Salfred	char uidbuf[32];
53274462Salfred
53374462Salfred	/* parameter checking */
53474462Salfred	if (nconf == NULL) {
53574462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
53674462Salfred		return (FALSE);
53774462Salfred	}
53874462Salfred	if (address == NULL) {
53974462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
54074462Salfred		return (FALSE);
54174462Salfred	}
54274462Salfred	client = local_rpcb();
54374462Salfred	if (! client) {
54474462Salfred		return (FALSE);
54574462Salfred	}
54674462Salfred
54774462Salfred	/* convert to universal */
54874462Salfred	/*LINTED const castaway*/
54974462Salfred	parms.r_addr = taddr2uaddr((struct netconfig *) nconf,
55074462Salfred				   (struct netbuf *)address);
55174462Salfred	if (!parms.r_addr) {
55290735Siedowse		CLNT_DESTROY(client);
55374462Salfred		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
55474462Salfred		return (FALSE); /* no universal address */
55574462Salfred	}
55674462Salfred	parms.r_prog = program;
55774462Salfred	parms.r_vers = version;
55874462Salfred	parms.r_netid = nconf->nc_netid;
55974462Salfred	/*
56074462Salfred	 * Though uid is not being used directly, we still send it for
56174462Salfred	 * completeness.  For non-unix platforms, perhaps some other
56274462Salfred	 * string or an empty string can be sent.
56374462Salfred	 */
56474462Salfred	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
56574462Salfred	parms.r_owner = uidbuf;
56674462Salfred
56774462Salfred	CLNT_CALL(client, (rpcproc_t)RPCBPROC_SET, (xdrproc_t) xdr_rpcb,
56874462Salfred	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
56974462Salfred	    (char *)(void *)&rslt, tottimeout);
57074462Salfred
57174462Salfred	CLNT_DESTROY(client);
57274462Salfred	free(parms.r_addr);
57374462Salfred	return (rslt);
57474462Salfred}
57574462Salfred
57674462Salfred/*
57774462Salfred * Remove the mapping between program, version and netbuf address.
57874462Salfred * Calls the rpcbind service to do the un-mapping.
57974462Salfred * If netbuf is NULL, unset for all the transports, otherwise unset
58074462Salfred * only for the given transport.
58174462Salfred */
58274462Salfredbool_t
583309489Sngierpcb_unset(rpcprog_t program, rpcvers_t version, const struct netconfig *nconf)
58474462Salfred{
58574462Salfred	CLIENT *client;
58674462Salfred	bool_t rslt = FALSE;
58774462Salfred	RPCB parms;
58874462Salfred	char uidbuf[32];
58974462Salfred
59074462Salfred	client = local_rpcb();
59174462Salfred	if (! client) {
59274462Salfred		return (FALSE);
59374462Salfred	}
59474462Salfred
59574462Salfred	parms.r_prog = program;
59674462Salfred	parms.r_vers = version;
59774462Salfred	if (nconf)
59874462Salfred		parms.r_netid = nconf->nc_netid;
59974462Salfred	else {
60074462Salfred		/*LINTED const castaway*/
60174462Salfred		parms.r_netid = (char *) &nullstring[0]; /* unsets  all */
60274462Salfred	}
60374462Salfred	/*LINTED const castaway*/
60474462Salfred	parms.r_addr = (char *) &nullstring[0];
60574462Salfred	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
60674462Salfred	parms.r_owner = uidbuf;
60774462Salfred
60874462Salfred	CLNT_CALL(client, (rpcproc_t)RPCBPROC_UNSET, (xdrproc_t) xdr_rpcb,
60974462Salfred	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
61074462Salfred	    (char *)(void *)&rslt, tottimeout);
61174462Salfred
61274462Salfred	CLNT_DESTROY(client);
61374462Salfred	return (rslt);
61474462Salfred}
61574462Salfred
61674462Salfred/*
61774462Salfred * From the merged list, find the appropriate entry
61874462Salfred */
61974462Salfredstatic struct netbuf *
620309489Sngiegot_entry(rpcb_entry_list_ptr relp, const struct netconfig *nconf)
62174462Salfred{
62274462Salfred	struct netbuf *na = NULL;
62374462Salfred	rpcb_entry_list_ptr sp;
62474462Salfred	rpcb_entry *rmap;
62574462Salfred
62674462Salfred	for (sp = relp; sp != NULL; sp = sp->rpcb_entry_next) {
62774462Salfred		rmap = &sp->rpcb_entry_map;
62874462Salfred		if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) &&
62974462Salfred		    (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) &&
63074462Salfred		    (nconf->nc_semantics == rmap->r_nc_semantics) &&
631121653Smbr		    (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != 0)) {
63274462Salfred			na = uaddr2taddr(nconf, rmap->r_maddr);
63374462Salfred#ifdef ND_DEBUG
63474462Salfred			fprintf(stderr, "\tRemote address is [%s].\n",
63574462Salfred				rmap->r_maddr);
63674462Salfred			if (!na)
63774462Salfred				fprintf(stderr,
63874462Salfred				    "\tCouldn't resolve remote address!\n");
63974462Salfred#endif
64074462Salfred			break;
64174462Salfred		}
64274462Salfred	}
64374462Salfred	return (na);
64474462Salfred}
64574462Salfred
64674462Salfred/*
64799775Salfred * Quick check to see if rpcbind is up.  Tries to connect over
64899775Salfred * local transport.
64999775Salfred */
650156090Sdeischenstatic bool_t
651309484Sngie__rpcbind_is_up(void)
65299775Salfred{
65399775Salfred	struct netconfig *nconf;
65499775Salfred	struct sockaddr_un sun;
65599775Salfred	void *localhandle;
65699775Salfred	int sock;
65799775Salfred
65899775Salfred	nconf = NULL;
65999775Salfred	localhandle = setnetconfig();
660100701Siedowse	while ((nconf = getnetconfig(localhandle)) != NULL) {
66199775Salfred		if (nconf->nc_protofmly != NULL &&
66299775Salfred		    strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)
66399775Salfred			 break;
66499775Salfred	}
665294240Sngie	endnetconfig(localhandle);
666294240Sngie
66799775Salfred	if (nconf == NULL)
66899775Salfred		return (FALSE);
66999775Salfred
67099775Salfred	memset(&sun, 0, sizeof sun);
67199775Salfred	sock = _socket(AF_LOCAL, SOCK_STREAM, 0);
67299775Salfred	if (sock < 0)
67399775Salfred		return (FALSE);
67499775Salfred	sun.sun_family = AF_LOCAL;
67599775Salfred	strncpy(sun.sun_path, _PATH_RPCBINDSOCK, sizeof(sun.sun_path));
67699775Salfred	sun.sun_len = SUN_LEN(&sun);
67799775Salfred
67899775Salfred	if (_connect(sock, (struct sockaddr *)&sun, sun.sun_len) < 0) {
67999775Salfred		_close(sock);
68099775Salfred		return (FALSE);
68199775Salfred	}
68299775Salfred
68399775Salfred	_close(sock);
68499775Salfred	return (TRUE);
68599775Salfred}
68699775Salfred
68799775Salfred/*
68874462Salfred * An internal function which optimizes rpcb_getaddr function.  It also
68974462Salfred * returns the client handle that it uses to contact the remote rpcbind.
69074462Salfred *
69174462Salfred * The algorithm used: If the transports is TCP or UDP, it first tries
69274462Salfred * version 2 (portmap), 4 and then 3 (svr4).  This order should be
69374462Salfred * changed in the next OS release to 4, 2 and 3.  We are assuming that by
69474462Salfred * that time, version 4 would be available on many machines on the network.
69574462Salfred * With this algorithm, we get performance as well as a plan for
69674462Salfred * obsoleting version 2.
69774462Salfred *
69874462Salfred * For all other transports, the algorithm remains as 4 and then 3.
69974462Salfred *
70074462Salfred * XXX: Due to some problems with t_connect(), we do not reuse the same client
70174462Salfred * handle for COTS cases and hence in these cases we do not return the
70274462Salfred * client handle.  This code will change if t_connect() ever
70374462Salfred * starts working properly.  Also look under clnt_vc.c.
70474462Salfred */
70574462Salfredstruct netbuf *
706309489Sngie__rpcb_findaddr_timed(rpcprog_t program, rpcvers_t version,
707309489Sngie    const struct netconfig *nconf, const char *host,
708309489Sngie    CLIENT **clpp, struct timeval *tp)
70974462Salfred{
71099775Salfred	static bool_t check_rpcbind = TRUE;
71174462Salfred	CLIENT *client = NULL;
71274462Salfred	RPCB parms;
71374462Salfred	enum clnt_stat clnt_st;
71474462Salfred	char *ua = NULL;
71574462Salfred	rpcvers_t vers;
71674462Salfred	struct netbuf *address = NULL;
71774462Salfred	rpcvers_t start_vers = RPCBVERS4;
71874462Salfred	struct netbuf servaddr;
71974462Salfred
72074462Salfred	/* parameter checking */
72174462Salfred	if (nconf == NULL) {
72274462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
72374462Salfred		return (NULL);
72474462Salfred	}
72574462Salfred
72674462Salfred	parms.r_addr = NULL;
72774462Salfred
72899775Salfred	/*
72999775Salfred	 * Use default total timeout if no timeout is specified.
73099775Salfred	 */
73199775Salfred	if (tp == NULL)
73299775Salfred		tp = &tottimeout;
73399775Salfred
73474462Salfred#ifdef PORTMAP
73574462Salfred	/* Try version 2 for TCP or UDP */
73674462Salfred	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
73774462Salfred		u_short port = 0;
73874462Salfred		struct netbuf remote;
73974462Salfred		rpcvers_t pmapvers = 2;
74074462Salfred		struct pmap pmapparms;
74174462Salfred
74274462Salfred		/*
74374462Salfred		 * Try UDP only - there are some portmappers out
74474462Salfred		 * there that use UDP only.
74574462Salfred		 */
74674462Salfred		if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
74774462Salfred			struct netconfig *newnconf;
74874462Salfred
749100701Siedowse			if ((newnconf = getnetconfigent("udp")) == NULL) {
75074462Salfred				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
75174462Salfred				return (NULL);
75274462Salfred			}
75374462Salfred			client = getclnthandle(host, newnconf, &parms.r_addr);
754100701Siedowse			freenetconfigent(newnconf);
75574462Salfred		} else {
75674462Salfred			client = getclnthandle(host, nconf, &parms.r_addr);
75774462Salfred		}
75899775Salfred		if (client == NULL)
75974462Salfred			return (NULL);
76074462Salfred
76199775Salfred		/*
76299775Salfred		 * Set version and retry timeout.
76399775Salfred		 */
76499775Salfred		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
76599775Salfred		CLNT_CONTROL(client, CLSET_VERS, (char *)&pmapvers);
76699775Salfred
76774462Salfred		pmapparms.pm_prog = program;
76874462Salfred		pmapparms.pm_vers = version;
76974462Salfred		pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ?
77074462Salfred					IPPROTO_UDP : IPPROTO_TCP;
77174462Salfred		pmapparms.pm_port = 0;	/* not needed */
77274462Salfred		clnt_st = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
77374462Salfred		    (xdrproc_t) xdr_pmap, (caddr_t)(void *)&pmapparms,
77474462Salfred		    (xdrproc_t) xdr_u_short, (caddr_t)(void *)&port,
77599775Salfred		    *tp);
77674462Salfred		if (clnt_st != RPC_SUCCESS) {
77774462Salfred			if ((clnt_st == RPC_PROGVERSMISMATCH) ||
77874462Salfred				(clnt_st == RPC_PROGUNAVAIL))
77974462Salfred				goto try_rpcbind; /* Try different versions */
78074462Salfred			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
78174462Salfred			clnt_geterr(client, &rpc_createerr.cf_error);
78274462Salfred			goto error;
78374462Salfred		} else if (port == 0) {
78474462Salfred			address = NULL;
78574462Salfred			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
78674462Salfred			goto error;
78774462Salfred		}
78874462Salfred		port = htons(port);
78999775Salfred		CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)&remote);
79074462Salfred		if (((address = (struct netbuf *)
79174462Salfred			malloc(sizeof (struct netbuf))) == NULL) ||
79274462Salfred		    ((address->buf = (char *)
79374462Salfred			malloc(remote.len)) == NULL)) {
79474462Salfred			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
79574462Salfred			clnt_geterr(client, &rpc_createerr.cf_error);
796290899Sngie			free(address);
797290899Sngie			address = NULL;
79874462Salfred			goto error;
79974462Salfred		}
80074462Salfred		memcpy(address->buf, remote.buf, remote.len);
80174462Salfred		memcpy(&((char *)address->buf)[sizeof (short)],
80274462Salfred				(char *)(void *)&port, sizeof (short));
80374462Salfred		address->len = address->maxlen = remote.len;
80474462Salfred		goto done;
80574462Salfred	}
80674462Salfred#endif				/* PORTMAP */
80774462Salfred
80874462Salfredtry_rpcbind:
80974462Salfred	/*
81099775Salfred	 * Check if rpcbind is up.  This prevents needless delays when
81199775Salfred	 * accessing applications such as the keyserver while booting
81299775Salfred	 * disklessly.
81399775Salfred	 */
81499775Salfred	if (check_rpcbind && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
81599775Salfred		if (!__rpcbind_is_up()) {
81699775Salfred			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
81799775Salfred			rpc_createerr.cf_error.re_errno = 0;
81899775Salfred			goto error;
81999775Salfred		}
82099775Salfred		check_rpcbind = FALSE;
82199775Salfred	}
82299775Salfred
82399775Salfred	/*
82474462Salfred	 * Now we try version 4 and then 3.
82574462Salfred	 * We also send the remote system the address we used to
82674462Salfred	 * contact it in case it can help to connect back with us
82774462Salfred	 */
82874462Salfred	parms.r_prog = program;
82974462Salfred	parms.r_vers = version;
83074462Salfred	/*LINTED const castaway*/
83174462Salfred	parms.r_owner = (char *) &nullstring[0];	/* not needed; */
83274462Salfred							/* just for xdring */
83374462Salfred	parms.r_netid = nconf->nc_netid; /* not really needed */
83474462Salfred
83574462Salfred	/*
83674462Salfred	 * If a COTS transport is being used, try getting address via CLTS
83774462Salfred	 * transport.  This works only with version 4.
83874462Salfred	 */
83999775Salfred	if (nconf->nc_semantics == NC_TPI_COTS_ORD ||
84099775Salfred			nconf->nc_semantics == NC_TPI_COTS) {
84199775Salfred
84274462Salfred		void *handle;
84374462Salfred		struct netconfig *nconf_clts;
84474462Salfred		rpcb_entry_list_ptr relp = NULL;
84574462Salfred
84674462Salfred		if (client == NULL) {
84774462Salfred			/* This did not go through the above PORTMAP/TCP code */
84874462Salfred			if ((handle = __rpc_setconf("datagram_v")) != NULL) {
84974462Salfred				while ((nconf_clts = __rpc_getconf(handle))
85074462Salfred					!= NULL) {
85174462Salfred					if (strcmp(nconf_clts->nc_protofmly,
85274462Salfred						nconf->nc_protofmly) != 0) {
85374462Salfred						continue;
85474462Salfred					}
85574462Salfred					client = getclnthandle(host, nconf_clts,
85674462Salfred							&parms.r_addr);
85774462Salfred					break;
85874462Salfred				}
85974462Salfred				__rpc_endconf(handle);
86074462Salfred			}
86174462Salfred			if (client == NULL)
86274462Salfred				goto regular_rpcbind;	/* Go the regular way */
86374462Salfred		} else {
86474462Salfred			/* This is a UDP PORTMAP handle.  Change to version 4 */
86574462Salfred			vers = RPCBVERS4;
86674462Salfred			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
86774462Salfred		}
86874462Salfred		/*
86974462Salfred		 * We also send the remote system the address we used to
87074462Salfred		 * contact it in case it can help it connect back with us
87174462Salfred		 */
87274462Salfred		if (parms.r_addr == NULL) {
87374462Salfred			/*LINTED const castaway*/
87474462Salfred			parms.r_addr = (char *) &nullstring[0]; /* for XDRing */
87574462Salfred		}
87699775Salfred
87799775Salfred		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
87899775Salfred
87974462Salfred		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDRLIST,
88074462Salfred		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
88174462Salfred		    (xdrproc_t) xdr_rpcb_entry_list_ptr,
88299775Salfred		    (char *)(void *)&relp, *tp);
88374462Salfred		if (clnt_st == RPC_SUCCESS) {
88474462Salfred			if ((address = got_entry(relp, nconf)) != NULL) {
88574462Salfred				xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
88674462Salfred				    (char *)(void *)&relp);
887109952Smbr				CLNT_CONTROL(client, CLGET_SVC_ADDR,
888109952Smbr					(char *)(void *)&servaddr);
889109952Smbr				__rpc_fixup_addr(address, &servaddr);
89074462Salfred				goto done;
89174462Salfred			}
89274462Salfred			/* Entry not found for this transport */
89374462Salfred			xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
89474462Salfred			    (char *)(void *)&relp);
89574462Salfred			/*
89674462Salfred			 * XXX: should have perhaps returned with error but
89774462Salfred			 * since the remote machine might not always be able
89874462Salfred			 * to send the address on all transports, we try the
89974462Salfred			 * regular way with regular_rpcbind
90074462Salfred			 */
90174462Salfred			goto regular_rpcbind;
90274462Salfred		} else if ((clnt_st == RPC_PROGVERSMISMATCH) ||
90374462Salfred			(clnt_st == RPC_PROGUNAVAIL)) {
90474462Salfred			start_vers = RPCBVERS;	/* Try version 3 now */
90574462Salfred			goto regular_rpcbind; /* Try different versions */
90674462Salfred		} else {
90774462Salfred			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
90874462Salfred			clnt_geterr(client, &rpc_createerr.cf_error);
90974462Salfred			goto error;
91074462Salfred		}
91174462Salfred	}
91274462Salfred
91374462Salfredregular_rpcbind:
91474462Salfred
91574462Salfred	/* Now the same transport is to be used to get the address */
91674462Salfred	if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
91774462Salfred			(nconf->nc_semantics == NC_TPI_COTS))) {
91874462Salfred		/* A CLTS type of client - destroy it */
91974462Salfred		CLNT_DESTROY(client);
92074462Salfred		client = NULL;
92174462Salfred	}
92274462Salfred
92374462Salfred	if (client == NULL) {
92474462Salfred		client = getclnthandle(host, nconf, &parms.r_addr);
92574462Salfred		if (client == NULL) {
92674462Salfred			goto error;
92774462Salfred		}
92874462Salfred	}
92974462Salfred	if (parms.r_addr == NULL) {
93074462Salfred		/*LINTED const castaway*/
93174462Salfred		parms.r_addr = (char *) &nullstring[0];
93274462Salfred	}
93374462Salfred
93474462Salfred	/* First try from start_vers and then version 3 (RPCBVERS) */
93599775Salfred
93699775Salfred	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *) &rpcbrmttime);
93774462Salfred	for (vers = start_vers;  vers >= RPCBVERS; vers--) {
93874462Salfred		/* Set the version */
93974462Salfred		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
94074462Salfred		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
94174462Salfred		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
94299775Salfred		    (xdrproc_t) xdr_wrapstring, (char *)(void *) &ua, *tp);
94374462Salfred		if (clnt_st == RPC_SUCCESS) {
944121653Smbr			if ((ua == NULL) || (ua[0] == 0)) {
94574462Salfred				/* address unknown */
94674462Salfred				rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
94774462Salfred				goto error;
94874462Salfred			}
94974462Salfred			address = uaddr2taddr(nconf, ua);
95074462Salfred#ifdef ND_DEBUG
95174462Salfred			fprintf(stderr, "\tRemote address is [%s]\n", ua);
95274462Salfred			if (!address)
95374462Salfred				fprintf(stderr,
95474462Salfred					"\tCouldn't resolve remote address!\n");
95574462Salfred#endif
95674462Salfred			xdr_free((xdrproc_t)xdr_wrapstring,
95774462Salfred			    (char *)(void *)&ua);
95874462Salfred
95974462Salfred			if (! address) {
96074462Salfred				/* We don't know about your universal address */
96174462Salfred				rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
96274462Salfred				goto error;
96374462Salfred			}
96474462Salfred			CLNT_CONTROL(client, CLGET_SVC_ADDR,
96574462Salfred			    (char *)(void *)&servaddr);
96674462Salfred			__rpc_fixup_addr(address, &servaddr);
96774462Salfred			goto done;
96874462Salfred		} else if (clnt_st == RPC_PROGVERSMISMATCH) {
96974462Salfred			struct rpc_err rpcerr;
97074462Salfred
97174462Salfred			clnt_geterr(client, &rpcerr);
97274462Salfred			if (rpcerr.re_vers.low > RPCBVERS4)
97374462Salfred				goto error;  /* a new version, can't handle */
97474462Salfred		} else if (clnt_st != RPC_PROGUNAVAIL) {
97574462Salfred			/* Cant handle this error */
97674462Salfred			rpc_createerr.cf_stat = clnt_st;
97774462Salfred			clnt_geterr(client, &rpc_createerr.cf_error);
97874462Salfred			goto error;
97974462Salfred		}
98074462Salfred	}
98174462Salfred
98274462Salfrederror:
98374462Salfred	if (client) {
98474462Salfred		CLNT_DESTROY(client);
98574462Salfred		client = NULL;
98674462Salfred	}
98774462Salfreddone:
98874462Salfred	if (nconf->nc_semantics != NC_TPI_CLTS) {
98974462Salfred		/* This client is the connectionless one */
99074462Salfred		if (client) {
99174462Salfred			CLNT_DESTROY(client);
99274462Salfred			client = NULL;
99374462Salfred		}
99474462Salfred	}
99574462Salfred	if (clpp) {
99674462Salfred		*clpp = client;
99774462Salfred	} else if (client) {
99874462Salfred		CLNT_DESTROY(client);
99974462Salfred	}
100079726Siedowse	if (parms.r_addr != NULL && parms.r_addr != nullstring)
100179726Siedowse		free(parms.r_addr);
100274462Salfred	return (address);
100374462Salfred}
100474462Salfred
100574462Salfred
100674462Salfred/*
100774462Salfred * Find the mapped address for program, version.
100874462Salfred * Calls the rpcbind service remotely to do the lookup.
100974462Salfred * Uses the transport specified in nconf.
101074462Salfred * Returns FALSE (0) if no map exists, else returns 1.
101174462Salfred *
101274462Salfred * Assuming that the address is all properly allocated
101374462Salfred */
1014309501Sngiebool_t
1015309489Sngierpcb_getaddr(rpcprog_t program, rpcvers_t version, const struct netconfig *nconf,
1016309489Sngie    struct netbuf *address, const char *host)
101774462Salfred{
101874462Salfred	struct netbuf *na;
101974462Salfred
102099775Salfred	if ((na = __rpcb_findaddr_timed(program, version,
102199775Salfred	    (struct netconfig *) nconf, (char *) host,
102299775Salfred	    (CLIENT **) NULL, (struct timeval *) NULL)) == NULL)
102374462Salfred		return (FALSE);
102474462Salfred
102574462Salfred	if (na->len > address->maxlen) {
102674462Salfred		/* Too long address */
102774462Salfred		free(na->buf);
102874462Salfred		free(na);
102974462Salfred		rpc_createerr.cf_stat = RPC_FAILED;
103074462Salfred		return (FALSE);
103174462Salfred	}
103274462Salfred	memcpy(address->buf, na->buf, (size_t)na->len);
103374462Salfred	address->len = na->len;
103474462Salfred	free(na->buf);
103574462Salfred	free(na);
103674462Salfred	return (TRUE);
103774462Salfred}
103874462Salfred
103974462Salfred/*
104074462Salfred * Get a copy of the current maps.
104174462Salfred * Calls the rpcbind service remotely to get the maps.
104274462Salfred *
104374462Salfred * It returns only a list of the services
104474462Salfred * It returns NULL on failure.
104574462Salfred */
104674462Salfredrpcblist *
1047309489Sngierpcb_getmaps(const struct netconfig *nconf, const char *host)
104874462Salfred{
104974462Salfred	rpcblist_ptr head = NULL;
105074462Salfred	CLIENT *client;
105174462Salfred	enum clnt_stat clnt_st;
105274462Salfred	rpcvers_t vers = 0;
105374462Salfred
105474462Salfred	client = getclnthandle(host, nconf, NULL);
105574462Salfred	if (client == NULL) {
105674462Salfred		return (head);
105774462Salfred	}
105874462Salfred	clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
105974462Salfred	    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
106074462Salfred	    (char *)(void *)&head, tottimeout);
106174462Salfred	if (clnt_st == RPC_SUCCESS)
106274462Salfred		goto done;
106374462Salfred
106474462Salfred	if ((clnt_st != RPC_PROGVERSMISMATCH) &&
106574462Salfred	    (clnt_st != RPC_PROGUNAVAIL)) {
106674462Salfred		rpc_createerr.cf_stat = RPC_RPCBFAILURE;
106774462Salfred		clnt_geterr(client, &rpc_createerr.cf_error);
106874462Salfred		goto done;
106974462Salfred	}
107074462Salfred
107174462Salfred	/* fall back to earlier version */
107274462Salfred	CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
107374462Salfred	if (vers == RPCBVERS4) {
107474462Salfred		vers = RPCBVERS;
107574462Salfred		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
107674462Salfred		if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
107774462Salfred		    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
107874462Salfred		    (char *)(void *)&head, tottimeout) == RPC_SUCCESS)
107974462Salfred			goto done;
108074462Salfred	}
108174462Salfred	rpc_createerr.cf_stat = RPC_RPCBFAILURE;
108274462Salfred	clnt_geterr(client, &rpc_createerr.cf_error);
108374462Salfred
108474462Salfreddone:
108574462Salfred	CLNT_DESTROY(client);
108674462Salfred	return (head);
108774462Salfred}
108874462Salfred
108974462Salfred/*
109074462Salfred * rpcbinder remote-call-service interface.
109174462Salfred * This routine is used to call the rpcbind remote call service
109274462Salfred * which will look up a service program in the address maps, and then
109374462Salfred * remotely call that routine with the given parameters. This allows
109474462Salfred * programs to do a lookup and call in one step.
1095309487Sngie *
1096309487Sngie * nconf    -Netconfig structure
1097309487Sngie * host     - Remote host name
1098309487Sngie * proc     - Remote proc identifiers
1099309487Sngie * xdrargs, xdrres;  XDR routines
1100309487Sngie * argsp, resp - Argument and Result
1101309487Sngie * tout     - Timeout value for this call
1102309487Sngie * addr_ptr - Preallocated netbuf address
1103309487Sngie */
110474462Salfredenum clnt_stat
1105309487Sngierpcb_rmtcall(const struct netconfig *nconf, const char *host, rpcprog_t prog,
1106309487Sngie    rpcvers_t vers, rpcproc_t proc, xdrproc_t xdrargs, caddr_t argsp,
1107309487Sngie    xdrproc_t xdrres, caddr_t resp, struct timeval tout,
1108309487Sngie    const struct netbuf *addr_ptr)
110974462Salfred{
111074462Salfred	CLIENT *client;
111174462Salfred	enum clnt_stat stat;
111274462Salfred	struct r_rpcb_rmtcallargs a;
111374462Salfred	struct r_rpcb_rmtcallres r;
111474462Salfred	rpcvers_t rpcb_vers;
111574462Salfred
111679726Siedowse	stat = 0;
111774462Salfred	client = getclnthandle(host, nconf, NULL);
111874462Salfred	if (client == NULL) {
111974462Salfred		return (RPC_FAILED);
112074462Salfred	}
112174462Salfred	/*LINTED const castaway*/
112274462Salfred	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)(void *)&rmttimeout);
112374462Salfred	a.prog = prog;
112474462Salfred	a.vers = vers;
112574462Salfred	a.proc = proc;
112674462Salfred	a.args.args_val = argsp;
112774462Salfred	a.xdr_args = xdrargs;
112874462Salfred	r.addr = NULL;
112974462Salfred	r.results.results_val = resp;
113074462Salfred	r.xdr_res = xdrres;
113174462Salfred
113274462Salfred	for (rpcb_vers = RPCBVERS4; rpcb_vers >= RPCBVERS; rpcb_vers--) {
113374462Salfred		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&rpcb_vers);
113474462Salfred		stat = CLNT_CALL(client, (rpcproc_t)RPCBPROC_CALLIT,
113574462Salfred		    (xdrproc_t) xdr_rpcb_rmtcallargs, (char *)(void *)&a,
113674462Salfred		    (xdrproc_t) xdr_rpcb_rmtcallres, (char *)(void *)&r, tout);
113774462Salfred		if ((stat == RPC_SUCCESS) && (addr_ptr != NULL)) {
113874462Salfred			struct netbuf *na;
113974462Salfred			/*LINTED const castaway*/
114074462Salfred			na = uaddr2taddr((struct netconfig *) nconf, r.addr);
114174462Salfred			if (!na) {
114274462Salfred				stat = RPC_N2AXLATEFAILURE;
114374462Salfred				/*LINTED const castaway*/
114474462Salfred				((struct netbuf *) addr_ptr)->len = 0;
114574462Salfred				goto error;
114674462Salfred			}
114774462Salfred			if (na->len > addr_ptr->maxlen) {
114874462Salfred				/* Too long address */
114974462Salfred				stat = RPC_FAILED; /* XXX A better error no */
115074462Salfred				free(na->buf);
115174462Salfred				free(na);
115274462Salfred				/*LINTED const castaway*/
115374462Salfred				((struct netbuf *) addr_ptr)->len = 0;
115474462Salfred				goto error;
115574462Salfred			}
115674462Salfred			memcpy(addr_ptr->buf, na->buf, (size_t)na->len);
115774462Salfred			/*LINTED const castaway*/
115874462Salfred			((struct netbuf *)addr_ptr)->len = na->len;
115974462Salfred			free(na->buf);
116074462Salfred			free(na);
116174462Salfred			break;
116274462Salfred		} else if ((stat != RPC_PROGVERSMISMATCH) &&
116374462Salfred			    (stat != RPC_PROGUNAVAIL)) {
116474462Salfred			goto error;
116574462Salfred		}
116674462Salfred	}
116774462Salfrederror:
116874462Salfred	CLNT_DESTROY(client);
116974462Salfred	if (r.addr)
117074462Salfred		xdr_free((xdrproc_t) xdr_wrapstring, (char *)(void *)&r.addr);
117174462Salfred	return (stat);
117274462Salfred}
117374462Salfred
117474462Salfred/*
117574462Salfred * Gets the time on the remote host.
117674462Salfred * Returns 1 if succeeds else 0.
117774462Salfred */
117874462Salfredbool_t
1179309489Sngierpcb_gettime(const char *host, time_t *timep)
118074462Salfred{
118174462Salfred	CLIENT *client = NULL;
118274462Salfred	void *handle;
118374462Salfred	struct netconfig *nconf;
118474462Salfred	rpcvers_t vers;
118574462Salfred	enum clnt_stat st;
118674462Salfred
118774462Salfred
1188121653Smbr	if ((host == NULL) || (host[0] == 0)) {
118974462Salfred		time(timep);
119074462Salfred		return (TRUE);
119174462Salfred	}
119274462Salfred
119374462Salfred	if ((handle = __rpc_setconf("netpath")) == NULL) {
119474462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
119574462Salfred		return (FALSE);
119674462Salfred	}
119774462Salfred	rpc_createerr.cf_stat = RPC_SUCCESS;
119874462Salfred	while (client == NULL) {
119974462Salfred		if ((nconf = __rpc_getconf(handle)) == NULL) {
120074462Salfred			if (rpc_createerr.cf_stat == RPC_SUCCESS)
120174462Salfred				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
120274462Salfred			break;
120374462Salfred		}
120474462Salfred		client = getclnthandle(host, nconf, NULL);
120574462Salfred		if (client)
120674462Salfred			break;
120774462Salfred	}
120874462Salfred	__rpc_endconf(handle);
120974462Salfred	if (client == (CLIENT *) NULL) {
121074462Salfred		return (FALSE);
121174462Salfred	}
121274462Salfred
121374462Salfred	st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
121474462Salfred		(xdrproc_t) xdr_void, NULL,
121574462Salfred		(xdrproc_t) xdr_int, (char *)(void *)timep, tottimeout);
121674462Salfred
121774462Salfred	if ((st == RPC_PROGVERSMISMATCH) || (st == RPC_PROGUNAVAIL)) {
121874462Salfred		CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
121974462Salfred		if (vers == RPCBVERS4) {
122074462Salfred			/* fall back to earlier version */
122174462Salfred			vers = RPCBVERS;
122274462Salfred			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
122374462Salfred			st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
122474462Salfred				(xdrproc_t) xdr_void, NULL,
122574462Salfred				(xdrproc_t) xdr_int, (char *)(void *)timep,
122674462Salfred				tottimeout);
122774462Salfred		}
122874462Salfred	}
122974462Salfred	CLNT_DESTROY(client);
123074462Salfred	return (st == RPC_SUCCESS? TRUE: FALSE);
123174462Salfred}
123274462Salfred
123374462Salfred/*
123474462Salfred * Converts taddr to universal address.  This routine should never
123574462Salfred * really be called because local n2a libraries are always provided.
123674462Salfred */
123774462Salfredchar *
1238309489Sngierpcb_taddr2uaddr(struct netconfig *nconf, struct netbuf *taddr)
123974462Salfred{
124074462Salfred	CLIENT *client;
124174462Salfred	char *uaddr = NULL;
124274462Salfred
124374462Salfred
124474462Salfred	/* parameter checking */
124574462Salfred	if (nconf == NULL) {
124674462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
124774462Salfred		return (NULL);
124874462Salfred	}
124974462Salfred	if (taddr == NULL) {
125074462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
125174462Salfred		return (NULL);
125274462Salfred	}
125374462Salfred	client = local_rpcb();
125474462Salfred	if (! client) {
125574462Salfred		return (NULL);
125674462Salfred	}
125774462Salfred
125874462Salfred	CLNT_CALL(client, (rpcproc_t)RPCBPROC_TADDR2UADDR,
125974462Salfred	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
126074462Salfred	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr, tottimeout);
126174462Salfred	CLNT_DESTROY(client);
126274462Salfred	return (uaddr);
126374462Salfred}
126474462Salfred
126574462Salfred/*
126674462Salfred * Converts universal address to netbuf.  This routine should never
126774462Salfred * really be called because local n2a libraries are always provided.
126874462Salfred */
126974462Salfredstruct netbuf *
1270309489Sngierpcb_uaddr2taddr(struct netconfig *nconf, char *uaddr)
127174462Salfred{
127274462Salfred	CLIENT *client;
127374462Salfred	struct netbuf *taddr;
127474462Salfred
127574462Salfred
127674462Salfred	/* parameter checking */
127774462Salfred	if (nconf == NULL) {
127874462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
127974462Salfred		return (NULL);
128074462Salfred	}
128174462Salfred	if (uaddr == NULL) {
128274462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
128374462Salfred		return (NULL);
128474462Salfred	}
128574462Salfred	client = local_rpcb();
128674462Salfred	if (! client) {
128774462Salfred		return (NULL);
128874462Salfred	}
128974462Salfred
129074462Salfred	taddr = (struct netbuf *)calloc(1, sizeof (struct netbuf));
129174462Salfred	if (taddr == NULL) {
129274462Salfred		CLNT_DESTROY(client);
129374462Salfred		return (NULL);
129474462Salfred	}
129574462Salfred	if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_UADDR2TADDR,
129674462Salfred	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr,
129774462Salfred	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
129874462Salfred	    tottimeout) != RPC_SUCCESS) {
129974462Salfred		free(taddr);
130074462Salfred		taddr = NULL;
130174462Salfred	}
130274462Salfred	CLNT_DESTROY(client);
130374462Salfred	return (taddr);
130474462Salfred}
1305