174462Salfred/*	$NetBSD: rpcb_clnt.c,v 1.6 2000/07/16 06:41:43 itojun Exp $	*/
274462Salfred
374462Salfred/*
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 *
2174462Salfred * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
2274462Salfred * unrestricted use provided that this legend is included on all tape
2374462Salfred * media and as a part of the software program in whole or part.  Users
2474462Salfred * may copy or modify Sun RPC without charge, but are not authorized
2574462Salfred * to license or distribute it to anyone else except as part of a product or
2674462Salfred * program developed by the user.
2774462Salfred *
2874462Salfred * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
2974462Salfred * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
3074462Salfred * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
3174462Salfred *
3274462Salfred * Sun RPC is provided with no support and without any obligation on the
3374462Salfred * part of Sun Microsystems, Inc. to assist in its use, correction,
3474462Salfred * modification or enhancement.
3574462Salfred *
3674462Salfred * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
3774462Salfred * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
3874462Salfred * OR ANY PART THEREOF.
3974462Salfred *
4074462Salfred * In no event will Sun Microsystems, Inc. be liable for any lost revenue
4174462Salfred * or profits or other special, indirect and consequential damages, even if
4274462Salfred * Sun has been advised of the possibility of such damages.
4374462Salfred *
4474462Salfred * Sun Microsystems, Inc.
4574462Salfred * 2550 Garcia Avenue
4674462Salfred * Mountain View, California  94043
4774462Salfred */
4874462Salfred/*
4974462Salfred * Copyright (c) 1986-1991 by Sun Microsystems Inc.
5074462Salfred */
5174462Salfred
5274462Salfred/* #ident	"@(#)rpcb_clnt.c	1.27	94/04/24 SMI" */
5374462Salfred
5474462Salfred
55136581Sobrien#if defined(LIBC_SCCS) && !defined(lint)
5674462Salfredstatic char sccsid[] = "@(#)rpcb_clnt.c 1.30 89/06/21 Copyr 1988 Sun Micro";
5774462Salfred#endif
5892990Sobrien#include <sys/cdefs.h>
5992990Sobrien__FBSDID("$FreeBSD$");
6074462Salfred
6174462Salfred/*
6274462Salfred * rpcb_clnt.c
6374462Salfred * interface to rpcbind rpc service.
6474462Salfred *
6574462Salfred * Copyright (C) 1988, Sun Microsystems, Inc.
6674462Salfred */
6774462Salfred
6875094Siedowse#include "namespace.h"
6974462Salfred#include "reentrant.h"
7074462Salfred#include <sys/types.h>
7174462Salfred#include <sys/socket.h>
7274462Salfred#include <sys/un.h>
7374462Salfred#include <sys/utsname.h>
7474462Salfred#include <rpc/rpc.h>
7574462Salfred#include <rpc/rpcb_prot.h>
7674462Salfred#include <rpc/nettype.h>
7774462Salfred#include <netconfig.h>
7874462Salfred#ifdef PORTMAP
7974462Salfred#include <netinet/in.h>		/* FOR IPPROTO_TCP/UDP definitions */
8074462Salfred#include <rpc/pmap_prot.h>
8174462Salfred#endif				/* PORTMAP */
8274462Salfred#include <stdio.h>
8374462Salfred#include <errno.h>
8474462Salfred#include <stdlib.h>
8574462Salfred#include <string.h>
8674462Salfred#include <unistd.h>
8774462Salfred#include <netdb.h>
8874462Salfred#include <syslog.h>
8974462Salfred#include "un-namespace.h"
9074462Salfred
9174462Salfred#include "rpc_com.h"
92156090Sdeischen#include "mt_misc.h"
9374462Salfred
9474462Salfredstatic struct timeval tottimeout = { 60, 0 };
9574462Salfredstatic const struct timeval rmttimeout = { 3, 0 };
9699775Salfredstatic struct timeval rpcbrmttime = { 15, 0 };
9774462Salfred
9892905Sobrienextern bool_t xdr_wrapstring(XDR *, char **);
9974462Salfred
10074462Salfredstatic const char nullstring[] = "\000";
10174462Salfred
10274462Salfred#define	CACHESIZE 6
10374462Salfred
10474462Salfredstruct address_cache {
10574462Salfred	char *ac_host;
10674462Salfred	char *ac_netid;
10774462Salfred	char *ac_uaddr;
10874462Salfred	struct netbuf *ac_taddr;
10974462Salfred	struct address_cache *ac_next;
11074462Salfred};
11174462Salfred
11274462Salfredstatic struct address_cache *front;
11374462Salfredstatic int cachesize;
11474462Salfred
11574462Salfred#define	CLCR_GET_RPCB_TIMEOUT	1
11674462Salfred#define	CLCR_SET_RPCB_TIMEOUT	2
11774462Salfred
11874462Salfred
11974462Salfredextern int __rpc_lowvers;
12074462Salfred
12192905Sobrienstatic struct address_cache *check_cache(const char *, const char *);
12292905Sobrienstatic void delete_cache(struct netbuf *);
12392941Sobrienstatic void add_cache(const char *, const char *, struct netbuf *, char *);
12492941Sobrienstatic CLIENT *getclnthandle(const char *, const struct netconfig *, char **);
12592905Sobrienstatic CLIENT *local_rpcb(void);
12692941Sobrienstatic struct netbuf *got_entry(rpcb_entry_list_ptr, const struct netconfig *);
12774462Salfred
12874462Salfred/*
12974462Salfred * This routine adjusts the timeout used for calls to the remote rpcbind.
13074462Salfred * Also, this routine can be used to set the use of portmapper version 2
13174462Salfred * only when doing rpc_broadcasts
13274462Salfred * These are private routines that may not be provided in future releases.
13374462Salfred */
13474462Salfredbool_t
13574462Salfred__rpc_control(request, info)
13674462Salfred	int	request;
13774462Salfred	void	*info;
13874462Salfred{
13974462Salfred	switch (request) {
14074462Salfred	case CLCR_GET_RPCB_TIMEOUT:
14174462Salfred		*(struct timeval *)info = tottimeout;
14274462Salfred		break;
14374462Salfred	case CLCR_SET_RPCB_TIMEOUT:
14474462Salfred		tottimeout = *(struct timeval *)info;
14574462Salfred		break;
14674462Salfred	case CLCR_SET_LOWVERS:
14774462Salfred		__rpc_lowvers = *(int *)info;
14874462Salfred		break;
14974462Salfred	case CLCR_GET_LOWVERS:
15074462Salfred		*(int *)info = __rpc_lowvers;
15174462Salfred		break;
15274462Salfred	default:
15374462Salfred		return (FALSE);
15474462Salfred	}
15574462Salfred	return (TRUE);
15674462Salfred}
15774462Salfred
15874462Salfred/*
15974462Salfred *	It might seem that a reader/writer lock would be more reasonable here.
16074462Salfred *	However because getclnthandle(), the only user of the cache functions,
16174462Salfred *	may do a delete_cache() operation if a check_cache() fails to return an
16274462Salfred *	address useful to clnt_tli_create(), we may as well use a mutex.
16374462Salfred */
16474462Salfred/*
16574462Salfred * As it turns out, if the cache lock is *not* a reader/writer lock, we will
16674462Salfred * block all clnt_create's if we are trying to connect to a host that's down,
16774462Salfred * since the lock will be held all during that time.
16874462Salfred */
16974462Salfred
17074462Salfred/*
17174462Salfred * The routines check_cache(), add_cache(), delete_cache() manage the
17274462Salfred * cache of rpcbind addresses for (host, netid).
17374462Salfred */
17474462Salfred
17574462Salfredstatic struct address_cache *
17674462Salfredcheck_cache(host, netid)
17774462Salfred	const char *host, *netid;
17874462Salfred{
17974462Salfred	struct address_cache *cptr;
18074462Salfred
18174462Salfred	/* READ LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
18274462Salfred
18374462Salfred	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
18474462Salfred		if (!strcmp(cptr->ac_host, host) &&
18574462Salfred		    !strcmp(cptr->ac_netid, netid)) {
18674462Salfred#ifdef ND_DEBUG
18774462Salfred			fprintf(stderr, "Found cache entry for %s: %s\n",
18874462Salfred				host, netid);
18974462Salfred#endif
19074462Salfred			return (cptr);
19174462Salfred		}
19274462Salfred	}
19374462Salfred	return ((struct address_cache *) NULL);
19474462Salfred}
19574462Salfred
19674462Salfredstatic void
19774462Salfreddelete_cache(addr)
19874462Salfred	struct netbuf *addr;
19974462Salfred{
20074462Salfred	struct address_cache *cptr, *prevptr = NULL;
20174462Salfred
20274462Salfred	/* WRITE LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
20374462Salfred	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
20474462Salfred		if (!memcmp(cptr->ac_taddr->buf, addr->buf, addr->len)) {
20574462Salfred			free(cptr->ac_host);
20674462Salfred			free(cptr->ac_netid);
20774462Salfred			free(cptr->ac_taddr->buf);
20874462Salfred			free(cptr->ac_taddr);
20974462Salfred			if (cptr->ac_uaddr)
21074462Salfred				free(cptr->ac_uaddr);
21174462Salfred			if (prevptr)
21274462Salfred				prevptr->ac_next = cptr->ac_next;
21374462Salfred			else
21474462Salfred				front = cptr->ac_next;
21574462Salfred			free(cptr);
21674462Salfred			cachesize--;
21774462Salfred			break;
21874462Salfred		}
21974462Salfred		prevptr = cptr;
22074462Salfred	}
22174462Salfred}
22274462Salfred
22374462Salfredstatic void
22474462Salfredadd_cache(host, netid, taddr, uaddr)
22574462Salfred	const char *host, *netid;
22674462Salfred	char *uaddr;
22774462Salfred	struct netbuf *taddr;
22874462Salfred{
22974462Salfred	struct address_cache  *ad_cache, *cptr, *prevptr;
23074462Salfred
23174462Salfred	ad_cache = (struct address_cache *)
23274462Salfred			malloc(sizeof (struct address_cache));
23374462Salfred	if (!ad_cache) {
23474462Salfred		return;
23574462Salfred	}
23674462Salfred	ad_cache->ac_host = strdup(host);
23774462Salfred	ad_cache->ac_netid = strdup(netid);
23874462Salfred	ad_cache->ac_uaddr = uaddr ? strdup(uaddr) : NULL;
23974462Salfred	ad_cache->ac_taddr = (struct netbuf *)malloc(sizeof (struct netbuf));
24074462Salfred	if (!ad_cache->ac_host || !ad_cache->ac_netid || !ad_cache->ac_taddr ||
24174462Salfred		(uaddr && !ad_cache->ac_uaddr)) {
242162193Smbr		goto out;
24374462Salfred	}
24474462Salfred	ad_cache->ac_taddr->len = ad_cache->ac_taddr->maxlen = taddr->len;
24574462Salfred	ad_cache->ac_taddr->buf = (char *) malloc(taddr->len);
24674462Salfred	if (ad_cache->ac_taddr->buf == NULL) {
247162193Smbrout:
248162193Smbr		if (ad_cache->ac_host)
249162193Smbr			free(ad_cache->ac_host);
250162193Smbr		if (ad_cache->ac_netid)
251162193Smbr			free(ad_cache->ac_netid);
252162193Smbr		if (ad_cache->ac_uaddr)
253162193Smbr			free(ad_cache->ac_uaddr);
254162193Smbr		if (ad_cache->ac_taddr)
255162193Smbr			free(ad_cache->ac_taddr);
256162193Smbr		free(ad_cache);
25774462Salfred		return;
25874462Salfred	}
25974462Salfred	memcpy(ad_cache->ac_taddr->buf, taddr->buf, taddr->len);
26074462Salfred#ifdef ND_DEBUG
26174462Salfred	fprintf(stderr, "Added to cache: %s : %s\n", host, netid);
26274462Salfred#endif
26374462Salfred
26474462Salfred/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  cptr */
26574462Salfred
26674462Salfred	rwlock_wrlock(&rpcbaddr_cache_lock);
26774462Salfred	if (cachesize < CACHESIZE) {
26874462Salfred		ad_cache->ac_next = front;
26974462Salfred		front = ad_cache;
27074462Salfred		cachesize++;
27174462Salfred	} else {
27274462Salfred		/* Free the last entry */
27374462Salfred		cptr = front;
27474462Salfred		prevptr = NULL;
27574462Salfred		while (cptr->ac_next) {
27674462Salfred			prevptr = cptr;
27774462Salfred			cptr = cptr->ac_next;
27874462Salfred		}
27974462Salfred
28074462Salfred#ifdef ND_DEBUG
28174462Salfred		fprintf(stderr, "Deleted from cache: %s : %s\n",
28274462Salfred			cptr->ac_host, cptr->ac_netid);
28374462Salfred#endif
28474462Salfred		free(cptr->ac_host);
28574462Salfred		free(cptr->ac_netid);
28674462Salfred		free(cptr->ac_taddr->buf);
28774462Salfred		free(cptr->ac_taddr);
28874462Salfred		if (cptr->ac_uaddr)
28974462Salfred			free(cptr->ac_uaddr);
29074462Salfred
29174462Salfred		if (prevptr) {
29274462Salfred			prevptr->ac_next = NULL;
29374462Salfred			ad_cache->ac_next = front;
29474462Salfred			front = ad_cache;
29574462Salfred		} else {
29674462Salfred			front = ad_cache;
29774462Salfred			ad_cache->ac_next = NULL;
29874462Salfred		}
29974462Salfred		free(cptr);
30074462Salfred	}
30174462Salfred	rwlock_unlock(&rpcbaddr_cache_lock);
30274462Salfred}
30374462Salfred
30474462Salfred/*
30574462Salfred * This routine will return a client handle that is connected to the
30681069Siedowse * rpcbind. If targaddr is non-NULL, the "universal address" of the
30781069Siedowse * host will be stored in *targaddr; the caller is responsible for
30881069Siedowse * freeing this string.
30981069Siedowse * On error, returns NULL and free's everything.
31074462Salfred */
31174462Salfredstatic CLIENT *
31274462Salfredgetclnthandle(host, nconf, targaddr)
31374462Salfred	const char *host;
31474462Salfred	const struct netconfig *nconf;
31574462Salfred	char **targaddr;
31674462Salfred{
31774462Salfred	CLIENT *client;
31874462Salfred	struct netbuf *addr, taddr;
31974462Salfred	struct netbuf addr_to_delete;
32074462Salfred	struct __rpc_sockinfo si;
32174462Salfred	struct addrinfo hints, *res, *tres;
32274462Salfred	struct address_cache *ad_cache;
32374462Salfred	char *tmpaddr;
32474462Salfred
32574462Salfred/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  ad_cache */
32674462Salfred
32774462Salfred	/* Get the address of the rpcbind.  Check cache first */
32879726Siedowse	client = NULL;
32974462Salfred	addr_to_delete.len = 0;
33074462Salfred	rwlock_rdlock(&rpcbaddr_cache_lock);
33190269Salfred	ad_cache = NULL;
33290269Salfred	if (host != NULL)
33390269Salfred		ad_cache = check_cache(host, nconf->nc_netid);
33474462Salfred	if (ad_cache != NULL) {
33574462Salfred		addr = ad_cache->ac_taddr;
33674462Salfred		client = clnt_tli_create(RPC_ANYFD, nconf, addr,
33774462Salfred		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
33874462Salfred		if (client != NULL) {
33974462Salfred			if (targaddr)
34081069Siedowse				*targaddr = strdup(ad_cache->ac_uaddr);
34174462Salfred			rwlock_unlock(&rpcbaddr_cache_lock);
34274462Salfred			return (client);
34374462Salfred		}
34474462Salfred		addr_to_delete.len = addr->len;
34574462Salfred		addr_to_delete.buf = (char *)malloc(addr->len);
34674462Salfred		if (addr_to_delete.buf == NULL) {
34774462Salfred			addr_to_delete.len = 0;
34874462Salfred		} else {
34974462Salfred			memcpy(addr_to_delete.buf, addr->buf, addr->len);
35074462Salfred		}
35174462Salfred	}
35274462Salfred	rwlock_unlock(&rpcbaddr_cache_lock);
35374462Salfred	if (addr_to_delete.len != 0) {
35474462Salfred		/*
35574462Salfred		 * Assume this may be due to cache data being
35674462Salfred		 *  outdated
35774462Salfred		 */
35874462Salfred		rwlock_wrlock(&rpcbaddr_cache_lock);
35974462Salfred		delete_cache(&addr_to_delete);
36074462Salfred		rwlock_unlock(&rpcbaddr_cache_lock);
36174462Salfred		free(addr_to_delete.buf);
36274462Salfred	}
36374462Salfred	if (!__rpc_nconf2sockinfo(nconf, &si)) {
36474462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
36574462Salfred		return NULL;
36674462Salfred	}
36774462Salfred
36874462Salfred	memset(&hints, 0, sizeof hints);
36974462Salfred	hints.ai_family = si.si_af;
37074462Salfred	hints.ai_socktype = si.si_socktype;
37174462Salfred	hints.ai_protocol = si.si_proto;
37274462Salfred
37374462Salfred#ifdef CLNT_DEBUG
37474462Salfred	printf("trying netid %s family %d proto %d socktype %d\n",
37574462Salfred	    nconf->nc_netid, si.si_af, si.si_proto, si.si_socktype);
37674462Salfred#endif
37774462Salfred
37890269Salfred	if (nconf->nc_protofmly != NULL && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
37990269Salfred		client = local_rpcb();
38090269Salfred		if (! client) {
38190269Salfred#ifdef ND_DEBUG
38290269Salfred			clnt_pcreateerror("rpcbind clnt interface");
38390269Salfred#endif
38490269Salfred			return (NULL);
38590269Salfred		} else {
38690269Salfred			struct sockaddr_un sun;
387172259Smatteo			if (targaddr) {
388172259Smatteo			    *targaddr = malloc(sizeof(sun.sun_path));
389172259Smatteo			    if (*targaddr == NULL) {
390172259Smatteo				CLNT_DESTROY(client);
391172259Smatteo				return (NULL);
392172259Smatteo			    }
393172259Smatteo			    strncpy(*targaddr, _PATH_RPCBINDSOCK,
394172259Smatteo				sizeof(sun.sun_path));
395172259Smatteo			}
39690269Salfred			return (client);
39790269Salfred		}
39890269Salfred	} else {
39990269Salfred		if (getaddrinfo(host, "sunrpc", &hints, &res) != 0) {
40090269Salfred			rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
40190269Salfred			return NULL;
40290269Salfred		}
40374462Salfred	}
40474462Salfred
40574462Salfred	for (tres = res; tres != NULL; tres = tres->ai_next) {
40674462Salfred		taddr.buf = tres->ai_addr;
40774462Salfred		taddr.len = taddr.maxlen = tres->ai_addrlen;
40874462Salfred
40974462Salfred#ifdef ND_DEBUG
41074462Salfred		{
41174462Salfred			char *ua;
41274462Salfred
41374462Salfred			ua = taddr2uaddr(nconf, &taddr);
41474462Salfred			fprintf(stderr, "Got it [%s]\n", ua);
41574462Salfred			free(ua);
41674462Salfred		}
41774462Salfred#endif
41874462Salfred
41974462Salfred#ifdef ND_DEBUG
42074462Salfred		{
42174462Salfred			int i;
42274462Salfred
42374462Salfred			fprintf(stderr, "\tnetbuf len = %d, maxlen = %d\n",
42474462Salfred				taddr.len, taddr.maxlen);
42574462Salfred			fprintf(stderr, "\tAddress is ");
42674462Salfred			for (i = 0; i < taddr.len; i++)
42774462Salfred				fprintf(stderr, "%u.", ((char *)(taddr.buf))[i]);
42874462Salfred			fprintf(stderr, "\n");
42974462Salfred		}
43074462Salfred#endif
43174462Salfred		client = clnt_tli_create(RPC_ANYFD, nconf, &taddr,
43274462Salfred		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
43374462Salfred#ifdef ND_DEBUG
43474462Salfred		if (! client) {
43574462Salfred			clnt_pcreateerror("rpcbind clnt interface");
43674462Salfred		}
43774462Salfred#endif
43874462Salfred
43974462Salfred		if (client) {
44074462Salfred			tmpaddr = targaddr ? taddr2uaddr(nconf, &taddr) : NULL;
44174462Salfred			add_cache(host, nconf->nc_netid, &taddr, tmpaddr);
44274462Salfred			if (targaddr)
44374462Salfred				*targaddr = tmpaddr;
44474462Salfred			break;
44574462Salfred		}
44674462Salfred	}
44790269Salfred	if (res)
44890269Salfred		freeaddrinfo(res);
44974462Salfred	return (client);
45074462Salfred}
45174462Salfred
45274462Salfred/* XXX */
45374462Salfred#define IN4_LOCALHOST_STRING	"127.0.0.1"
45474462Salfred#define IN6_LOCALHOST_STRING	"::1"
45574462Salfred
45674462Salfred/*
45774462Salfred * This routine will return a client handle that is connected to the local
45874462Salfred * rpcbind. Returns NULL on error and free's everything.
45974462Salfred */
46074462Salfredstatic CLIENT *
46174462Salfredlocal_rpcb()
46274462Salfred{
46374462Salfred	CLIENT *client;
46474462Salfred	static struct netconfig *loopnconf;
46574462Salfred	static char *hostname;
46674462Salfred	int sock;
46774462Salfred	size_t tsize;
46874462Salfred	struct netbuf nbuf;
46974462Salfred	struct sockaddr_un sun;
47074462Salfred
47174462Salfred	/*
47274462Salfred	 * Try connecting to the local rpcbind through a local socket
47374462Salfred	 * first. If this doesn't work, try all transports defined in
47474462Salfred	 * the netconfig file.
47574462Salfred	 */
47674462Salfred	memset(&sun, 0, sizeof sun);
47774462Salfred	sock = _socket(AF_LOCAL, SOCK_STREAM, 0);
47874462Salfred	if (sock < 0)
47974462Salfred		goto try_nconf;
48074462Salfred	sun.sun_family = AF_LOCAL;
48174462Salfred	strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
48274462Salfred	nbuf.len = sun.sun_len = SUN_LEN(&sun);
48374462Salfred	nbuf.maxlen = sizeof (struct sockaddr_un);
48474462Salfred	nbuf.buf = &sun;
48574462Salfred
48674462Salfred	tsize = __rpc_get_t_size(AF_LOCAL, 0, 0);
48774462Salfred	client = clnt_vc_create(sock, &nbuf, (rpcprog_t)RPCBPROG,
48874462Salfred	    (rpcvers_t)RPCBVERS, tsize, tsize);
48974462Salfred
49090735Siedowse	if (client != NULL) {
49190735Siedowse		/* Mark the socket to be closed in destructor */
49290735Siedowse		(void) CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
49374462Salfred		return client;
49490735Siedowse	}
49574462Salfred
49690735Siedowse	/* Nobody needs this socket anymore; free the descriptor. */
49790735Siedowse	_close(sock);
49890735Siedowse
49974462Salfredtry_nconf:
50074462Salfred
50174462Salfred/* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */
50274462Salfred	mutex_lock(&loopnconf_lock);
50374462Salfred	if (loopnconf == NULL) {
50474462Salfred		struct netconfig *nconf, *tmpnconf = NULL;
50574462Salfred		void *nc_handle;
50674462Salfred		int fd;
50774462Salfred
50874462Salfred		nc_handle = setnetconfig();
50974462Salfred		if (nc_handle == NULL) {
51074462Salfred			/* fails to open netconfig file */
51174462Salfred			syslog (LOG_ERR, "rpc: failed to open " NETCONFIG);
51274462Salfred			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
51374462Salfred			mutex_unlock(&loopnconf_lock);
51474462Salfred			return (NULL);
51574462Salfred		}
51674462Salfred		while ((nconf = getnetconfig(nc_handle)) != NULL) {
51774462Salfred#ifdef INET6
51874462Salfred			if ((strcmp(nconf->nc_protofmly, NC_INET6) == 0 ||
51974462Salfred#else
52074462Salfred			if ((
52174462Salfred#endif
52274462Salfred			     strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
52374462Salfred			    (nconf->nc_semantics == NC_TPI_COTS ||
52474462Salfred			     nconf->nc_semantics == NC_TPI_COTS_ORD)) {
52574462Salfred				fd = __rpc_nconf2fd(nconf);
52674462Salfred				/*
52774462Salfred				 * Can't create a socket, assume that
52874462Salfred				 * this family isn't configured in the kernel.
52974462Salfred				 */
53074462Salfred				if (fd < 0)
53174462Salfred					continue;
53274462Salfred				_close(fd);
53374462Salfred				tmpnconf = nconf;
53474462Salfred				if (!strcmp(nconf->nc_protofmly, NC_INET))
53574462Salfred					hostname = IN4_LOCALHOST_STRING;
53674462Salfred				else
53774462Salfred					hostname = IN6_LOCALHOST_STRING;
53874462Salfred			}
53974462Salfred		}
54074462Salfred		if (tmpnconf == NULL) {
54174462Salfred			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
54274462Salfred			mutex_unlock(&loopnconf_lock);
54374462Salfred			return (NULL);
54474462Salfred		}
54574462Salfred		loopnconf = getnetconfigent(tmpnconf->nc_netid);
54674462Salfred		/* loopnconf is never freed */
54774462Salfred		endnetconfig(nc_handle);
54874462Salfred	}
54974462Salfred	mutex_unlock(&loopnconf_lock);
55074462Salfred	client = getclnthandle(hostname, loopnconf, NULL);
55174462Salfred	return (client);
55274462Salfred}
55374462Salfred
55474462Salfred/*
55574462Salfred * Set a mapping between program, version and address.
55674462Salfred * Calls the rpcbind service to do the mapping.
55774462Salfred */
55874462Salfredbool_t
55974462Salfredrpcb_set(program, version, nconf, address)
56074462Salfred	rpcprog_t program;
56174462Salfred	rpcvers_t version;
56274462Salfred	const struct netconfig *nconf;	/* Network structure of transport */
56374462Salfred	const struct netbuf *address;		/* Services netconfig address */
56474462Salfred{
56574462Salfred	CLIENT *client;
56674462Salfred	bool_t rslt = FALSE;
56774462Salfred	RPCB parms;
56874462Salfred	char uidbuf[32];
56974462Salfred
57074462Salfred	/* parameter checking */
57174462Salfred	if (nconf == NULL) {
57274462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
57374462Salfred		return (FALSE);
57474462Salfred	}
57574462Salfred	if (address == NULL) {
57674462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
57774462Salfred		return (FALSE);
57874462Salfred	}
57974462Salfred	client = local_rpcb();
58074462Salfred	if (! client) {
58174462Salfred		return (FALSE);
58274462Salfred	}
58374462Salfred
58474462Salfred	/* convert to universal */
58574462Salfred	/*LINTED const castaway*/
58674462Salfred	parms.r_addr = taddr2uaddr((struct netconfig *) nconf,
58774462Salfred				   (struct netbuf *)address);
58874462Salfred	if (!parms.r_addr) {
58990735Siedowse		CLNT_DESTROY(client);
59074462Salfred		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
59174462Salfred		return (FALSE); /* no universal address */
59274462Salfred	}
59374462Salfred	parms.r_prog = program;
59474462Salfred	parms.r_vers = version;
59574462Salfred	parms.r_netid = nconf->nc_netid;
59674462Salfred	/*
59774462Salfred	 * Though uid is not being used directly, we still send it for
59874462Salfred	 * completeness.  For non-unix platforms, perhaps some other
59974462Salfred	 * string or an empty string can be sent.
60074462Salfred	 */
60174462Salfred	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
60274462Salfred	parms.r_owner = uidbuf;
60374462Salfred
60474462Salfred	CLNT_CALL(client, (rpcproc_t)RPCBPROC_SET, (xdrproc_t) xdr_rpcb,
60574462Salfred	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
60674462Salfred	    (char *)(void *)&rslt, tottimeout);
60774462Salfred
60874462Salfred	CLNT_DESTROY(client);
60974462Salfred	free(parms.r_addr);
61074462Salfred	return (rslt);
61174462Salfred}
61274462Salfred
61374462Salfred/*
61474462Salfred * Remove the mapping between program, version and netbuf address.
61574462Salfred * Calls the rpcbind service to do the un-mapping.
61674462Salfred * If netbuf is NULL, unset for all the transports, otherwise unset
61774462Salfred * only for the given transport.
61874462Salfred */
61974462Salfredbool_t
62074462Salfredrpcb_unset(program, version, nconf)
62174462Salfred	rpcprog_t program;
62274462Salfred	rpcvers_t version;
62374462Salfred	const struct netconfig *nconf;
62474462Salfred{
62574462Salfred	CLIENT *client;
62674462Salfred	bool_t rslt = FALSE;
62774462Salfred	RPCB parms;
62874462Salfred	char uidbuf[32];
62974462Salfred
63074462Salfred	client = local_rpcb();
63174462Salfred	if (! client) {
63274462Salfred		return (FALSE);
63374462Salfred	}
63474462Salfred
63574462Salfred	parms.r_prog = program;
63674462Salfred	parms.r_vers = version;
63774462Salfred	if (nconf)
63874462Salfred		parms.r_netid = nconf->nc_netid;
63974462Salfred	else {
64074462Salfred		/*LINTED const castaway*/
64174462Salfred		parms.r_netid = (char *) &nullstring[0]; /* unsets  all */
64274462Salfred	}
64374462Salfred	/*LINTED const castaway*/
64474462Salfred	parms.r_addr = (char *) &nullstring[0];
64574462Salfred	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
64674462Salfred	parms.r_owner = uidbuf;
64774462Salfred
64874462Salfred	CLNT_CALL(client, (rpcproc_t)RPCBPROC_UNSET, (xdrproc_t) xdr_rpcb,
64974462Salfred	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
65074462Salfred	    (char *)(void *)&rslt, tottimeout);
65174462Salfred
65274462Salfred	CLNT_DESTROY(client);
65374462Salfred	return (rslt);
65474462Salfred}
65574462Salfred
65674462Salfred/*
65774462Salfred * From the merged list, find the appropriate entry
65874462Salfred */
65974462Salfredstatic struct netbuf *
66074462Salfredgot_entry(relp, nconf)
66174462Salfred	rpcb_entry_list_ptr relp;
66274462Salfred	const struct netconfig *nconf;
66374462Salfred{
66474462Salfred	struct netbuf *na = NULL;
66574462Salfred	rpcb_entry_list_ptr sp;
66674462Salfred	rpcb_entry *rmap;
66774462Salfred
66874462Salfred	for (sp = relp; sp != NULL; sp = sp->rpcb_entry_next) {
66974462Salfred		rmap = &sp->rpcb_entry_map;
67074462Salfred		if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) &&
67174462Salfred		    (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) &&
67274462Salfred		    (nconf->nc_semantics == rmap->r_nc_semantics) &&
673121653Smbr		    (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != 0)) {
67474462Salfred			na = uaddr2taddr(nconf, rmap->r_maddr);
67574462Salfred#ifdef ND_DEBUG
67674462Salfred			fprintf(stderr, "\tRemote address is [%s].\n",
67774462Salfred				rmap->r_maddr);
67874462Salfred			if (!na)
67974462Salfred				fprintf(stderr,
68074462Salfred				    "\tCouldn't resolve remote address!\n");
68174462Salfred#endif
68274462Salfred			break;
68374462Salfred		}
68474462Salfred	}
68574462Salfred	return (na);
68674462Salfred}
68774462Salfred
68874462Salfred/*
68999775Salfred * Quick check to see if rpcbind is up.  Tries to connect over
69099775Salfred * local transport.
69199775Salfred */
692156090Sdeischenstatic bool_t
69399775Salfred__rpcbind_is_up()
69499775Salfred{
69599775Salfred	struct netconfig *nconf;
69699775Salfred	struct sockaddr_un sun;
69799775Salfred	void *localhandle;
69899775Salfred	int sock;
69999775Salfred
70099775Salfred	nconf = NULL;
70199775Salfred	localhandle = setnetconfig();
702100701Siedowse	while ((nconf = getnetconfig(localhandle)) != NULL) {
70399775Salfred		if (nconf->nc_protofmly != NULL &&
70499775Salfred		    strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)
70599775Salfred			 break;
70699775Salfred	}
70799775Salfred	if (nconf == NULL)
70899775Salfred		return (FALSE);
70999775Salfred
71099775Salfred	endnetconfig(localhandle);
71199775Salfred
71299775Salfred	memset(&sun, 0, sizeof sun);
71399775Salfred	sock = _socket(AF_LOCAL, SOCK_STREAM, 0);
71499775Salfred	if (sock < 0)
71599775Salfred		return (FALSE);
71699775Salfred	sun.sun_family = AF_LOCAL;
71799775Salfred	strncpy(sun.sun_path, _PATH_RPCBINDSOCK, sizeof(sun.sun_path));
71899775Salfred	sun.sun_len = SUN_LEN(&sun);
71999775Salfred
72099775Salfred	if (_connect(sock, (struct sockaddr *)&sun, sun.sun_len) < 0) {
72199775Salfred		_close(sock);
72299775Salfred		return (FALSE);
72399775Salfred	}
72499775Salfred
72599775Salfred	_close(sock);
72699775Salfred	return (TRUE);
72799775Salfred}
72899775Salfred
72999775Salfred/*
73074462Salfred * An internal function which optimizes rpcb_getaddr function.  It also
73174462Salfred * returns the client handle that it uses to contact the remote rpcbind.
73274462Salfred *
73374462Salfred * The algorithm used: If the transports is TCP or UDP, it first tries
73474462Salfred * version 2 (portmap), 4 and then 3 (svr4).  This order should be
73574462Salfred * changed in the next OS release to 4, 2 and 3.  We are assuming that by
73674462Salfred * that time, version 4 would be available on many machines on the network.
73774462Salfred * With this algorithm, we get performance as well as a plan for
73874462Salfred * obsoleting version 2.
73974462Salfred *
74074462Salfred * For all other transports, the algorithm remains as 4 and then 3.
74174462Salfred *
74274462Salfred * XXX: Due to some problems with t_connect(), we do not reuse the same client
74374462Salfred * handle for COTS cases and hence in these cases we do not return the
74474462Salfred * client handle.  This code will change if t_connect() ever
74574462Salfred * starts working properly.  Also look under clnt_vc.c.
74674462Salfred */
74774462Salfredstruct netbuf *
74899775Salfred__rpcb_findaddr_timed(program, version, nconf, host, clpp, tp)
74974462Salfred	rpcprog_t program;
75074462Salfred	rpcvers_t version;
75174462Salfred	const struct netconfig *nconf;
75274462Salfred	const char *host;
75374462Salfred	CLIENT **clpp;
75499775Salfred	struct timeval *tp;
75574462Salfred{
75699775Salfred	static bool_t check_rpcbind = TRUE;
75774462Salfred	CLIENT *client = NULL;
75874462Salfred	RPCB parms;
75974462Salfred	enum clnt_stat clnt_st;
76074462Salfred	char *ua = NULL;
76174462Salfred	rpcvers_t vers;
76274462Salfred	struct netbuf *address = NULL;
76374462Salfred	rpcvers_t start_vers = RPCBVERS4;
76474462Salfred	struct netbuf servaddr;
76574462Salfred
76674462Salfred	/* parameter checking */
76774462Salfred	if (nconf == NULL) {
76874462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
76974462Salfred		return (NULL);
77074462Salfred	}
77174462Salfred
77274462Salfred	parms.r_addr = NULL;
77374462Salfred
77499775Salfred	/*
77599775Salfred	 * Use default total timeout if no timeout is specified.
77699775Salfred	 */
77799775Salfred	if (tp == NULL)
77899775Salfred		tp = &tottimeout;
77999775Salfred
78074462Salfred#ifdef PORTMAP
78174462Salfred	/* Try version 2 for TCP or UDP */
78274462Salfred	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
78374462Salfred		u_short port = 0;
78474462Salfred		struct netbuf remote;
78574462Salfred		rpcvers_t pmapvers = 2;
78674462Salfred		struct pmap pmapparms;
78774462Salfred
78874462Salfred		/*
78974462Salfred		 * Try UDP only - there are some portmappers out
79074462Salfred		 * there that use UDP only.
79174462Salfred		 */
79274462Salfred		if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
79374462Salfred			struct netconfig *newnconf;
79474462Salfred
795100701Siedowse			if ((newnconf = getnetconfigent("udp")) == NULL) {
79674462Salfred				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
79774462Salfred				return (NULL);
79874462Salfred			}
79974462Salfred			client = getclnthandle(host, newnconf, &parms.r_addr);
800100701Siedowse			freenetconfigent(newnconf);
80174462Salfred		} else {
80274462Salfred			client = getclnthandle(host, nconf, &parms.r_addr);
80374462Salfred		}
80499775Salfred		if (client == NULL)
80574462Salfred			return (NULL);
80674462Salfred
80799775Salfred		/*
80899775Salfred		 * Set version and retry timeout.
80999775Salfred		 */
81099775Salfred		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
81199775Salfred		CLNT_CONTROL(client, CLSET_VERS, (char *)&pmapvers);
81299775Salfred
81374462Salfred		pmapparms.pm_prog = program;
81474462Salfred		pmapparms.pm_vers = version;
81574462Salfred		pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ?
81674462Salfred					IPPROTO_UDP : IPPROTO_TCP;
81774462Salfred		pmapparms.pm_port = 0;	/* not needed */
81874462Salfred		clnt_st = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
81974462Salfred		    (xdrproc_t) xdr_pmap, (caddr_t)(void *)&pmapparms,
82074462Salfred		    (xdrproc_t) xdr_u_short, (caddr_t)(void *)&port,
82199775Salfred		    *tp);
82274462Salfred		if (clnt_st != RPC_SUCCESS) {
82374462Salfred			if ((clnt_st == RPC_PROGVERSMISMATCH) ||
82474462Salfred				(clnt_st == RPC_PROGUNAVAIL))
82574462Salfred				goto try_rpcbind; /* Try different versions */
82674462Salfred			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
82774462Salfred			clnt_geterr(client, &rpc_createerr.cf_error);
82874462Salfred			goto error;
82974462Salfred		} else if (port == 0) {
83074462Salfred			address = NULL;
83174462Salfred			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
83274462Salfred			goto error;
83374462Salfred		}
83474462Salfred		port = htons(port);
83599775Salfred		CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)&remote);
83674462Salfred		if (((address = (struct netbuf *)
83774462Salfred			malloc(sizeof (struct netbuf))) == NULL) ||
83874462Salfred		    ((address->buf = (char *)
83974462Salfred			malloc(remote.len)) == NULL)) {
84074462Salfred			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
84174462Salfred			clnt_geterr(client, &rpc_createerr.cf_error);
84274462Salfred			if (address) {
84374462Salfred				free(address);
84474462Salfred				address = NULL;
84574462Salfred			}
84674462Salfred			goto error;
84774462Salfred		}
84874462Salfred		memcpy(address->buf, remote.buf, remote.len);
84974462Salfred		memcpy(&((char *)address->buf)[sizeof (short)],
85074462Salfred				(char *)(void *)&port, sizeof (short));
85174462Salfred		address->len = address->maxlen = remote.len;
85274462Salfred		goto done;
85374462Salfred	}
85474462Salfred#endif				/* PORTMAP */
85574462Salfred
85674462Salfredtry_rpcbind:
85774462Salfred	/*
85899775Salfred	 * Check if rpcbind is up.  This prevents needless delays when
85999775Salfred	 * accessing applications such as the keyserver while booting
86099775Salfred	 * disklessly.
86199775Salfred	 */
86299775Salfred	if (check_rpcbind && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
86399775Salfred		if (!__rpcbind_is_up()) {
86499775Salfred			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
86599775Salfred			rpc_createerr.cf_error.re_errno = 0;
86699775Salfred			goto error;
86799775Salfred		}
86899775Salfred		check_rpcbind = FALSE;
86999775Salfred	}
87099775Salfred
87199775Salfred	/*
87274462Salfred	 * Now we try version 4 and then 3.
87374462Salfred	 * We also send the remote system the address we used to
87474462Salfred	 * contact it in case it can help to connect back with us
87574462Salfred	 */
87674462Salfred	parms.r_prog = program;
87774462Salfred	parms.r_vers = version;
87874462Salfred	/*LINTED const castaway*/
87974462Salfred	parms.r_owner = (char *) &nullstring[0];	/* not needed; */
88074462Salfred							/* just for xdring */
88174462Salfred	parms.r_netid = nconf->nc_netid; /* not really needed */
88274462Salfred
88374462Salfred	/*
88474462Salfred	 * If a COTS transport is being used, try getting address via CLTS
88574462Salfred	 * transport.  This works only with version 4.
88674462Salfred	 */
88799775Salfred	if (nconf->nc_semantics == NC_TPI_COTS_ORD ||
88899775Salfred			nconf->nc_semantics == NC_TPI_COTS) {
88999775Salfred
89074462Salfred		void *handle;
89174462Salfred		struct netconfig *nconf_clts;
89274462Salfred		rpcb_entry_list_ptr relp = NULL;
89374462Salfred
89474462Salfred		if (client == NULL) {
89574462Salfred			/* This did not go through the above PORTMAP/TCP code */
89674462Salfred			if ((handle = __rpc_setconf("datagram_v")) != NULL) {
89774462Salfred				while ((nconf_clts = __rpc_getconf(handle))
89874462Salfred					!= NULL) {
89974462Salfred					if (strcmp(nconf_clts->nc_protofmly,
90074462Salfred						nconf->nc_protofmly) != 0) {
90174462Salfred						continue;
90274462Salfred					}
90374462Salfred					client = getclnthandle(host, nconf_clts,
90474462Salfred							&parms.r_addr);
90574462Salfred					break;
90674462Salfred				}
90774462Salfred				__rpc_endconf(handle);
90874462Salfred			}
90974462Salfred			if (client == NULL)
91074462Salfred				goto regular_rpcbind;	/* Go the regular way */
91174462Salfred		} else {
91274462Salfred			/* This is a UDP PORTMAP handle.  Change to version 4 */
91374462Salfred			vers = RPCBVERS4;
91474462Salfred			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
91574462Salfred		}
91674462Salfred		/*
91774462Salfred		 * We also send the remote system the address we used to
91874462Salfred		 * contact it in case it can help it connect back with us
91974462Salfred		 */
92074462Salfred		if (parms.r_addr == NULL) {
92174462Salfred			/*LINTED const castaway*/
92274462Salfred			parms.r_addr = (char *) &nullstring[0]; /* for XDRing */
92374462Salfred		}
92499775Salfred
92599775Salfred		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
92699775Salfred
92774462Salfred		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDRLIST,
92874462Salfred		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
92974462Salfred		    (xdrproc_t) xdr_rpcb_entry_list_ptr,
93099775Salfred		    (char *)(void *)&relp, *tp);
93174462Salfred		if (clnt_st == RPC_SUCCESS) {
93274462Salfred			if ((address = got_entry(relp, nconf)) != NULL) {
93374462Salfred				xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
93474462Salfred				    (char *)(void *)&relp);
935109952Smbr				CLNT_CONTROL(client, CLGET_SVC_ADDR,
936109952Smbr					(char *)(void *)&servaddr);
937109952Smbr				__rpc_fixup_addr(address, &servaddr);
93874462Salfred				goto done;
93974462Salfred			}
94074462Salfred			/* Entry not found for this transport */
94174462Salfred			xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
94274462Salfred			    (char *)(void *)&relp);
94374462Salfred			/*
94474462Salfred			 * XXX: should have perhaps returned with error but
94574462Salfred			 * since the remote machine might not always be able
94674462Salfred			 * to send the address on all transports, we try the
94774462Salfred			 * regular way with regular_rpcbind
94874462Salfred			 */
94974462Salfred			goto regular_rpcbind;
95074462Salfred		} else if ((clnt_st == RPC_PROGVERSMISMATCH) ||
95174462Salfred			(clnt_st == RPC_PROGUNAVAIL)) {
95274462Salfred			start_vers = RPCBVERS;	/* Try version 3 now */
95374462Salfred			goto regular_rpcbind; /* Try different versions */
95474462Salfred		} else {
95574462Salfred			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
95674462Salfred			clnt_geterr(client, &rpc_createerr.cf_error);
95774462Salfred			goto error;
95874462Salfred		}
95974462Salfred	}
96074462Salfred
96174462Salfredregular_rpcbind:
96274462Salfred
96374462Salfred	/* Now the same transport is to be used to get the address */
96474462Salfred	if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
96574462Salfred			(nconf->nc_semantics == NC_TPI_COTS))) {
96674462Salfred		/* A CLTS type of client - destroy it */
96774462Salfred		CLNT_DESTROY(client);
96874462Salfred		client = NULL;
96974462Salfred	}
97074462Salfred
97174462Salfred	if (client == NULL) {
97274462Salfred		client = getclnthandle(host, nconf, &parms.r_addr);
97374462Salfred		if (client == NULL) {
97474462Salfred			goto error;
97574462Salfred		}
97674462Salfred	}
97774462Salfred	if (parms.r_addr == NULL) {
97874462Salfred		/*LINTED const castaway*/
97974462Salfred		parms.r_addr = (char *) &nullstring[0];
98074462Salfred	}
98174462Salfred
98274462Salfred	/* First try from start_vers and then version 3 (RPCBVERS) */
98399775Salfred
98499775Salfred	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *) &rpcbrmttime);
98574462Salfred	for (vers = start_vers;  vers >= RPCBVERS; vers--) {
98674462Salfred		/* Set the version */
98774462Salfred		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
98874462Salfred		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
98974462Salfred		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
99099775Salfred		    (xdrproc_t) xdr_wrapstring, (char *)(void *) &ua, *tp);
99174462Salfred		if (clnt_st == RPC_SUCCESS) {
992121653Smbr			if ((ua == NULL) || (ua[0] == 0)) {
99374462Salfred				/* address unknown */
99474462Salfred				rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
99574462Salfred				goto error;
99674462Salfred			}
99774462Salfred			address = uaddr2taddr(nconf, ua);
99874462Salfred#ifdef ND_DEBUG
99974462Salfred			fprintf(stderr, "\tRemote address is [%s]\n", ua);
100074462Salfred			if (!address)
100174462Salfred				fprintf(stderr,
100274462Salfred					"\tCouldn't resolve remote address!\n");
100374462Salfred#endif
100474462Salfred			xdr_free((xdrproc_t)xdr_wrapstring,
100574462Salfred			    (char *)(void *)&ua);
100674462Salfred
100774462Salfred			if (! address) {
100874462Salfred				/* We don't know about your universal address */
100974462Salfred				rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
101074462Salfred				goto error;
101174462Salfred			}
101274462Salfred			CLNT_CONTROL(client, CLGET_SVC_ADDR,
101374462Salfred			    (char *)(void *)&servaddr);
101474462Salfred			__rpc_fixup_addr(address, &servaddr);
101574462Salfred			goto done;
101674462Salfred		} else if (clnt_st == RPC_PROGVERSMISMATCH) {
101774462Salfred			struct rpc_err rpcerr;
101874462Salfred
101974462Salfred			clnt_geterr(client, &rpcerr);
102074462Salfred			if (rpcerr.re_vers.low > RPCBVERS4)
102174462Salfred				goto error;  /* a new version, can't handle */
102274462Salfred		} else if (clnt_st != RPC_PROGUNAVAIL) {
102374462Salfred			/* Cant handle this error */
102474462Salfred			rpc_createerr.cf_stat = clnt_st;
102574462Salfred			clnt_geterr(client, &rpc_createerr.cf_error);
102674462Salfred			goto error;
102774462Salfred		}
102874462Salfred	}
102974462Salfred
103074462Salfrederror:
103174462Salfred	if (client) {
103274462Salfred		CLNT_DESTROY(client);
103374462Salfred		client = NULL;
103474462Salfred	}
103574462Salfreddone:
103674462Salfred	if (nconf->nc_semantics != NC_TPI_CLTS) {
103774462Salfred		/* This client is the connectionless one */
103874462Salfred		if (client) {
103974462Salfred			CLNT_DESTROY(client);
104074462Salfred			client = NULL;
104174462Salfred		}
104274462Salfred	}
104374462Salfred	if (clpp) {
104474462Salfred		*clpp = client;
104574462Salfred	} else if (client) {
104674462Salfred		CLNT_DESTROY(client);
104774462Salfred	}
104879726Siedowse	if (parms.r_addr != NULL && parms.r_addr != nullstring)
104979726Siedowse		free(parms.r_addr);
105074462Salfred	return (address);
105174462Salfred}
105274462Salfred
105374462Salfred
105474462Salfred/*
105574462Salfred * Find the mapped address for program, version.
105674462Salfred * Calls the rpcbind service remotely to do the lookup.
105774462Salfred * Uses the transport specified in nconf.
105874462Salfred * Returns FALSE (0) if no map exists, else returns 1.
105974462Salfred *
106074462Salfred * Assuming that the address is all properly allocated
106174462Salfred */
106274462Salfredint
106374462Salfredrpcb_getaddr(program, version, nconf, address, host)
106474462Salfred	rpcprog_t program;
106574462Salfred	rpcvers_t version;
106674462Salfred	const struct netconfig *nconf;
106774462Salfred	struct netbuf *address;
106874462Salfred	const char *host;
106974462Salfred{
107074462Salfred	struct netbuf *na;
107174462Salfred
107299775Salfred	if ((na = __rpcb_findaddr_timed(program, version,
107399775Salfred	    (struct netconfig *) nconf, (char *) host,
107499775Salfred	    (CLIENT **) NULL, (struct timeval *) NULL)) == NULL)
107574462Salfred		return (FALSE);
107674462Salfred
107774462Salfred	if (na->len > address->maxlen) {
107874462Salfred		/* Too long address */
107974462Salfred		free(na->buf);
108074462Salfred		free(na);
108174462Salfred		rpc_createerr.cf_stat = RPC_FAILED;
108274462Salfred		return (FALSE);
108374462Salfred	}
108474462Salfred	memcpy(address->buf, na->buf, (size_t)na->len);
108574462Salfred	address->len = na->len;
108674462Salfred	free(na->buf);
108774462Salfred	free(na);
108874462Salfred	return (TRUE);
108974462Salfred}
109074462Salfred
109174462Salfred/*
109274462Salfred * Get a copy of the current maps.
109374462Salfred * Calls the rpcbind service remotely to get the maps.
109474462Salfred *
109574462Salfred * It returns only a list of the services
109674462Salfred * It returns NULL on failure.
109774462Salfred */
109874462Salfredrpcblist *
109974462Salfredrpcb_getmaps(nconf, host)
110074462Salfred	const struct netconfig *nconf;
110174462Salfred	const char *host;
110274462Salfred{
110374462Salfred	rpcblist_ptr head = NULL;
110474462Salfred	CLIENT *client;
110574462Salfred	enum clnt_stat clnt_st;
110674462Salfred	rpcvers_t vers = 0;
110774462Salfred
110874462Salfred	client = getclnthandle(host, nconf, NULL);
110974462Salfred	if (client == NULL) {
111074462Salfred		return (head);
111174462Salfred	}
111274462Salfred	clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
111374462Salfred	    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
111474462Salfred	    (char *)(void *)&head, tottimeout);
111574462Salfred	if (clnt_st == RPC_SUCCESS)
111674462Salfred		goto done;
111774462Salfred
111874462Salfred	if ((clnt_st != RPC_PROGVERSMISMATCH) &&
111974462Salfred	    (clnt_st != RPC_PROGUNAVAIL)) {
112074462Salfred		rpc_createerr.cf_stat = RPC_RPCBFAILURE;
112174462Salfred		clnt_geterr(client, &rpc_createerr.cf_error);
112274462Salfred		goto done;
112374462Salfred	}
112474462Salfred
112574462Salfred	/* fall back to earlier version */
112674462Salfred	CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
112774462Salfred	if (vers == RPCBVERS4) {
112874462Salfred		vers = RPCBVERS;
112974462Salfred		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
113074462Salfred		if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
113174462Salfred		    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
113274462Salfred		    (char *)(void *)&head, tottimeout) == RPC_SUCCESS)
113374462Salfred			goto done;
113474462Salfred	}
113574462Salfred	rpc_createerr.cf_stat = RPC_RPCBFAILURE;
113674462Salfred	clnt_geterr(client, &rpc_createerr.cf_error);
113774462Salfred
113874462Salfreddone:
113974462Salfred	CLNT_DESTROY(client);
114074462Salfred	return (head);
114174462Salfred}
114274462Salfred
114374462Salfred/*
114474462Salfred * rpcbinder remote-call-service interface.
114574462Salfred * This routine is used to call the rpcbind remote call service
114674462Salfred * which will look up a service program in the address maps, and then
114774462Salfred * remotely call that routine with the given parameters. This allows
114874462Salfred * programs to do a lookup and call in one step.
114974462Salfred*/
115074462Salfredenum clnt_stat
115174462Salfredrpcb_rmtcall(nconf, host, prog, vers, proc, xdrargs, argsp,
115274462Salfred		xdrres, resp, tout, addr_ptr)
115374462Salfred	const struct netconfig *nconf;	/* Netconfig structure */
115474462Salfred	const char *host;			/* Remote host name */
115574462Salfred	rpcprog_t prog;
115674462Salfred	rpcvers_t vers;
115774462Salfred	rpcproc_t proc;			/* Remote proc identifiers */
115874462Salfred	xdrproc_t xdrargs, xdrres;	/* XDR routines */
115974462Salfred	caddr_t argsp, resp;		/* Argument and Result */
116074462Salfred	struct timeval tout;		/* Timeout value for this call */
116174462Salfred	const struct netbuf *addr_ptr;	/* Preallocated netbuf address */
116274462Salfred{
116374462Salfred	CLIENT *client;
116474462Salfred	enum clnt_stat stat;
116574462Salfred	struct r_rpcb_rmtcallargs a;
116674462Salfred	struct r_rpcb_rmtcallres r;
116774462Salfred	rpcvers_t rpcb_vers;
116874462Salfred
116979726Siedowse	stat = 0;
117074462Salfred	client = getclnthandle(host, nconf, NULL);
117174462Salfred	if (client == NULL) {
117274462Salfred		return (RPC_FAILED);
117374462Salfred	}
117474462Salfred	/*LINTED const castaway*/
117574462Salfred	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)(void *)&rmttimeout);
117674462Salfred	a.prog = prog;
117774462Salfred	a.vers = vers;
117874462Salfred	a.proc = proc;
117974462Salfred	a.args.args_val = argsp;
118074462Salfred	a.xdr_args = xdrargs;
118174462Salfred	r.addr = NULL;
118274462Salfred	r.results.results_val = resp;
118374462Salfred	r.xdr_res = xdrres;
118474462Salfred
118574462Salfred	for (rpcb_vers = RPCBVERS4; rpcb_vers >= RPCBVERS; rpcb_vers--) {
118674462Salfred		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&rpcb_vers);
118774462Salfred		stat = CLNT_CALL(client, (rpcproc_t)RPCBPROC_CALLIT,
118874462Salfred		    (xdrproc_t) xdr_rpcb_rmtcallargs, (char *)(void *)&a,
118974462Salfred		    (xdrproc_t) xdr_rpcb_rmtcallres, (char *)(void *)&r, tout);
119074462Salfred		if ((stat == RPC_SUCCESS) && (addr_ptr != NULL)) {
119174462Salfred			struct netbuf *na;
119274462Salfred			/*LINTED const castaway*/
119374462Salfred			na = uaddr2taddr((struct netconfig *) nconf, r.addr);
119474462Salfred			if (!na) {
119574462Salfred				stat = RPC_N2AXLATEFAILURE;
119674462Salfred				/*LINTED const castaway*/
119774462Salfred				((struct netbuf *) addr_ptr)->len = 0;
119874462Salfred				goto error;
119974462Salfred			}
120074462Salfred			if (na->len > addr_ptr->maxlen) {
120174462Salfred				/* Too long address */
120274462Salfred				stat = RPC_FAILED; /* XXX A better error no */
120374462Salfred				free(na->buf);
120474462Salfred				free(na);
120574462Salfred				/*LINTED const castaway*/
120674462Salfred				((struct netbuf *) addr_ptr)->len = 0;
120774462Salfred				goto error;
120874462Salfred			}
120974462Salfred			memcpy(addr_ptr->buf, na->buf, (size_t)na->len);
121074462Salfred			/*LINTED const castaway*/
121174462Salfred			((struct netbuf *)addr_ptr)->len = na->len;
121274462Salfred			free(na->buf);
121374462Salfred			free(na);
121474462Salfred			break;
121574462Salfred		} else if ((stat != RPC_PROGVERSMISMATCH) &&
121674462Salfred			    (stat != RPC_PROGUNAVAIL)) {
121774462Salfred			goto error;
121874462Salfred		}
121974462Salfred	}
122074462Salfrederror:
122174462Salfred	CLNT_DESTROY(client);
122274462Salfred	if (r.addr)
122374462Salfred		xdr_free((xdrproc_t) xdr_wrapstring, (char *)(void *)&r.addr);
122474462Salfred	return (stat);
122574462Salfred}
122674462Salfred
122774462Salfred/*
122874462Salfred * Gets the time on the remote host.
122974462Salfred * Returns 1 if succeeds else 0.
123074462Salfred */
123174462Salfredbool_t
123274462Salfredrpcb_gettime(host, timep)
123374462Salfred	const char *host;
123474462Salfred	time_t *timep;
123574462Salfred{
123674462Salfred	CLIENT *client = NULL;
123774462Salfred	void *handle;
123874462Salfred	struct netconfig *nconf;
123974462Salfred	rpcvers_t vers;
124074462Salfred	enum clnt_stat st;
124174462Salfred
124274462Salfred
1243121653Smbr	if ((host == NULL) || (host[0] == 0)) {
124474462Salfred		time(timep);
124574462Salfred		return (TRUE);
124674462Salfred	}
124774462Salfred
124874462Salfred	if ((handle = __rpc_setconf("netpath")) == NULL) {
124974462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
125074462Salfred		return (FALSE);
125174462Salfred	}
125274462Salfred	rpc_createerr.cf_stat = RPC_SUCCESS;
125374462Salfred	while (client == NULL) {
125474462Salfred		if ((nconf = __rpc_getconf(handle)) == NULL) {
125574462Salfred			if (rpc_createerr.cf_stat == RPC_SUCCESS)
125674462Salfred				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
125774462Salfred			break;
125874462Salfred		}
125974462Salfred		client = getclnthandle(host, nconf, NULL);
126074462Salfred		if (client)
126174462Salfred			break;
126274462Salfred	}
126374462Salfred	__rpc_endconf(handle);
126474462Salfred	if (client == (CLIENT *) NULL) {
126574462Salfred		return (FALSE);
126674462Salfred	}
126774462Salfred
126874462Salfred	st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
126974462Salfred		(xdrproc_t) xdr_void, NULL,
127074462Salfred		(xdrproc_t) xdr_int, (char *)(void *)timep, tottimeout);
127174462Salfred
127274462Salfred	if ((st == RPC_PROGVERSMISMATCH) || (st == RPC_PROGUNAVAIL)) {
127374462Salfred		CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
127474462Salfred		if (vers == RPCBVERS4) {
127574462Salfred			/* fall back to earlier version */
127674462Salfred			vers = RPCBVERS;
127774462Salfred			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
127874462Salfred			st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
127974462Salfred				(xdrproc_t) xdr_void, NULL,
128074462Salfred				(xdrproc_t) xdr_int, (char *)(void *)timep,
128174462Salfred				tottimeout);
128274462Salfred		}
128374462Salfred	}
128474462Salfred	CLNT_DESTROY(client);
128574462Salfred	return (st == RPC_SUCCESS? TRUE: FALSE);
128674462Salfred}
128774462Salfred
128874462Salfred/*
128974462Salfred * Converts taddr to universal address.  This routine should never
129074462Salfred * really be called because local n2a libraries are always provided.
129174462Salfred */
129274462Salfredchar *
129374462Salfredrpcb_taddr2uaddr(nconf, taddr)
129474462Salfred	struct netconfig *nconf;
129574462Salfred	struct netbuf *taddr;
129674462Salfred{
129774462Salfred	CLIENT *client;
129874462Salfred	char *uaddr = NULL;
129974462Salfred
130074462Salfred
130174462Salfred	/* parameter checking */
130274462Salfred	if (nconf == NULL) {
130374462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
130474462Salfred		return (NULL);
130574462Salfred	}
130674462Salfred	if (taddr == NULL) {
130774462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
130874462Salfred		return (NULL);
130974462Salfred	}
131074462Salfred	client = local_rpcb();
131174462Salfred	if (! client) {
131274462Salfred		return (NULL);
131374462Salfred	}
131474462Salfred
131574462Salfred	CLNT_CALL(client, (rpcproc_t)RPCBPROC_TADDR2UADDR,
131674462Salfred	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
131774462Salfred	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr, tottimeout);
131874462Salfred	CLNT_DESTROY(client);
131974462Salfred	return (uaddr);
132074462Salfred}
132174462Salfred
132274462Salfred/*
132374462Salfred * Converts universal address to netbuf.  This routine should never
132474462Salfred * really be called because local n2a libraries are always provided.
132574462Salfred */
132674462Salfredstruct netbuf *
132774462Salfredrpcb_uaddr2taddr(nconf, uaddr)
132874462Salfred	struct netconfig *nconf;
132974462Salfred	char *uaddr;
133074462Salfred{
133174462Salfred	CLIENT *client;
133274462Salfred	struct netbuf *taddr;
133374462Salfred
133474462Salfred
133574462Salfred	/* parameter checking */
133674462Salfred	if (nconf == NULL) {
133774462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
133874462Salfred		return (NULL);
133974462Salfred	}
134074462Salfred	if (uaddr == NULL) {
134174462Salfred		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
134274462Salfred		return (NULL);
134374462Salfred	}
134474462Salfred	client = local_rpcb();
134574462Salfred	if (! client) {
134674462Salfred		return (NULL);
134774462Salfred	}
134874462Salfred
134974462Salfred	taddr = (struct netbuf *)calloc(1, sizeof (struct netbuf));
135074462Salfred	if (taddr == NULL) {
135174462Salfred		CLNT_DESTROY(client);
135274462Salfred		return (NULL);
135374462Salfred	}
135474462Salfred	if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_UADDR2TADDR,
135574462Salfred	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr,
135674462Salfred	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
135774462Salfred	    tottimeout) != RPC_SUCCESS) {
135874462Salfred		free(taddr);
135974462Salfred		taddr = NULL;
136074462Salfred	}
136174462Salfred	CLNT_DESTROY(client);
136274462Salfred	return (taddr);
136374462Salfred}
1364