rpcb_clnt.c revision 241007
167468Snon/*	$NetBSD: rpcb_clnt.c,v 1.6 2000/07/16 06:41:43 itojun Exp $	*/
279697Snon
367468Snon/*
467468Snon * The contents of this file are subject to the Sun Standards
567468Snon * License Version 1.0 the (the "License";) You may not use
679697Snon * this file except in compliance with the License.  You may
779697Snon * obtain a copy of the License at lib/libc/rpc/LICENSE
879697Snon *
979697Snon * Software distributed under the License is distributed on
1079697Snon * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
1179697Snon * express or implied.  See the License for the specific
1279697Snon * language governing rights and limitations under the License.
1379697Snon *
1467468Snon * The Original Code is Copyright 1998 by Sun Microsystems, Inc
1567468Snon *
1679697Snon * The Initial Developer of the Original Code is:  Sun
1767468Snon * Microsystems, Inc.
1879697Snon *
1979697Snon * All Rights Reserved.
2079697Snon *
2179697Snon * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
2267468Snon * unrestricted use provided that this legend is included on all tape
2367468Snon * media and as a part of the software program in whole or part.  Users
2479697Snon * may copy or modify Sun RPC without charge, but are not authorized
2567468Snon * to license or distribute it to anyone else except as part of a product or
2679697Snon * program developed by the user.
2767468Snon *
2879697Snon * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
2979697Snon * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
3079697Snon * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
3179697Snon *
3279697Snon * Sun RPC is provided with no support and without any obligation on the
3367468Snon * part of Sun Microsystems, Inc. to assist in its use, correction,
3467468Snon * modification or enhancement.
3567468Snon *
3667468Snon * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
3767468Snon * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
3867468Snon * OR ANY PART THEREOF.
3967468Snon *
4067468Snon * In no event will Sun Microsystems, Inc. be liable for any lost revenue
4167468Snon * or profits or other special, indirect and consequential damages, even if
4267468Snon * Sun has been advised of the possibility of such damages.
4367468Snon *
4467468Snon * Sun Microsystems, Inc.
4567468Snon * 2550 Garcia Avenue
4667468Snon * Mountain View, California  94043
4767468Snon */
4867468Snon/*
4967468Snon * Copyright (c) 1986-1991 by Sun Microsystems Inc.
5067468Snon */
5167468Snon
5267468Snon/* #ident	"@(#)rpcb_clnt.c	1.27	94/04/24 SMI" */
5367468Snon
5467468Snon
5567468Snon#if defined(LIBC_SCCS) && !defined(lint)
5667468Snonstatic char sccsid[] = "@(#)rpcb_clnt.c 1.30 89/06/21 Copyr 1988 Sun Micro";
5767468Snon#endif
5867468Snon#include <sys/cdefs.h>
5967468Snon__FBSDID("$FreeBSD: head/sys/rpc/rpcb_clnt.c 241007 2012-09-27 19:10:25Z pfg $");
6067468Snon
6167468Snon/*
6279697Snon * rpcb_clnt.c
6379697Snon * interface to rpcbind rpc service.
6479697Snon *
6567468Snon * Copyright (C) 1988, Sun Microsystems, Inc.
6667468Snon */
6767468Snon
6867468Snon#include "opt_inet6.h"
6967468Snon
7067468Snon#include <sys/param.h>
7179697Snon#include <sys/systm.h>
7273025Snon#include <sys/kernel.h>
7373025Snon#include <sys/malloc.h>
7467468Snon#include <sys/proc.h>
7579697Snon#include <sys/socket.h>
7679697Snon#include <sys/socketvar.h>
7773025Snon
7869979Snon#include <rpc/rpc.h>
7979697Snon#include <rpc/rpcb_clnt.h>
8079697Snon#include <rpc/rpcb_prot.h>
8167468Snon
8267468Snon#include <rpc/rpc_com.h>
8367468Snon
8467468Snonstatic struct timeval tottimeout = { 60, 0 };
8567468Snonstatic const struct timeval rmttimeout = { 3, 0 };
8679697Snonstatic const char nullstring[] = "\000";
8779697Snon
8867468Snonstatic CLIENT *local_rpcb(void);
8967468Snon
9067468Snon#if 0
9167468Snon
9267468Snonstatic struct timeval rpcbrmttime = { 15, 0 };
9367468Snon
9467468Snon#define	CACHESIZE 6
9567468Snon
9667468Snonstruct address_cache {
9767468Snon	char *ac_host;
9867468Snon	char *ac_netid;
9967468Snon	char *ac_uaddr;
10067468Snon	struct netbuf *ac_taddr;
10179697Snon	struct address_cache *ac_next;
10267468Snon};
10367468Snon
10479697Snonstatic struct address_cache *front;
10579697Snonstatic int cachesize;
10667468Snon
10767468Snon#define	CLCR_GET_RPCB_TIMEOUT	1
10867468Snon#define	CLCR_SET_RPCB_TIMEOUT	2
10967468Snon
11067468Snon
11167468Snonextern int __rpc_lowvers;
11267468Snon
11367468Snonstatic struct address_cache *check_cache(const char *, const char *);
11473025Snonstatic void delete_cache(struct netbuf *);
11567468Snonstatic void add_cache(const char *, const char *, struct netbuf *, char *);
11667468Snonstatic CLIENT *getclnthandle(const char *, const struct netconfig *, char **);
11767468Snonstatic CLIENT *local_rpcb(void);
11867468Snonstatic struct netbuf *got_entry(rpcb_entry_list_ptr, const struct netconfig *);
11979697Snon
12067468Snon/*
12179697Snon * This routine adjusts the timeout used for calls to the remote rpcbind.
12279697Snon * Also, this routine can be used to set the use of portmapper version 2
12379697Snon * only when doing rpc_broadcasts
12479697Snon * These are private routines that may not be provided in future releases.
12567468Snon */
12679697Snonbool_t
12779697Snon__rpc_control(request, info)
12879697Snon	int	request;
12979697Snon	void	*info;
13067468Snon{
13167468Snon	switch (request) {
13267468Snon	case CLCR_GET_RPCB_TIMEOUT:
13379697Snon		*(struct timeval *)info = tottimeout;
13479697Snon		break;
13579697Snon	case CLCR_SET_RPCB_TIMEOUT:
13679697Snon		tottimeout = *(struct timeval *)info;
13779697Snon		break;
13879697Snon	case CLCR_SET_LOWVERS:
13979697Snon		__rpc_lowvers = *(int *)info;
14079697Snon		break;
14179697Snon	case CLCR_GET_LOWVERS:
14279697Snon		*(int *)info = __rpc_lowvers;
14379697Snon		break;
14479697Snon	default:
14579697Snon		return (FALSE);
14679697Snon	}
14779697Snon	return (TRUE);
14879697Snon}
14967468Snon
15067468Snon/*
15167468Snon *	It might seem that a reader/writer lock would be more reasonable here.
15279697Snon *	However because getclnthandle(), the only user of the cache functions,
15379697Snon *	may do a delete_cache() operation if a check_cache() fails to return an
15467468Snon *	address useful to clnt_tli_create(), we may as well use a mutex.
15567468Snon */
15679697Snon/*
15779697Snon * As it turns out, if the cache lock is *not* a reader/writer lock, we will
15879697Snon * block all clnt_create's if we are trying to connect to a host that's down,
15979697Snon * since the lock will be held all during that time.
16067468Snon */
16179697Snon
16279697Snon/*
16367468Snon * The routines check_cache(), add_cache(), delete_cache() manage the
16467468Snon * cache of rpcbind addresses for (host, netid).
16567468Snon */
16667468Snon
16779697Snonstatic struct address_cache *
16879697Snoncheck_cache(host, netid)
16979697Snon	const char *host, *netid;
17079697Snon{
17179697Snon	struct address_cache *cptr;
17279697Snon
17379697Snon	/* READ LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
17479697Snon
17579697Snon	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
17679697Snon		if (!strcmp(cptr->ac_host, host) &&
17779697Snon		    !strcmp(cptr->ac_netid, netid)) {
17879697Snon#ifdef ND_DEBUG
17979697Snon			fprintf(stderr, "Found cache entry for %s: %s\n",
18079697Snon				host, netid);
18179697Snon#endif
18279697Snon			return (cptr);
18379697Snon		}
18479697Snon	}
18579697Snon	return ((struct address_cache *) NULL);
18679697Snon}
18779697Snon
18879697Snonstatic void
18979697Snondelete_cache(addr)
19079697Snon	struct netbuf *addr;
19179697Snon{
19279697Snon	struct address_cache *cptr, *prevptr = NULL;
19379697Snon
19467468Snon	/* WRITE LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
19589113Smsmith	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
19667468Snon		if (!memcmp(cptr->ac_taddr->buf, addr->buf, addr->len)) {
19767468Snon			free(cptr->ac_host);
19867468Snon			free(cptr->ac_netid);
19967468Snon			free(cptr->ac_taddr->buf);
20067468Snon			free(cptr->ac_taddr);
20167468Snon			if (cptr->ac_uaddr)
20267468Snon				free(cptr->ac_uaddr);
20379697Snon			if (prevptr)
20479697Snon				prevptr->ac_next = cptr->ac_next;
20579697Snon			else
20679697Snon				front = cptr->ac_next;
20779697Snon			free(cptr);
20879697Snon			cachesize--;
20979697Snon			break;
21079697Snon		}
21179697Snon		prevptr = cptr;
21279697Snon	}
21379697Snon}
21479697Snon
21579697Snonstatic void
21679697Snonadd_cache(host, netid, taddr, uaddr)
21779697Snon	const char *host, *netid;
21879697Snon	char *uaddr;
21979697Snon	struct netbuf *taddr;
22079697Snon{
22179697Snon	struct address_cache  *ad_cache, *cptr, *prevptr;
22279697Snon
22379697Snon	ad_cache = (struct address_cache *)
22479697Snon			malloc(sizeof (struct address_cache));
22579697Snon	if (!ad_cache) {
22679697Snon		return;
22779697Snon	}
22879697Snon	ad_cache->ac_host = strdup(host);
22967468Snon	ad_cache->ac_netid = strdup(netid);
23079697Snon	ad_cache->ac_uaddr = uaddr ? strdup(uaddr) : NULL;
23179697Snon	ad_cache->ac_taddr = (struct netbuf *)malloc(sizeof (struct netbuf));
23279697Snon	if (!ad_cache->ac_host || !ad_cache->ac_netid || !ad_cache->ac_taddr ||
23379697Snon		(uaddr && !ad_cache->ac_uaddr)) {
23479697Snon		goto out;
23579697Snon	}
23679697Snon	ad_cache->ac_taddr->len = ad_cache->ac_taddr->maxlen = taddr->len;
23779697Snon	ad_cache->ac_taddr->buf = (char *) malloc(taddr->len);
23879697Snon	if (ad_cache->ac_taddr->buf == NULL) {
23979697Snonout:
24079697Snon		if (ad_cache->ac_host)
24179697Snon			free(ad_cache->ac_host);
24279697Snon		if (ad_cache->ac_netid)
24379697Snon			free(ad_cache->ac_netid);
24479697Snon		if (ad_cache->ac_uaddr)
24579697Snon			free(ad_cache->ac_uaddr);
24679697Snon		if (ad_cache->ac_taddr)
24779697Snon			free(ad_cache->ac_taddr);
24879697Snon		free(ad_cache);
24979697Snon		return;
25079697Snon	}
25179697Snon	memcpy(ad_cache->ac_taddr->buf, taddr->buf, taddr->len);
25279697Snon#ifdef ND_DEBUG
25379697Snon	fprintf(stderr, "Added to cache: %s : %s\n", host, netid);
25479697Snon#endif
25579697Snon
25679697Snon/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  cptr */
25779697Snon
25879697Snon	rwlock_wrlock(&rpcbaddr_cache_lock);
25979697Snon	if (cachesize < CACHESIZE) {
26079697Snon		ad_cache->ac_next = front;
26179697Snon		front = ad_cache;
26279697Snon		cachesize++;
26379697Snon	} else {
26479697Snon		/* Free the last entry */
26579697Snon		cptr = front;
26679697Snon		prevptr = NULL;
26779697Snon		while (cptr->ac_next) {
26879697Snon			prevptr = cptr;
26979697Snon			cptr = cptr->ac_next;
27079697Snon		}
27179697Snon
27279697Snon#ifdef ND_DEBUG
27379697Snon		fprintf(stderr, "Deleted from cache: %s : %s\n",
27479697Snon			cptr->ac_host, cptr->ac_netid);
27579697Snon#endif
27679697Snon		free(cptr->ac_host);
27779697Snon		free(cptr->ac_netid);
27879697Snon		free(cptr->ac_taddr->buf);
27979697Snon		free(cptr->ac_taddr);
28079697Snon		if (cptr->ac_uaddr)
28179697Snon			free(cptr->ac_uaddr);
28279697Snon
28379697Snon		if (prevptr) {
28479697Snon			prevptr->ac_next = NULL;
28579697Snon			ad_cache->ac_next = front;
28679697Snon			front = ad_cache;
28779697Snon		} else {
28879697Snon			front = ad_cache;
28979697Snon			ad_cache->ac_next = NULL;
29079697Snon		}
29179697Snon		free(cptr);
29279697Snon	}
29379697Snon	rwlock_unlock(&rpcbaddr_cache_lock);
29479697Snon}
29579697Snon
29679697Snon/*
29779697Snon * This routine will return a client handle that is connected to the
29879697Snon * rpcbind. If targaddr is non-NULL, the "universal address" of the
29979697Snon * host will be stored in *targaddr; the caller is responsible for
30079697Snon * freeing this string.
30179697Snon * On error, returns NULL and free's everything.
30279697Snon */
30379697Snonstatic CLIENT *
30479697Snongetclnthandle(host, nconf, targaddr)
30579697Snon	const char *host;
30679697Snon	const struct netconfig *nconf;
30779697Snon	char **targaddr;
30879697Snon{
30979697Snon	CLIENT *client;
31079697Snon	struct netbuf *addr, taddr;
31179697Snon	struct netbuf addr_to_delete;
31279697Snon	struct __rpc_sockinfo si;
31379697Snon	struct addrinfo hints, *res, *tres;
31479697Snon	struct address_cache *ad_cache;
31579697Snon	char *tmpaddr;
31679697Snon
31779697Snon/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  ad_cache */
31879697Snon
31979697Snon	/* Get the address of the rpcbind.  Check cache first */
32079697Snon	client = NULL;
32179697Snon	addr_to_delete.len = 0;
32279697Snon	rwlock_rdlock(&rpcbaddr_cache_lock);
32379697Snon	ad_cache = NULL;
32479697Snon	if (host != NULL)
32579697Snon		ad_cache = check_cache(host, nconf->nc_netid);
32679697Snon	if (ad_cache != NULL) {
32779697Snon		addr = ad_cache->ac_taddr;
32879697Snon		client = clnt_tli_create(RPC_ANYFD, nconf, addr,
32979697Snon		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
33079697Snon		if (client != NULL) {
33179697Snon			if (targaddr)
33279697Snon				*targaddr = strdup(ad_cache->ac_uaddr);
33379697Snon			rwlock_unlock(&rpcbaddr_cache_lock);
33479697Snon			return (client);
33579697Snon		}
33679697Snon		addr_to_delete.len = addr->len;
33779697Snon		addr_to_delete.buf = (char *)malloc(addr->len);
33879697Snon		if (addr_to_delete.buf == NULL) {
33979697Snon			addr_to_delete.len = 0;
34079697Snon		} else {
34179697Snon			memcpy(addr_to_delete.buf, addr->buf, addr->len);
34279697Snon		}
34379697Snon	}
34479697Snon	rwlock_unlock(&rpcbaddr_cache_lock);
34579697Snon	if (addr_to_delete.len != 0) {
34679697Snon		/*
34779697Snon		 * Assume this may be due to cache data being
34879697Snon		 *  outdated
34979697Snon		 */
35079697Snon		rwlock_wrlock(&rpcbaddr_cache_lock);
35179697Snon		delete_cache(&addr_to_delete);
35279697Snon		rwlock_unlock(&rpcbaddr_cache_lock);
35379697Snon		free(addr_to_delete.buf);
35479697Snon	}
35579697Snon	if (!__rpc_nconf2sockinfo(nconf, &si)) {
35679697Snon		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
35779697Snon		return NULL;
35879697Snon	}
35979697Snon
36079697Snon	memset(&hints, 0, sizeof hints);
36179697Snon	hints.ai_family = si.si_af;
36279697Snon	hints.ai_socktype = si.si_socktype;
36379697Snon	hints.ai_protocol = si.si_proto;
36479697Snon
36579697Snon#ifdef CLNT_DEBUG
36679697Snon	printf("trying netid %s family %d proto %d socktype %d\n",
36779697Snon	    nconf->nc_netid, si.si_af, si.si_proto, si.si_socktype);
36879697Snon#endif
36979697Snon
37079697Snon	if (nconf->nc_protofmly != NULL && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
37179697Snon		client = local_rpcb();
37279697Snon		if (! client) {
37379697Snon#ifdef ND_DEBUG
37479697Snon			clnt_pcreateerror("rpcbind clnt interface");
37579697Snon#endif
37679697Snon			return (NULL);
37779697Snon		} else {
37879697Snon			struct sockaddr_un sun;
37979697Snon			if (targaddr) {
38079697Snon			    *targaddr = malloc(sizeof(sun.sun_path));
38179697Snon			    if (*targaddr == NULL) {
38279697Snon				CLNT_DESTROY(client);
38379697Snon				return (NULL);
38479697Snon			    }
38579697Snon			    strncpy(*targaddr, _PATH_RPCBINDSOCK,
38679697Snon				sizeof(sun.sun_path));
38779697Snon			}
38879697Snon			return (client);
38979697Snon		}
39079697Snon	} else {
39179697Snon		if (getaddrinfo(host, "sunrpc", &hints, &res) != 0) {
39279697Snon			rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
39379697Snon			return NULL;
39479697Snon		}
39579697Snon	}
39679697Snon
39779697Snon	for (tres = res; tres != NULL; tres = tres->ai_next) {
39879697Snon		taddr.buf = tres->ai_addr;
39979697Snon		taddr.len = taddr.maxlen = tres->ai_addrlen;
40079697Snon
40179697Snon#ifdef ND_DEBUG
40279697Snon		{
40379697Snon			char *ua;
40479697Snon
40579697Snon			ua = taddr2uaddr(nconf, &taddr);
40679697Snon			fprintf(stderr, "Got it [%s]\n", ua);
40779697Snon			free(ua);
40879697Snon		}
40979697Snon#endif
41079697Snon
41179697Snon#ifdef ND_DEBUG
41279697Snon		{
41379697Snon			int i;
41479697Snon
41579697Snon			fprintf(stderr, "\tnetbuf len = %d, maxlen = %d\n",
41679697Snon				taddr.len, taddr.maxlen);
41779697Snon			fprintf(stderr, "\tAddress is ");
41879697Snon			for (i = 0; i < taddr.len; i++)
41979697Snon				fprintf(stderr, "%u.", ((char *)(taddr.buf))[i]);
42079697Snon			fprintf(stderr, "\n");
42179697Snon		}
42279697Snon#endif
42379697Snon		client = clnt_tli_create(RPC_ANYFD, nconf, &taddr,
42479697Snon		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
42579697Snon#ifdef ND_DEBUG
42679697Snon		if (! client) {
42779697Snon			clnt_pcreateerror("rpcbind clnt interface");
42879697Snon		}
42979697Snon#endif
43079697Snon
43179697Snon		if (client) {
43279697Snon			tmpaddr = targaddr ? taddr2uaddr(nconf, &taddr) : NULL;
43379697Snon			add_cache(host, nconf->nc_netid, &taddr, tmpaddr);
43479697Snon			if (targaddr)
43579697Snon				*targaddr = tmpaddr;
43679697Snon			break;
43779697Snon		}
43879697Snon	}
43979697Snon	if (res)
44079697Snon		freeaddrinfo(res);
44179697Snon	return (client);
44279697Snon}
44379697Snon
44479697Snon#endif
44579697Snon
44679697Snon/* XXX */
44779697Snon#define IN4_LOCALHOST_STRING	"127.0.0.1"
44879697Snon#define IN6_LOCALHOST_STRING	"::1"
44979697Snon
45079697Snon/*
45179697Snon * This routine will return a client handle that is connected to the local
45279697Snon * rpcbind. Returns NULL on error and free's everything.
45379697Snon */
45479697Snonstatic CLIENT *
45579697Snonlocal_rpcb()
45679697Snon{
45779697Snon	CLIENT *client;
45879697Snon	struct socket *so;
45979697Snon	size_t tsize;
46079697Snon	struct sockaddr_un sun;
46179697Snon	int error;
46279697Snon
46379697Snon	/*
46479697Snon	 * Try connecting to the local rpcbind through a local socket
46579697Snon	 * first. If this doesn't work, try all transports defined in
46679697Snon	 * the netconfig file.
46779697Snon	 */
46879697Snon	memset(&sun, 0, sizeof sun);
46979697Snon	so = NULL;
47079697Snon	error = socreate(AF_LOCAL, &so, SOCK_STREAM, 0, curthread->td_ucred,
47179697Snon	    curthread);
47279697Snon	if (error)
47379697Snon		goto try_nconf;
47479697Snon	sun.sun_family = AF_LOCAL;
47579697Snon	strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
47679697Snon	sun.sun_len = SUN_LEN(&sun);
47779697Snon
47879697Snon	tsize = __rpc_get_t_size(AF_LOCAL, 0, 0);
47979697Snon	client = clnt_vc_create(so, (struct sockaddr *)&sun, (rpcprog_t)RPCBPROG,
48079697Snon	    (rpcvers_t)RPCBVERS, tsize, tsize, 1);
48179697Snon
48279697Snon	if (client != NULL) {
48379697Snon		/* Mark the socket to be closed in destructor */
48479697Snon		(void) CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
48579697Snon		return client;
48679697Snon	}
48779697Snon
48879697Snon	/* Nobody needs this socket anymore; free the descriptor. */
48979697Snon	soclose(so);
49079697Snon
49179697Snontry_nconf:
49279697Snon
49379697Snon#if 0
49479697Snon	static struct netconfig *loopnconf;
49579697Snon	static char *localhostname;
49679697Snon
49779697Snon/* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */
49879697Snon	mutex_lock(&loopnconf_lock);
49979697Snon	if (loopnconf == NULL) {
50079697Snon		struct netconfig *nconf, *tmpnconf = NULL;
50179697Snon		void *nc_handle;
50279697Snon		int fd;
50379697Snon
50479697Snon		nc_handle = setnetconfig();
50579697Snon		if (nc_handle == NULL) {
50679697Snon			/* fails to open netconfig file */
50779697Snon			syslog (LOG_ERR, "rpc: failed to open " NETCONFIG);
50879697Snon			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
50979697Snon			mutex_unlock(&loopnconf_lock);
51079697Snon			return (NULL);
51179697Snon		}
51279697Snon		while ((nconf = getnetconfig(nc_handle)) != NULL) {
51379697Snon			if ((
51479697Snon#ifdef INET6
51579697Snon			     strcmp(nconf->nc_protofmly, NC_INET6) == 0 ||
51679697Snon#endif
51779697Snon			     strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
51879697Snon			    (nconf->nc_semantics == NC_TPI_COTS ||
51979697Snon			     nconf->nc_semantics == NC_TPI_COTS_ORD)) {
52079697Snon				fd = __rpc_nconf2fd(nconf);
52179697Snon				/*
52279697Snon				 * Can't create a socket, assume that
52379697Snon				 * this family isn't configured in the kernel.
52479697Snon				 */
52579697Snon				if (fd < 0)
52679697Snon					continue;
52779697Snon				_close(fd);
52879697Snon				tmpnconf = nconf;
52979697Snon				if (!strcmp(nconf->nc_protofmly, NC_INET))
53079697Snon					localhostname = IN4_LOCALHOST_STRING;
53179697Snon				else
53279697Snon					localhostname = IN6_LOCALHOST_STRING;
53379697Snon			}
53479697Snon		}
53579697Snon		if (tmpnconf == NULL) {
53679697Snon			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
53779697Snon			mutex_unlock(&loopnconf_lock);
53879697Snon			return (NULL);
53979697Snon		}
54079697Snon		loopnconf = getnetconfigent(tmpnconf->nc_netid);
54179697Snon		/* loopnconf is never freed */
54279697Snon		endnetconfig(nc_handle);
54379697Snon	}
54479697Snon	mutex_unlock(&loopnconf_lock);
54579697Snon	client = getclnthandle(localhostname, loopnconf, NULL);
54679697Snon	return (client);
54779697Snon#else
54879697Snon	return (NULL);
54979697Snon#endif
55079697Snon}
55179697Snon
55279697Snon/*
55379697Snon * Set a mapping between program, version and address.
55479697Snon * Calls the rpcbind service to do the mapping.
55579697Snon */
55679697Snonbool_t
55779697Snonrpcb_set(rpcprog_t program, rpcvers_t version,
55879697Snon    const struct netconfig *nconf,	/* Network structure of transport */
55979697Snon    const struct netbuf *address)	/* Services netconfig address */
56079697Snon{
56179697Snon	CLIENT *client;
56279697Snon	bool_t rslt = FALSE;
56379697Snon	RPCB parms;
56479697Snon#if 0
56579697Snon	char uidbuf[32];
56679697Snon#endif
56779697Snon	struct netconfig nconfcopy;
56879697Snon	struct netbuf addresscopy;
56979697Snon
57079697Snon	/* parameter checking */
57179697Snon	if (nconf == NULL) {
57279697Snon		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
57379697Snon		return (FALSE);
57479697Snon	}
57579697Snon	if (address == NULL) {
57679697Snon		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
57779697Snon		return (FALSE);
57879697Snon	}
57979697Snon	client = local_rpcb();
58079697Snon	if (! client) {
58179697Snon		return (FALSE);
58279697Snon	}
58379697Snon
58479697Snon	/* convert to universal */
58579697Snon	/*LINTED const castaway*/
58679697Snon	nconfcopy = *nconf;
58779697Snon	addresscopy = *address;
58879697Snon	parms.r_addr = taddr2uaddr(&nconfcopy, &addresscopy);
58979697Snon	if (!parms.r_addr) {
59079697Snon		CLNT_DESTROY(client);
59179697Snon		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
59279697Snon		return (FALSE); /* no universal address */
59379697Snon	}
59479697Snon	parms.r_prog = program;
59579697Snon	parms.r_vers = version;
59679697Snon	parms.r_netid = nconf->nc_netid;
59779697Snon#if 0
59879697Snon	/*
59979697Snon	 * Though uid is not being used directly, we still send it for
60079697Snon	 * completeness.  For non-unix platforms, perhaps some other
60179697Snon	 * string or an empty string can be sent.
60279697Snon	 */
60379697Snon	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
60479697Snon	parms.r_owner = uidbuf;
60579697Snon#else
60679697Snon	parms.r_owner = "";
60779697Snon#endif
60879697Snon
60979697Snon	CLNT_CALL(client, (rpcproc_t)RPCBPROC_SET, (xdrproc_t) xdr_rpcb,
61079697Snon	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
61179697Snon	    (char *)(void *)&rslt, tottimeout);
61279697Snon
61379697Snon	CLNT_DESTROY(client);
61479697Snon	free(parms.r_addr, M_RPC);
61579697Snon	return (rslt);
61679697Snon}
61779697Snon
61879697Snon/*
61979697Snon * Remove the mapping between program, version and netbuf address.
62079697Snon * Calls the rpcbind service to do the un-mapping.
62179697Snon * If netbuf is NULL, unset for all the transports, otherwise unset
62279697Snon * only for the given transport.
62379697Snon */
62479697Snonbool_t
62579697Snonrpcb_unset(rpcprog_t program, rpcvers_t version, const struct netconfig *nconf)
62679697Snon{
62779697Snon	CLIENT *client;
62879697Snon	bool_t rslt = FALSE;
62979697Snon	RPCB parms;
63079697Snon#if 0
63179697Snon	char uidbuf[32];
63279697Snon#endif
63379697Snon
63479697Snon	client = local_rpcb();
63579697Snon	if (! client) {
63679697Snon		return (FALSE);
63779697Snon	}
63879697Snon
63979697Snon	parms.r_prog = program;
64079697Snon	parms.r_vers = version;
64179697Snon	if (nconf)
64279697Snon		parms.r_netid = nconf->nc_netid;
64379697Snon	else {
64479697Snon		/*LINTED const castaway*/
64579697Snon		parms.r_netid = (char *)(uintptr_t) &nullstring[0]; /* unsets  all */
64679697Snon	}
64779697Snon	/*LINTED const castaway*/
64879697Snon	parms.r_addr = (char *)(uintptr_t) &nullstring[0];
64979697Snon#if 0
65079697Snon	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
65179697Snon	parms.r_owner = uidbuf;
65279697Snon#else
65379697Snon	parms.r_owner = "";
65479697Snon#endif
65579697Snon
65679697Snon	CLNT_CALL(client, (rpcproc_t)RPCBPROC_UNSET, (xdrproc_t) xdr_rpcb,
65779697Snon	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
65879697Snon	    (char *)(void *)&rslt, tottimeout);
65979697Snon
66079697Snon	CLNT_DESTROY(client);
66179697Snon	return (rslt);
66279697Snon}
66379697Snon
66479697Snon#if 0
66579697Snon
66679697Snon/*
66779697Snon * From the merged list, find the appropriate entry
66879697Snon */
66979697Snonstatic struct netbuf *
67079697Snongot_entry(relp, nconf)
67179697Snon	rpcb_entry_list_ptr relp;
67279697Snon	const struct netconfig *nconf;
67379697Snon{
67479697Snon	struct netbuf *na = NULL;
67579697Snon	rpcb_entry_list_ptr sp;
67679697Snon	rpcb_entry *rmap;
67779697Snon
67879697Snon	for (sp = relp; sp != NULL; sp = sp->rpcb_entry_next) {
67979697Snon		rmap = &sp->rpcb_entry_map;
68079697Snon		if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) &&
68179697Snon		    (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) &&
68279697Snon		    (nconf->nc_semantics == rmap->r_nc_semantics) &&
68379697Snon		    (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != 0)) {
68479697Snon			na = uaddr2taddr(nconf, rmap->r_maddr);
68579697Snon#ifdef ND_DEBUG
68679697Snon			fprintf(stderr, "\tRemote address is [%s].\n",
68779697Snon				rmap->r_maddr);
68879697Snon			if (!na)
68979697Snon				fprintf(stderr,
69079697Snon				    "\tCouldn't resolve remote address!\n");
69179697Snon#endif
69279697Snon			break;
69379697Snon		}
69479697Snon	}
69579697Snon	return (na);
69679697Snon}
69779697Snon
69879697Snon/*
69979697Snon * Quick check to see if rpcbind is up.  Tries to connect over
70079697Snon * local transport.
70179697Snon */
70279697Snonstatic bool_t
70379697Snon__rpcbind_is_up()
70479697Snon{
70579697Snon	struct netconfig *nconf;
70679697Snon	struct sockaddr_un sun;
70779697Snon	void *localhandle;
70879697Snon	int sock;
70979697Snon
71079697Snon	nconf = NULL;
71179697Snon	localhandle = setnetconfig();
71279697Snon	while ((nconf = getnetconfig(localhandle)) != NULL) {
71379697Snon		if (nconf->nc_protofmly != NULL &&
71479697Snon		    strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)
71579697Snon			 break;
71679697Snon	}
71779697Snon	if (nconf == NULL)
71879697Snon		return (FALSE);
71979697Snon
72079697Snon	endnetconfig(localhandle);
72179697Snon
72279697Snon	memset(&sun, 0, sizeof sun);
72379697Snon	sock = _socket(AF_LOCAL, SOCK_STREAM, 0);
72479697Snon	if (sock < 0)
72579697Snon		return (FALSE);
72679697Snon	sun.sun_family = AF_LOCAL;
72779697Snon	strncpy(sun.sun_path, _PATH_RPCBINDSOCK, sizeof(sun.sun_path));
72879697Snon	sun.sun_len = SUN_LEN(&sun);
72979697Snon
73079697Snon	if (_connect(sock, (struct sockaddr *)&sun, sun.sun_len) < 0) {
73179697Snon		_close(sock);
73279697Snon		return (FALSE);
73379697Snon	}
73479697Snon
73579697Snon	_close(sock);
73679697Snon	return (TRUE);
73779697Snon}
73879697Snon
73979697Snon/*
74079697Snon * An internal function which optimizes rpcb_getaddr function.  It also
74179697Snon * returns the client handle that it uses to contact the remote rpcbind.
74279697Snon *
74379697Snon * The algorithm used: If the transports is TCP or UDP, it first tries
74479697Snon * version 2 (portmap), 4 and then 3 (svr4).  This order should be
74579697Snon * changed in the next OS release to 4, 2 and 3.  We are assuming that by
74679697Snon * that time, version 4 would be available on many machines on the network.
74779697Snon * With this algorithm, we get performance as well as a plan for
74879697Snon * obsoleting version 2.
74979697Snon *
75079697Snon * For all other transports, the algorithm remains as 4 and then 3.
75179697Snon *
75279697Snon * XXX: Due to some problems with t_connect(), we do not reuse the same client
75379697Snon * handle for COTS cases and hence in these cases we do not return the
75479697Snon * client handle.  This code will change if t_connect() ever
75579697Snon * starts working properly.  Also look under clnt_vc.c.
75679697Snon */
75779697Snonstruct netbuf *
75879697Snon__rpcb_findaddr_timed(program, version, nconf, host, clpp, tp)
75979697Snon	rpcprog_t program;
76079697Snon	rpcvers_t version;
76179697Snon	const struct netconfig *nconf;
76279697Snon	const char *host;
76379697Snon	CLIENT **clpp;
76479697Snon	struct timeval *tp;
76579697Snon{
76679697Snon	static bool_t check_rpcbind = TRUE;
76779697Snon	CLIENT *client = NULL;
76879697Snon	RPCB parms;
76979697Snon	enum clnt_stat clnt_st;
77079697Snon	char *ua = NULL;
77179697Snon	rpcvers_t vers;
77279697Snon	struct netbuf *address = NULL;
77379697Snon	rpcvers_t start_vers = RPCBVERS4;
77479697Snon	struct netbuf servaddr;
77579697Snon
77679697Snon	/* parameter checking */
77779697Snon	if (nconf == NULL) {
77879697Snon		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
77979697Snon		return (NULL);
78079697Snon	}
78179697Snon
78279697Snon	parms.r_addr = NULL;
78379697Snon
78479697Snon	/*
78579697Snon	 * Use default total timeout if no timeout is specified.
78679697Snon	 */
78779697Snon	if (tp == NULL)
78879697Snon		tp = &tottimeout;
78979697Snon
79079697Snon#ifdef PORTMAP
79179697Snon	/* Try version 2 for TCP or UDP */
79279697Snon	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
79379697Snon		u_short port = 0;
79479697Snon		struct netbuf remote;
79579697Snon		rpcvers_t pmapvers = 2;
79679697Snon		struct pmap pmapparms;
79779697Snon
79879697Snon		/*
79979697Snon		 * Try UDP only - there are some portmappers out
80079697Snon		 * there that use UDP only.
80179697Snon		 */
80279697Snon		if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
80379697Snon			struct netconfig *newnconf;
80479697Snon
80579697Snon			if ((newnconf = getnetconfigent("udp")) == NULL) {
80679697Snon				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
80779697Snon				return (NULL);
80879697Snon			}
80979697Snon			client = getclnthandle(host, newnconf, &parms.r_addr);
81079697Snon			freenetconfigent(newnconf);
81179697Snon		} else {
81279697Snon			client = getclnthandle(host, nconf, &parms.r_addr);
81379697Snon		}
81479697Snon		if (client == NULL)
81579697Snon			return (NULL);
81679697Snon
81779697Snon		/*
81879697Snon		 * Set version and retry timeout.
81979697Snon		 */
82079697Snon		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
82179697Snon		CLNT_CONTROL(client, CLSET_VERS, (char *)&pmapvers);
82279697Snon
82379697Snon		pmapparms.pm_prog = program;
82479697Snon		pmapparms.pm_vers = version;
82579697Snon		pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ?
82679697Snon					IPPROTO_UDP : IPPROTO_TCP;
82779697Snon		pmapparms.pm_port = 0;	/* not needed */
82879697Snon		clnt_st = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
82979697Snon		    (xdrproc_t) xdr_pmap, (caddr_t)(void *)&pmapparms,
83079697Snon		    (xdrproc_t) xdr_u_short, (caddr_t)(void *)&port,
83179697Snon		    *tp);
83279697Snon		if (clnt_st != RPC_SUCCESS) {
83379697Snon			if ((clnt_st == RPC_PROGVERSMISMATCH) ||
83479697Snon				(clnt_st == RPC_PROGUNAVAIL))
83579697Snon				goto try_rpcbind; /* Try different versions */
83679697Snon			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
83779697Snon			clnt_geterr(client, &rpc_createerr.cf_error);
83879697Snon			goto error;
83979697Snon		} else if (port == 0) {
84079697Snon			address = NULL;
84179697Snon			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
84279697Snon			goto error;
84379697Snon		}
84479697Snon		port = htons(port);
84579697Snon		CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)&remote);
84679697Snon		if (((address = (struct netbuf *)
84779697Snon			malloc(sizeof (struct netbuf))) == NULL) ||
84879697Snon		    ((address->buf = (char *)
84979697Snon			malloc(remote.len)) == NULL)) {
85079697Snon			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
85179697Snon			clnt_geterr(client, &rpc_createerr.cf_error);
85279697Snon			if (address) {
85379697Snon				free(address);
85479697Snon				address = NULL;
85579697Snon			}
85679697Snon			goto error;
85779697Snon		}
85879697Snon		memcpy(address->buf, remote.buf, remote.len);
85979697Snon		memcpy(&((char *)address->buf)[sizeof (short)],
86079697Snon				(char *)(void *)&port, sizeof (short));
86179697Snon		address->len = address->maxlen = remote.len;
86279697Snon		goto done;
86379697Snon	}
86479697Snon#endif				/* PORTMAP */
86579697Snon
86679697Snontry_rpcbind:
86779697Snon	/*
86879697Snon	 * Check if rpcbind is up.  This prevents needless delays when
86979697Snon	 * accessing applications such as the keyserver while booting
87079697Snon	 * disklessly.
87179697Snon	 */
87279697Snon	if (check_rpcbind && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
87379697Snon		if (!__rpcbind_is_up()) {
87479697Snon			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
87579697Snon			rpc_createerr.cf_error.re_errno = 0;
87679697Snon			goto error;
87779697Snon		}
87879697Snon		check_rpcbind = FALSE;
87979697Snon	}
88079697Snon
88179697Snon	/*
88279697Snon	 * Now we try version 4 and then 3.
88379697Snon	 * We also send the remote system the address we used to
88479697Snon	 * contact it in case it can help to connect back with us
88579697Snon	 */
88679697Snon	parms.r_prog = program;
88779697Snon	parms.r_vers = version;
88879697Snon	/*LINTED const castaway*/
88979697Snon	parms.r_owner = (char *) &nullstring[0];	/* not needed; */
89079697Snon							/* just for xdring */
89179697Snon	parms.r_netid = nconf->nc_netid; /* not really needed */
89279697Snon
89379697Snon	/*
89479697Snon	 * If a COTS transport is being used, try getting address via CLTS
89579697Snon	 * transport.  This works only with version 4.
89679697Snon	 */
89779697Snon	if (nconf->nc_semantics == NC_TPI_COTS_ORD ||
89879697Snon			nconf->nc_semantics == NC_TPI_COTS) {
89979697Snon
90079697Snon		void *handle;
90179697Snon		struct netconfig *nconf_clts;
90279697Snon		rpcb_entry_list_ptr relp = NULL;
90379697Snon
90479697Snon		if (client == NULL) {
90579697Snon			/* This did not go through the above PORTMAP/TCP code */
90679697Snon			if ((handle = __rpc_setconf("datagram_v")) != NULL) {
90779697Snon				while ((nconf_clts = __rpc_getconf(handle))
90879697Snon					!= NULL) {
90979697Snon					if (strcmp(nconf_clts->nc_protofmly,
91079697Snon						nconf->nc_protofmly) != 0) {
91179697Snon						continue;
91279697Snon					}
91379697Snon					client = getclnthandle(host, nconf_clts,
91479697Snon							&parms.r_addr);
91579697Snon					break;
91679697Snon				}
91779697Snon				__rpc_endconf(handle);
91879697Snon			}
91979697Snon			if (client == NULL)
92079697Snon				goto regular_rpcbind;	/* Go the regular way */
92179697Snon		} else {
92279697Snon			/* This is a UDP PORTMAP handle.  Change to version 4 */
92379697Snon			vers = RPCBVERS4;
92479697Snon			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
92579697Snon		}
92679697Snon		/*
92779697Snon		 * We also send the remote system the address we used to
92879697Snon		 * contact it in case it can help it connect back with us
92979697Snon		 */
93079697Snon		if (parms.r_addr == NULL) {
93179697Snon			/*LINTED const castaway*/
93279697Snon			parms.r_addr = (char *) &nullstring[0]; /* for XDRing */
93379697Snon		}
93479697Snon
93579697Snon		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
93679697Snon
93779697Snon		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDRLIST,
93879697Snon		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
93979697Snon		    (xdrproc_t) xdr_rpcb_entry_list_ptr,
94079697Snon		    (char *)(void *)&relp, *tp);
94179697Snon		if (clnt_st == RPC_SUCCESS) {
94279697Snon			if ((address = got_entry(relp, nconf)) != NULL) {
94379697Snon				xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
94479697Snon				    (char *)(void *)&relp);
94579697Snon				CLNT_CONTROL(client, CLGET_SVC_ADDR,
94679697Snon					(char *)(void *)&servaddr);
94779697Snon				__rpc_fixup_addr(address, &servaddr);
94879697Snon				goto done;
94979697Snon			}
95079697Snon			/* Entry not found for this transport */
95179697Snon			xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
95279697Snon			    (char *)(void *)&relp);
95379697Snon			/*
95479697Snon			 * XXX: should have perhaps returned with error but
95579697Snon			 * since the remote machine might not always be able
95679697Snon			 * to send the address on all transports, we try the
95779697Snon			 * regular way with regular_rpcbind
95879697Snon			 */
95979697Snon			goto regular_rpcbind;
96079697Snon		} else if ((clnt_st == RPC_PROGVERSMISMATCH) ||
96179697Snon			(clnt_st == RPC_PROGUNAVAIL)) {
96279697Snon			start_vers = RPCBVERS;	/* Try version 3 now */
96379697Snon			goto regular_rpcbind; /* Try different versions */
96479697Snon		} else {
96579697Snon			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
96679697Snon			clnt_geterr(client, &rpc_createerr.cf_error);
96779697Snon			goto error;
96879697Snon		}
96979697Snon	}
97079697Snon
97179697Snonregular_rpcbind:
97279697Snon
97379697Snon	/* Now the same transport is to be used to get the address */
97479697Snon	if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
97579697Snon			(nconf->nc_semantics == NC_TPI_COTS))) {
97679697Snon		/* A CLTS type of client - destroy it */
97779697Snon		CLNT_DESTROY(client);
97879697Snon		client = NULL;
97979697Snon	}
98079697Snon
98179697Snon	if (client == NULL) {
98279697Snon		client = getclnthandle(host, nconf, &parms.r_addr);
98379697Snon		if (client == NULL) {
98479697Snon			goto error;
98579697Snon		}
98679697Snon	}
98779697Snon	if (parms.r_addr == NULL) {
98879697Snon		/*LINTED const castaway*/
98979697Snon		parms.r_addr = (char *) &nullstring[0];
99079697Snon	}
99179697Snon
99279697Snon	/* First try from start_vers and then version 3 (RPCBVERS) */
99379697Snon
99479697Snon	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *) &rpcbrmttime);
99579697Snon	for (vers = start_vers;  vers >= RPCBVERS; vers--) {
99679697Snon		/* Set the version */
99779697Snon		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
99879697Snon		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
99979697Snon		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
100079697Snon		    (xdrproc_t) xdr_wrapstring, (char *)(void *) &ua, *tp);
100179697Snon		if (clnt_st == RPC_SUCCESS) {
100279697Snon			if ((ua == NULL) || (ua[0] == 0)) {
100379697Snon				/* address unknown */
100479697Snon				rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
100579697Snon				goto error;
100679697Snon			}
100779697Snon			address = uaddr2taddr(nconf, ua);
100879697Snon#ifdef ND_DEBUG
100979697Snon			fprintf(stderr, "\tRemote address is [%s]\n", ua);
101079697Snon			if (!address)
101179697Snon				fprintf(stderr,
101279697Snon					"\tCouldn't resolve remote address!\n");
101379697Snon#endif
101479697Snon			xdr_free((xdrproc_t)xdr_wrapstring,
101579697Snon			    (char *)(void *)&ua);
101679697Snon
101779697Snon			if (! address) {
101879697Snon				/* We don't know about your universal address */
101979697Snon				rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
102079697Snon				goto error;
102179697Snon			}
102279697Snon			CLNT_CONTROL(client, CLGET_SVC_ADDR,
102379697Snon			    (char *)(void *)&servaddr);
102479697Snon			__rpc_fixup_addr(address, &servaddr);
102579697Snon			goto done;
102679697Snon		} else if (clnt_st == RPC_PROGVERSMISMATCH) {
102779697Snon			struct rpc_err rpcerr;
102879697Snon
102979697Snon			clnt_geterr(client, &rpcerr);
103079697Snon			if (rpcerr.re_vers.low > RPCBVERS4)
103179697Snon				goto error;  /* a new version, can't handle */
103279697Snon		} else if (clnt_st != RPC_PROGUNAVAIL) {
103379697Snon			/* Cant handle this error */
103479697Snon			rpc_createerr.cf_stat = clnt_st;
103579697Snon			clnt_geterr(client, &rpc_createerr.cf_error);
103679697Snon			goto error;
103779697Snon		}
103879697Snon	}
103979697Snon
104079697Snonerror:
104179697Snon	if (client) {
104279697Snon		CLNT_DESTROY(client);
104379697Snon		client = NULL;
104479697Snon	}
104579697Snondone:
104679697Snon	if (nconf->nc_semantics != NC_TPI_CLTS) {
104779697Snon		/* This client is the connectionless one */
104879697Snon		if (client) {
104979697Snon			CLNT_DESTROY(client);
105079697Snon			client = NULL;
105179697Snon		}
105279697Snon	}
105379697Snon	if (clpp) {
105479697Snon		*clpp = client;
105579697Snon	} else if (client) {
105679697Snon		CLNT_DESTROY(client);
105779697Snon	}
105879697Snon	if (parms.r_addr != NULL && parms.r_addr != nullstring)
105979697Snon		free(parms.r_addr);
106079697Snon	return (address);
106179697Snon}
106279697Snon
106379697Snon
106479697Snon/*
106579697Snon * Find the mapped address for program, version.
106679697Snon * Calls the rpcbind service remotely to do the lookup.
106779697Snon * Uses the transport specified in nconf.
106879697Snon * Returns FALSE (0) if no map exists, else returns 1.
106979697Snon *
107079697Snon * Assuming that the address is all properly allocated
107179697Snon */
107279697Snonint
107379697Snonrpcb_getaddr(program, version, nconf, address, host)
107479697Snon	rpcprog_t program;
107579697Snon	rpcvers_t version;
107679697Snon	const struct netconfig *nconf;
107779697Snon	struct netbuf *address;
107879697Snon	const char *host;
107979697Snon{
108079697Snon	struct netbuf *na;
108179697Snon
108279697Snon	if ((na = __rpcb_findaddr_timed(program, version,
108379697Snon	    (struct netconfig *) nconf, (char *) host,
108479697Snon	    (CLIENT **) NULL, (struct timeval *) NULL)) == NULL)
108579697Snon		return (FALSE);
108679697Snon
108779697Snon	if (na->len > address->maxlen) {
108879697Snon		/* Too long address */
108979697Snon		free(na->buf);
109079697Snon		free(na);
109179697Snon		rpc_createerr.cf_stat = RPC_FAILED;
109279697Snon		return (FALSE);
109379697Snon	}
109479697Snon	memcpy(address->buf, na->buf, (size_t)na->len);
109579697Snon	address->len = na->len;
109679697Snon	free(na->buf);
109779697Snon	free(na);
109879697Snon	return (TRUE);
109979697Snon}
110079697Snon
110179697Snon/*
110279697Snon * Get a copy of the current maps.
110379697Snon * Calls the rpcbind service remotely to get the maps.
110479697Snon *
110579697Snon * It returns only a list of the services
110679697Snon * It returns NULL on failure.
110779697Snon */
110879697Snonrpcblist *
110979697Snonrpcb_getmaps(nconf, host)
111079697Snon	const struct netconfig *nconf;
111179697Snon	const char *host;
111279697Snon{
111379697Snon	rpcblist_ptr head = NULL;
111479697Snon	CLIENT *client;
111579697Snon	enum clnt_stat clnt_st;
111679697Snon	rpcvers_t vers = 0;
111779697Snon
111879697Snon	client = getclnthandle(host, nconf, NULL);
111979697Snon	if (client == NULL) {
112079697Snon		return (head);
112179697Snon	}
112279697Snon	clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
112379697Snon	    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
112479697Snon	    (char *)(void *)&head, tottimeout);
112579697Snon	if (clnt_st == RPC_SUCCESS)
112679697Snon		goto done;
112779697Snon
112879697Snon	if ((clnt_st != RPC_PROGVERSMISMATCH) &&
112979697Snon	    (clnt_st != RPC_PROGUNAVAIL)) {
113079697Snon		rpc_createerr.cf_stat = RPC_RPCBFAILURE;
113179697Snon		clnt_geterr(client, &rpc_createerr.cf_error);
113279697Snon		goto done;
113379697Snon	}
113479697Snon
113579697Snon	/* fall back to earlier version */
113679697Snon	CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
113779697Snon	if (vers == RPCBVERS4) {
113879697Snon		vers = RPCBVERS;
113979697Snon		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
114079697Snon		if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
114179697Snon		    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
114279697Snon		    (char *)(void *)&head, tottimeout) == RPC_SUCCESS)
114379697Snon			goto done;
114479697Snon	}
114579697Snon	rpc_createerr.cf_stat = RPC_RPCBFAILURE;
114679697Snon	clnt_geterr(client, &rpc_createerr.cf_error);
114779697Snon
114879697Snondone:
114979697Snon	CLNT_DESTROY(client);
115079697Snon	return (head);
115179697Snon}
115279697Snon
115379697Snon/*
115479697Snon * rpcbinder remote-call-service interface.
115579697Snon * This routine is used to call the rpcbind remote call service
115679697Snon * which will look up a service program in the address maps, and then
115779697Snon * remotely call that routine with the given parameters. This allows
115879697Snon * programs to do a lookup and call in one step.
115979697Snon*/
116079697Snonenum clnt_stat
116179697Snonrpcb_rmtcall(nconf, host, prog, vers, proc, xdrargs, argsp,
116279697Snon		xdrres, resp, tout, addr_ptr)
116379697Snon	const struct netconfig *nconf;	/* Netconfig structure */
116479697Snon	const char *host;			/* Remote host name */
116579697Snon	rpcprog_t prog;
116679697Snon	rpcvers_t vers;
116779697Snon	rpcproc_t proc;			/* Remote proc identifiers */
116879697Snon	xdrproc_t xdrargs, xdrres;	/* XDR routines */
116979697Snon	caddr_t argsp, resp;		/* Argument and Result */
117079697Snon	struct timeval tout;		/* Timeout value for this call */
117179697Snon	const struct netbuf *addr_ptr;	/* Preallocated netbuf address */
117279697Snon{
117379697Snon	CLIENT *client;
117479697Snon	enum clnt_stat stat;
117579697Snon	struct r_rpcb_rmtcallargs a;
117679697Snon	struct r_rpcb_rmtcallres r;
117779697Snon	rpcvers_t rpcb_vers;
117879697Snon
117979697Snon	stat = 0;
118079697Snon	client = getclnthandle(host, nconf, NULL);
118179697Snon	if (client == NULL) {
118279697Snon		return (RPC_FAILED);
118379697Snon	}
118479697Snon	/*LINTED const castaway*/
118579697Snon	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)(void *)&rmttimeout);
118679697Snon	a.prog = prog;
118779697Snon	a.vers = vers;
118879697Snon	a.proc = proc;
118979697Snon	a.args.args_val = argsp;
119079697Snon	a.xdr_args = xdrargs;
119179697Snon	r.addr = NULL;
119279697Snon	r.results.results_val = resp;
119379697Snon	r.xdr_res = xdrres;
119479697Snon
119579697Snon	for (rpcb_vers = RPCBVERS4; rpcb_vers >= RPCBVERS; rpcb_vers--) {
119679697Snon		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&rpcb_vers);
119779697Snon		stat = CLNT_CALL(client, (rpcproc_t)RPCBPROC_CALLIT,
119879697Snon		    (xdrproc_t) xdr_rpcb_rmtcallargs, (char *)(void *)&a,
119979697Snon		    (xdrproc_t) xdr_rpcb_rmtcallres, (char *)(void *)&r, tout);
120079697Snon		if ((stat == RPC_SUCCESS) && (addr_ptr != NULL)) {
120179697Snon			struct netbuf *na;
120279697Snon			/*LINTED const castaway*/
120379697Snon			na = uaddr2taddr((struct netconfig *) nconf, r.addr);
120479697Snon			if (!na) {
120579697Snon				stat = RPC_N2AXLATEFAILURE;
120679697Snon				/*LINTED const castaway*/
120779697Snon				((struct netbuf *) addr_ptr)->len = 0;
120879697Snon				goto error;
120979697Snon			}
121079697Snon			if (na->len > addr_ptr->maxlen) {
121179697Snon				/* Too long address */
121279697Snon				stat = RPC_FAILED; /* XXX A better error no */
121379697Snon				free(na->buf);
121479697Snon				free(na);
121579697Snon				/*LINTED const castaway*/
121679697Snon				((struct netbuf *) addr_ptr)->len = 0;
121779697Snon				goto error;
121879697Snon			}
121979697Snon			memcpy(addr_ptr->buf, na->buf, (size_t)na->len);
122079697Snon			/*LINTED const castaway*/
122179697Snon			((struct netbuf *)addr_ptr)->len = na->len;
122279697Snon			free(na->buf);
122379697Snon			free(na);
122479697Snon			break;
122579697Snon		} else if ((stat != RPC_PROGVERSMISMATCH) &&
122679697Snon			    (stat != RPC_PROGUNAVAIL)) {
122779697Snon			goto error;
122879697Snon		}
122979697Snon	}
123079697Snonerror:
123179697Snon	CLNT_DESTROY(client);
123279697Snon	if (r.addr)
123379697Snon		xdr_free((xdrproc_t) xdr_wrapstring, (char *)(void *)&r.addr);
123479697Snon	return (stat);
123579697Snon}
123679697Snon
123779697Snon/*
123879697Snon * Gets the time on the remote host.
123979697Snon * Returns 1 if succeeds else 0.
124079697Snon */
124179697Snonbool_t
124279697Snonrpcb_gettime(host, timep)
124379697Snon	const char *host;
124479697Snon	time_t *timep;
124579697Snon{
124679697Snon	CLIENT *client = NULL;
124779697Snon	void *handle;
124879697Snon	struct netconfig *nconf;
124979697Snon	rpcvers_t vers;
125079697Snon	enum clnt_stat st;
125179697Snon
125279697Snon
125379697Snon	if ((host == NULL) || (host[0] == 0)) {
125479697Snon		time(timep);
125579697Snon		return (TRUE);
125679697Snon	}
125779697Snon
125879697Snon	if ((handle = __rpc_setconf("netpath")) == NULL) {
125979697Snon		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
126079697Snon		return (FALSE);
126179697Snon	}
126279697Snon	rpc_createerr.cf_stat = RPC_SUCCESS;
126379697Snon	while (client == NULL) {
126479697Snon		if ((nconf = __rpc_getconf(handle)) == NULL) {
126579697Snon			if (rpc_createerr.cf_stat == RPC_SUCCESS)
126679697Snon				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
126779697Snon			break;
126879697Snon		}
126979697Snon		client = getclnthandle(host, nconf, NULL);
127079697Snon		if (client)
127179697Snon			break;
127279697Snon	}
127379697Snon	__rpc_endconf(handle);
127479697Snon	if (client == (CLIENT *) NULL) {
127579697Snon		return (FALSE);
127679697Snon	}
127779697Snon
127879697Snon	st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
127979697Snon		(xdrproc_t) xdr_void, NULL,
128079697Snon		(xdrproc_t) xdr_int, (char *)(void *)timep, tottimeout);
128179697Snon
128279697Snon	if ((st == RPC_PROGVERSMISMATCH) || (st == RPC_PROGUNAVAIL)) {
128379697Snon		CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
128479697Snon		if (vers == RPCBVERS4) {
128579697Snon			/* fall back to earlier version */
128679697Snon			vers = RPCBVERS;
128779697Snon			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
128879697Snon			st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
128979697Snon				(xdrproc_t) xdr_void, NULL,
129079697Snon				(xdrproc_t) xdr_int, (char *)(void *)timep,
129179697Snon				tottimeout);
129279697Snon		}
129379697Snon	}
129479697Snon	CLNT_DESTROY(client);
129579697Snon	return (st == RPC_SUCCESS? TRUE: FALSE);
129679697Snon}
129779697Snon
129879697Snonstatic bool_t
129979697Snonxdr_netbuf(XDR *xdrs, struct netbuf *objp)
130079697Snon{
130179697Snon	bool_t dummy;
130279697Snon	void **pp;
130379697Snon
130479697Snon	if (!xdr_uint32_t(xdrs, (uint32_t *) &objp->maxlen)) {
130579697Snon		return (FALSE);
130679697Snon	}
130779697Snon	pp = &objp->buf;
130879697Snon	dummy = xdr_bytes(xdrs, (char **) pp,
130979697Snon			(u_int *)&(objp->len), objp->maxlen);
131079697Snon	return (dummy);
131179697Snon}
131279697Snon
131379697Snon/*
131479697Snon * Converts taddr to universal address.  This routine should never
131579697Snon * really be called because local n2a libraries are always provided.
131679697Snon */
131779697Snonchar *
131879697Snonrpcb_taddr2uaddr(struct netconfig *nconf, struct netbuf *taddr)
131979697Snon{
132079697Snon	CLIENT *client;
132179697Snon	char *uaddr = NULL;
132279697Snon
132379697Snon
132479697Snon	/* parameter checking */
132579697Snon	if (nconf == NULL) {
132679697Snon		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
132779697Snon		return (NULL);
132879697Snon	}
132979697Snon	if (taddr == NULL) {
133079697Snon		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
133179697Snon		return (NULL);
133279697Snon	}
133379697Snon	client = local_rpcb();
133479697Snon	if (! client) {
133579697Snon		return (NULL);
133679697Snon	}
133779697Snon
133879697Snon	CLNT_CALL(client, (rpcproc_t)RPCBPROC_TADDR2UADDR,
133979697Snon	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
134079697Snon	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr, tottimeout);
134179697Snon	CLNT_DESTROY(client);
134279697Snon	return (uaddr);
134379697Snon}
134479697Snon
134579697Snon/*
134679697Snon * Converts universal address to netbuf.  This routine should never
134779697Snon * really be called because local n2a libraries are always provided.
134879697Snon */
134979697Snonstruct netbuf *
135079697Snonrpcb_uaddr2taddr(struct netconfig *nconf, char *uaddr)
135179697Snon{
135279697Snon	CLIENT *client;
135379697Snon	struct netbuf *taddr;
135479697Snon
135579697Snon
135679697Snon	/* parameter checking */
135779697Snon	if (nconf == NULL) {
135879697Snon		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
135979697Snon		return (NULL);
136079697Snon	}
136179697Snon	if (uaddr == NULL) {
136279697Snon		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
136379697Snon		return (NULL);
136479697Snon	}
136579697Snon	client = local_rpcb();
136679697Snon	if (! client) {
136779697Snon		return (NULL);
136879697Snon	}
136979697Snon
137079697Snon	taddr = (struct netbuf *)malloc(sizeof (struct netbuf), M_RPC, M_WAITOK|M_ZERO);
137179697Snon	if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_UADDR2TADDR,
137279697Snon	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr,
137379697Snon	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
137479697Snon	    tottimeout) != RPC_SUCCESS) {
137579697Snon		free(taddr);
137679697Snon		taddr = NULL;
137779697Snon	}
137879697Snon	CLNT_DESTROY(client);
137979697Snon	return (taddr);
138079697Snon}
138179697Snon
138279697Snon#endif
138379697Snon