1177633Sdfr/*	$NetBSD: rpcb_clnt.c,v 1.6 2000/07/16 06:41:43 itojun Exp $	*/
2177633Sdfr
3261057Smav/*-
4261057Smav * Copyright (c) 2010, Oracle America, Inc.
5261057Smav * All rights reserved.
6177633Sdfr *
7261057Smav * Redistribution and use in source and binary forms, with or without
8261057Smav * modification, are permitted provided that the following conditions are met:
9261057Smav * - Redistributions of source code must retain the above copyright notice,
10261057Smav *   this list of conditions and the following disclaimer.
11261057Smav * - Redistributions in binary form must reproduce the above copyright notice,
12261057Smav *   this list of conditions and the following disclaimer in the documentation
13261057Smav *   and/or other materials provided with the distribution.
14261057Smav * - Neither the name of the "Oracle America, Inc." nor the names of its
15261057Smav *   contributors may be used to endorse or promote products derived
16261057Smav *   from this software without specific prior written permission.
17177633Sdfr *
18261057Smav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19261057Smav * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20261057Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21261057Smav * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22261057Smav * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23261057Smav * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24261057Smav * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25261057Smav * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26261057Smav * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27261057Smav * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28261057Smav * POSSIBILITY OF SUCH DAMAGE.
29177633Sdfr */
30177633Sdfr/*
31177633Sdfr * Copyright (c) 1986-1991 by Sun Microsystems Inc.
32177633Sdfr */
33177633Sdfr
34177633Sdfr/* #ident	"@(#)rpcb_clnt.c	1.27	94/04/24 SMI" */
35177633Sdfr
36177633Sdfr
37177633Sdfr#if defined(LIBC_SCCS) && !defined(lint)
38177633Sdfrstatic char sccsid[] = "@(#)rpcb_clnt.c 1.30 89/06/21 Copyr 1988 Sun Micro";
39177633Sdfr#endif
40177633Sdfr#include <sys/cdefs.h>
41177633Sdfr__FBSDID("$FreeBSD$");
42177633Sdfr
43177633Sdfr/*
44177633Sdfr * rpcb_clnt.c
45177633Sdfr * interface to rpcbind rpc service.
46177633Sdfr *
47177633Sdfr * Copyright (C) 1988, Sun Microsystems, Inc.
48177633Sdfr */
49177633Sdfr
50177633Sdfr#include "opt_inet6.h"
51177633Sdfr
52177633Sdfr#include <sys/param.h>
53177633Sdfr#include <sys/systm.h>
54177633Sdfr#include <sys/kernel.h>
55177633Sdfr#include <sys/malloc.h>
56177633Sdfr#include <sys/proc.h>
57177633Sdfr#include <sys/socket.h>
58177633Sdfr#include <sys/socketvar.h>
59177633Sdfr
60177633Sdfr#include <rpc/rpc.h>
61177633Sdfr#include <rpc/rpcb_clnt.h>
62177633Sdfr#include <rpc/rpcb_prot.h>
63177633Sdfr
64177685Sdfr#include <rpc/rpc_com.h>
65177633Sdfr
66177633Sdfrstatic struct timeval tottimeout = { 60, 0 };
67177633Sdfrstatic const char nullstring[] = "\000";
68177633Sdfr
69177633Sdfrstatic CLIENT *local_rpcb(void);
70177633Sdfr
71177633Sdfr#if 0
72177633Sdfr
73259984Sdimstatic const struct timeval rmttimeout = { 3, 0 };
74177633Sdfrstatic struct timeval rpcbrmttime = { 15, 0 };
75177633Sdfr
76177633Sdfr#define	CACHESIZE 6
77177633Sdfr
78177633Sdfrstruct address_cache {
79177633Sdfr	char *ac_host;
80177633Sdfr	char *ac_netid;
81177633Sdfr	char *ac_uaddr;
82177633Sdfr	struct netbuf *ac_taddr;
83177633Sdfr	struct address_cache *ac_next;
84177633Sdfr};
85177633Sdfr
86177633Sdfrstatic struct address_cache *front;
87177633Sdfrstatic int cachesize;
88177633Sdfr
89177633Sdfr#define	CLCR_GET_RPCB_TIMEOUT	1
90177633Sdfr#define	CLCR_SET_RPCB_TIMEOUT	2
91177633Sdfr
92177633Sdfr
93177633Sdfrextern int __rpc_lowvers;
94177633Sdfr
95177633Sdfrstatic struct address_cache *check_cache(const char *, const char *);
96177633Sdfrstatic void delete_cache(struct netbuf *);
97177633Sdfrstatic void add_cache(const char *, const char *, struct netbuf *, char *);
98177633Sdfrstatic CLIENT *getclnthandle(const char *, const struct netconfig *, char **);
99177633Sdfrstatic CLIENT *local_rpcb(void);
100177633Sdfrstatic struct netbuf *got_entry(rpcb_entry_list_ptr, const struct netconfig *);
101177633Sdfr
102177633Sdfr/*
103177633Sdfr * This routine adjusts the timeout used for calls to the remote rpcbind.
104177633Sdfr * Also, this routine can be used to set the use of portmapper version 2
105177633Sdfr * only when doing rpc_broadcasts
106177633Sdfr * These are private routines that may not be provided in future releases.
107177633Sdfr */
108177633Sdfrbool_t
109177633Sdfr__rpc_control(request, info)
110177633Sdfr	int	request;
111177633Sdfr	void	*info;
112177633Sdfr{
113177633Sdfr	switch (request) {
114177633Sdfr	case CLCR_GET_RPCB_TIMEOUT:
115177633Sdfr		*(struct timeval *)info = tottimeout;
116177633Sdfr		break;
117177633Sdfr	case CLCR_SET_RPCB_TIMEOUT:
118177633Sdfr		tottimeout = *(struct timeval *)info;
119177633Sdfr		break;
120177633Sdfr	case CLCR_SET_LOWVERS:
121177633Sdfr		__rpc_lowvers = *(int *)info;
122177633Sdfr		break;
123177633Sdfr	case CLCR_GET_LOWVERS:
124177633Sdfr		*(int *)info = __rpc_lowvers;
125177633Sdfr		break;
126177633Sdfr	default:
127177633Sdfr		return (FALSE);
128177633Sdfr	}
129177633Sdfr	return (TRUE);
130177633Sdfr}
131177633Sdfr
132177633Sdfr/*
133177633Sdfr *	It might seem that a reader/writer lock would be more reasonable here.
134177633Sdfr *	However because getclnthandle(), the only user of the cache functions,
135177633Sdfr *	may do a delete_cache() operation if a check_cache() fails to return an
136177633Sdfr *	address useful to clnt_tli_create(), we may as well use a mutex.
137177633Sdfr */
138177633Sdfr/*
139177633Sdfr * As it turns out, if the cache lock is *not* a reader/writer lock, we will
140177633Sdfr * block all clnt_create's if we are trying to connect to a host that's down,
141177633Sdfr * since the lock will be held all during that time.
142177633Sdfr */
143177633Sdfr
144177633Sdfr/*
145177633Sdfr * The routines check_cache(), add_cache(), delete_cache() manage the
146177633Sdfr * cache of rpcbind addresses for (host, netid).
147177633Sdfr */
148177633Sdfr
149177633Sdfrstatic struct address_cache *
150177633Sdfrcheck_cache(host, netid)
151177633Sdfr	const char *host, *netid;
152177633Sdfr{
153177633Sdfr	struct address_cache *cptr;
154177633Sdfr
155177633Sdfr	/* READ LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
156177633Sdfr
157177633Sdfr	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
158177633Sdfr		if (!strcmp(cptr->ac_host, host) &&
159177633Sdfr		    !strcmp(cptr->ac_netid, netid)) {
160177633Sdfr#ifdef ND_DEBUG
161177633Sdfr			fprintf(stderr, "Found cache entry for %s: %s\n",
162177633Sdfr				host, netid);
163177633Sdfr#endif
164177633Sdfr			return (cptr);
165177633Sdfr		}
166177633Sdfr	}
167177633Sdfr	return ((struct address_cache *) NULL);
168177633Sdfr}
169177633Sdfr
170177633Sdfrstatic void
171177633Sdfrdelete_cache(addr)
172177633Sdfr	struct netbuf *addr;
173177633Sdfr{
174177633Sdfr	struct address_cache *cptr, *prevptr = NULL;
175177633Sdfr
176177633Sdfr	/* WRITE LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
177177633Sdfr	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
178177633Sdfr		if (!memcmp(cptr->ac_taddr->buf, addr->buf, addr->len)) {
179177633Sdfr			free(cptr->ac_host);
180177633Sdfr			free(cptr->ac_netid);
181177633Sdfr			free(cptr->ac_taddr->buf);
182177633Sdfr			free(cptr->ac_taddr);
183177633Sdfr			if (cptr->ac_uaddr)
184177633Sdfr				free(cptr->ac_uaddr);
185177633Sdfr			if (prevptr)
186177633Sdfr				prevptr->ac_next = cptr->ac_next;
187177633Sdfr			else
188177633Sdfr				front = cptr->ac_next;
189177633Sdfr			free(cptr);
190177633Sdfr			cachesize--;
191177633Sdfr			break;
192177633Sdfr		}
193177633Sdfr		prevptr = cptr;
194177633Sdfr	}
195177633Sdfr}
196177633Sdfr
197177633Sdfrstatic void
198177633Sdfradd_cache(host, netid, taddr, uaddr)
199177633Sdfr	const char *host, *netid;
200177633Sdfr	char *uaddr;
201177633Sdfr	struct netbuf *taddr;
202177633Sdfr{
203177633Sdfr	struct address_cache  *ad_cache, *cptr, *prevptr;
204177633Sdfr
205177633Sdfr	ad_cache = (struct address_cache *)
206177633Sdfr			malloc(sizeof (struct address_cache));
207177633Sdfr	if (!ad_cache) {
208177633Sdfr		return;
209177633Sdfr	}
210177633Sdfr	ad_cache->ac_host = strdup(host);
211177633Sdfr	ad_cache->ac_netid = strdup(netid);
212177633Sdfr	ad_cache->ac_uaddr = uaddr ? strdup(uaddr) : NULL;
213177633Sdfr	ad_cache->ac_taddr = (struct netbuf *)malloc(sizeof (struct netbuf));
214177633Sdfr	if (!ad_cache->ac_host || !ad_cache->ac_netid || !ad_cache->ac_taddr ||
215177633Sdfr		(uaddr && !ad_cache->ac_uaddr)) {
216177633Sdfr		goto out;
217177633Sdfr	}
218177633Sdfr	ad_cache->ac_taddr->len = ad_cache->ac_taddr->maxlen = taddr->len;
219177633Sdfr	ad_cache->ac_taddr->buf = (char *) malloc(taddr->len);
220177633Sdfr	if (ad_cache->ac_taddr->buf == NULL) {
221177633Sdfrout:
222177633Sdfr		if (ad_cache->ac_host)
223177633Sdfr			free(ad_cache->ac_host);
224177633Sdfr		if (ad_cache->ac_netid)
225177633Sdfr			free(ad_cache->ac_netid);
226177633Sdfr		if (ad_cache->ac_uaddr)
227177633Sdfr			free(ad_cache->ac_uaddr);
228177633Sdfr		if (ad_cache->ac_taddr)
229177633Sdfr			free(ad_cache->ac_taddr);
230177633Sdfr		free(ad_cache);
231177633Sdfr		return;
232177633Sdfr	}
233177633Sdfr	memcpy(ad_cache->ac_taddr->buf, taddr->buf, taddr->len);
234177633Sdfr#ifdef ND_DEBUG
235177633Sdfr	fprintf(stderr, "Added to cache: %s : %s\n", host, netid);
236177633Sdfr#endif
237177633Sdfr
238177633Sdfr/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  cptr */
239177633Sdfr
240177633Sdfr	rwlock_wrlock(&rpcbaddr_cache_lock);
241177633Sdfr	if (cachesize < CACHESIZE) {
242177633Sdfr		ad_cache->ac_next = front;
243177633Sdfr		front = ad_cache;
244177633Sdfr		cachesize++;
245177633Sdfr	} else {
246177633Sdfr		/* Free the last entry */
247177633Sdfr		cptr = front;
248177633Sdfr		prevptr = NULL;
249177633Sdfr		while (cptr->ac_next) {
250177633Sdfr			prevptr = cptr;
251177633Sdfr			cptr = cptr->ac_next;
252177633Sdfr		}
253177633Sdfr
254177633Sdfr#ifdef ND_DEBUG
255177633Sdfr		fprintf(stderr, "Deleted from cache: %s : %s\n",
256177633Sdfr			cptr->ac_host, cptr->ac_netid);
257177633Sdfr#endif
258177633Sdfr		free(cptr->ac_host);
259177633Sdfr		free(cptr->ac_netid);
260177633Sdfr		free(cptr->ac_taddr->buf);
261177633Sdfr		free(cptr->ac_taddr);
262177633Sdfr		if (cptr->ac_uaddr)
263177633Sdfr			free(cptr->ac_uaddr);
264177633Sdfr
265177633Sdfr		if (prevptr) {
266177633Sdfr			prevptr->ac_next = NULL;
267177633Sdfr			ad_cache->ac_next = front;
268177633Sdfr			front = ad_cache;
269177633Sdfr		} else {
270177633Sdfr			front = ad_cache;
271177633Sdfr			ad_cache->ac_next = NULL;
272177633Sdfr		}
273177633Sdfr		free(cptr);
274177633Sdfr	}
275177633Sdfr	rwlock_unlock(&rpcbaddr_cache_lock);
276177633Sdfr}
277177633Sdfr
278177633Sdfr/*
279177633Sdfr * This routine will return a client handle that is connected to the
280177633Sdfr * rpcbind. If targaddr is non-NULL, the "universal address" of the
281177633Sdfr * host will be stored in *targaddr; the caller is responsible for
282177633Sdfr * freeing this string.
283177633Sdfr * On error, returns NULL and free's everything.
284177633Sdfr */
285177633Sdfrstatic CLIENT *
286177633Sdfrgetclnthandle(host, nconf, targaddr)
287177633Sdfr	const char *host;
288177633Sdfr	const struct netconfig *nconf;
289177633Sdfr	char **targaddr;
290177633Sdfr{
291177633Sdfr	CLIENT *client;
292177633Sdfr	struct netbuf *addr, taddr;
293177633Sdfr	struct netbuf addr_to_delete;
294177633Sdfr	struct __rpc_sockinfo si;
295177633Sdfr	struct addrinfo hints, *res, *tres;
296177633Sdfr	struct address_cache *ad_cache;
297177633Sdfr	char *tmpaddr;
298177633Sdfr
299177633Sdfr/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  ad_cache */
300177633Sdfr
301177633Sdfr	/* Get the address of the rpcbind.  Check cache first */
302177633Sdfr	client = NULL;
303177633Sdfr	addr_to_delete.len = 0;
304177633Sdfr	rwlock_rdlock(&rpcbaddr_cache_lock);
305177633Sdfr	ad_cache = NULL;
306177633Sdfr	if (host != NULL)
307177633Sdfr		ad_cache = check_cache(host, nconf->nc_netid);
308177633Sdfr	if (ad_cache != NULL) {
309177633Sdfr		addr = ad_cache->ac_taddr;
310177633Sdfr		client = clnt_tli_create(RPC_ANYFD, nconf, addr,
311177633Sdfr		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
312177633Sdfr		if (client != NULL) {
313177633Sdfr			if (targaddr)
314177633Sdfr				*targaddr = strdup(ad_cache->ac_uaddr);
315177633Sdfr			rwlock_unlock(&rpcbaddr_cache_lock);
316177633Sdfr			return (client);
317177633Sdfr		}
318177633Sdfr		addr_to_delete.len = addr->len;
319177633Sdfr		addr_to_delete.buf = (char *)malloc(addr->len);
320177633Sdfr		if (addr_to_delete.buf == NULL) {
321177633Sdfr			addr_to_delete.len = 0;
322177633Sdfr		} else {
323177633Sdfr			memcpy(addr_to_delete.buf, addr->buf, addr->len);
324177633Sdfr		}
325177633Sdfr	}
326177633Sdfr	rwlock_unlock(&rpcbaddr_cache_lock);
327177633Sdfr	if (addr_to_delete.len != 0) {
328177633Sdfr		/*
329177633Sdfr		 * Assume this may be due to cache data being
330177633Sdfr		 *  outdated
331177633Sdfr		 */
332177633Sdfr		rwlock_wrlock(&rpcbaddr_cache_lock);
333177633Sdfr		delete_cache(&addr_to_delete);
334177633Sdfr		rwlock_unlock(&rpcbaddr_cache_lock);
335177633Sdfr		free(addr_to_delete.buf);
336177633Sdfr	}
337177633Sdfr	if (!__rpc_nconf2sockinfo(nconf, &si)) {
338177633Sdfr		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
339177633Sdfr		return NULL;
340177633Sdfr	}
341177633Sdfr
342177633Sdfr	memset(&hints, 0, sizeof hints);
343177633Sdfr	hints.ai_family = si.si_af;
344177633Sdfr	hints.ai_socktype = si.si_socktype;
345177633Sdfr	hints.ai_protocol = si.si_proto;
346177633Sdfr
347177633Sdfr#ifdef CLNT_DEBUG
348177633Sdfr	printf("trying netid %s family %d proto %d socktype %d\n",
349177633Sdfr	    nconf->nc_netid, si.si_af, si.si_proto, si.si_socktype);
350177633Sdfr#endif
351177633Sdfr
352177633Sdfr	if (nconf->nc_protofmly != NULL && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
353177633Sdfr		client = local_rpcb();
354177633Sdfr		if (! client) {
355177633Sdfr#ifdef ND_DEBUG
356177633Sdfr			clnt_pcreateerror("rpcbind clnt interface");
357177633Sdfr#endif
358177633Sdfr			return (NULL);
359177633Sdfr		} else {
360177633Sdfr			struct sockaddr_un sun;
361177633Sdfr			if (targaddr) {
362177633Sdfr			    *targaddr = malloc(sizeof(sun.sun_path));
363177633Sdfr			    if (*targaddr == NULL) {
364177633Sdfr				CLNT_DESTROY(client);
365177633Sdfr				return (NULL);
366177633Sdfr			    }
367177633Sdfr			    strncpy(*targaddr, _PATH_RPCBINDSOCK,
368177633Sdfr				sizeof(sun.sun_path));
369177633Sdfr			}
370177633Sdfr			return (client);
371177633Sdfr		}
372177633Sdfr	} else {
373177633Sdfr		if (getaddrinfo(host, "sunrpc", &hints, &res) != 0) {
374177633Sdfr			rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
375177633Sdfr			return NULL;
376177633Sdfr		}
377177633Sdfr	}
378177633Sdfr
379177633Sdfr	for (tres = res; tres != NULL; tres = tres->ai_next) {
380177633Sdfr		taddr.buf = tres->ai_addr;
381177633Sdfr		taddr.len = taddr.maxlen = tres->ai_addrlen;
382177633Sdfr
383177633Sdfr#ifdef ND_DEBUG
384177633Sdfr		{
385177633Sdfr			char *ua;
386177633Sdfr
387177633Sdfr			ua = taddr2uaddr(nconf, &taddr);
388177633Sdfr			fprintf(stderr, "Got it [%s]\n", ua);
389177633Sdfr			free(ua);
390177633Sdfr		}
391177633Sdfr#endif
392177633Sdfr
393177633Sdfr#ifdef ND_DEBUG
394177633Sdfr		{
395177633Sdfr			int i;
396177633Sdfr
397177633Sdfr			fprintf(stderr, "\tnetbuf len = %d, maxlen = %d\n",
398177633Sdfr				taddr.len, taddr.maxlen);
399177633Sdfr			fprintf(stderr, "\tAddress is ");
400177633Sdfr			for (i = 0; i < taddr.len; i++)
401177633Sdfr				fprintf(stderr, "%u.", ((char *)(taddr.buf))[i]);
402177633Sdfr			fprintf(stderr, "\n");
403177633Sdfr		}
404177633Sdfr#endif
405177633Sdfr		client = clnt_tli_create(RPC_ANYFD, nconf, &taddr,
406177633Sdfr		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
407177633Sdfr#ifdef ND_DEBUG
408177633Sdfr		if (! client) {
409177633Sdfr			clnt_pcreateerror("rpcbind clnt interface");
410177633Sdfr		}
411177633Sdfr#endif
412177633Sdfr
413177633Sdfr		if (client) {
414177633Sdfr			tmpaddr = targaddr ? taddr2uaddr(nconf, &taddr) : NULL;
415177633Sdfr			add_cache(host, nconf->nc_netid, &taddr, tmpaddr);
416177633Sdfr			if (targaddr)
417177633Sdfr				*targaddr = tmpaddr;
418177633Sdfr			break;
419177633Sdfr		}
420177633Sdfr	}
421177633Sdfr	if (res)
422177633Sdfr		freeaddrinfo(res);
423177633Sdfr	return (client);
424177633Sdfr}
425177633Sdfr
426177633Sdfr#endif
427177633Sdfr
428177633Sdfr/* XXX */
429177633Sdfr#define IN4_LOCALHOST_STRING	"127.0.0.1"
430177633Sdfr#define IN6_LOCALHOST_STRING	"::1"
431177633Sdfr
432177633Sdfr/*
433177633Sdfr * This routine will return a client handle that is connected to the local
434177633Sdfr * rpcbind. Returns NULL on error and free's everything.
435177633Sdfr */
436177633Sdfrstatic CLIENT *
437177633Sdfrlocal_rpcb()
438177633Sdfr{
439177633Sdfr	CLIENT *client;
440177633Sdfr	struct socket *so;
441177633Sdfr	size_t tsize;
442177633Sdfr	struct sockaddr_un sun;
443177633Sdfr	int error;
444177633Sdfr
445177633Sdfr	/*
446177633Sdfr	 * Try connecting to the local rpcbind through a local socket
447177633Sdfr	 * first. If this doesn't work, try all transports defined in
448177633Sdfr	 * the netconfig file.
449177633Sdfr	 */
450177633Sdfr	memset(&sun, 0, sizeof sun);
451177633Sdfr	so = NULL;
452177633Sdfr	error = socreate(AF_LOCAL, &so, SOCK_STREAM, 0, curthread->td_ucred,
453177633Sdfr	    curthread);
454177633Sdfr	if (error)
455177633Sdfr		goto try_nconf;
456177633Sdfr	sun.sun_family = AF_LOCAL;
457177633Sdfr	strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
458177633Sdfr	sun.sun_len = SUN_LEN(&sun);
459177633Sdfr
460177633Sdfr	tsize = __rpc_get_t_size(AF_LOCAL, 0, 0);
461177633Sdfr	client = clnt_vc_create(so, (struct sockaddr *)&sun, (rpcprog_t)RPCBPROG,
462221127Srmacklem	    (rpcvers_t)RPCBVERS, tsize, tsize, 1);
463177633Sdfr
464177633Sdfr	if (client != NULL) {
465177633Sdfr		/* Mark the socket to be closed in destructor */
466177633Sdfr		(void) CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
467177633Sdfr		return client;
468177633Sdfr	}
469177633Sdfr
470177633Sdfr	/* Nobody needs this socket anymore; free the descriptor. */
471177633Sdfr	soclose(so);
472177633Sdfr
473177633Sdfrtry_nconf:
474177633Sdfr
475177633Sdfr#if 0
476177633Sdfr	static struct netconfig *loopnconf;
477180087Sjulian	static char *localhostname;
478177633Sdfr
479177633Sdfr/* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */
480177633Sdfr	mutex_lock(&loopnconf_lock);
481177633Sdfr	if (loopnconf == NULL) {
482177633Sdfr		struct netconfig *nconf, *tmpnconf = NULL;
483177633Sdfr		void *nc_handle;
484177633Sdfr		int fd;
485177633Sdfr
486177633Sdfr		nc_handle = setnetconfig();
487177633Sdfr		if (nc_handle == NULL) {
488177633Sdfr			/* fails to open netconfig file */
489177633Sdfr			syslog (LOG_ERR, "rpc: failed to open " NETCONFIG);
490177633Sdfr			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
491177633Sdfr			mutex_unlock(&loopnconf_lock);
492177633Sdfr			return (NULL);
493177633Sdfr		}
494177633Sdfr		while ((nconf = getnetconfig(nc_handle)) != NULL) {
495177633Sdfr			if ((
496177633Sdfr#ifdef INET6
497177633Sdfr			     strcmp(nconf->nc_protofmly, NC_INET6) == 0 ||
498177633Sdfr#endif
499177633Sdfr			     strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
500177633Sdfr			    (nconf->nc_semantics == NC_TPI_COTS ||
501177633Sdfr			     nconf->nc_semantics == NC_TPI_COTS_ORD)) {
502177633Sdfr				fd = __rpc_nconf2fd(nconf);
503177633Sdfr				/*
504177633Sdfr				 * Can't create a socket, assume that
505177633Sdfr				 * this family isn't configured in the kernel.
506177633Sdfr				 */
507177633Sdfr				if (fd < 0)
508177633Sdfr					continue;
509177633Sdfr				_close(fd);
510177633Sdfr				tmpnconf = nconf;
511177633Sdfr				if (!strcmp(nconf->nc_protofmly, NC_INET))
512180087Sjulian					localhostname = IN4_LOCALHOST_STRING;
513177633Sdfr				else
514180087Sjulian					localhostname = IN6_LOCALHOST_STRING;
515177633Sdfr			}
516177633Sdfr		}
517177633Sdfr		if (tmpnconf == NULL) {
518177633Sdfr			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
519177633Sdfr			mutex_unlock(&loopnconf_lock);
520177633Sdfr			return (NULL);
521177633Sdfr		}
522177633Sdfr		loopnconf = getnetconfigent(tmpnconf->nc_netid);
523177633Sdfr		/* loopnconf is never freed */
524177633Sdfr		endnetconfig(nc_handle);
525177633Sdfr	}
526177633Sdfr	mutex_unlock(&loopnconf_lock);
527180087Sjulian	client = getclnthandle(localhostname, loopnconf, NULL);
528177633Sdfr	return (client);
529177633Sdfr#else
530177633Sdfr	return (NULL);
531177633Sdfr#endif
532177633Sdfr}
533177633Sdfr
534177633Sdfr/*
535177633Sdfr * Set a mapping between program, version and address.
536177633Sdfr * Calls the rpcbind service to do the mapping.
537177633Sdfr */
538177633Sdfrbool_t
539177633Sdfrrpcb_set(rpcprog_t program, rpcvers_t version,
540177633Sdfr    const struct netconfig *nconf,	/* Network structure of transport */
541177633Sdfr    const struct netbuf *address)	/* Services netconfig address */
542177633Sdfr{
543177633Sdfr	CLIENT *client;
544177633Sdfr	bool_t rslt = FALSE;
545177633Sdfr	RPCB parms;
546177633Sdfr#if 0
547177633Sdfr	char uidbuf[32];
548177633Sdfr#endif
549177633Sdfr	struct netconfig nconfcopy;
550177633Sdfr	struct netbuf addresscopy;
551177633Sdfr
552177633Sdfr	/* parameter checking */
553177633Sdfr	if (nconf == NULL) {
554177633Sdfr		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
555177633Sdfr		return (FALSE);
556177633Sdfr	}
557177633Sdfr	if (address == NULL) {
558177633Sdfr		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
559177633Sdfr		return (FALSE);
560177633Sdfr	}
561177633Sdfr	client = local_rpcb();
562177633Sdfr	if (! client) {
563177633Sdfr		return (FALSE);
564177633Sdfr	}
565177633Sdfr
566177633Sdfr	/* convert to universal */
567177633Sdfr	/*LINTED const castaway*/
568177633Sdfr	nconfcopy = *nconf;
569177633Sdfr	addresscopy = *address;
570177633Sdfr	parms.r_addr = taddr2uaddr(&nconfcopy, &addresscopy);
571177633Sdfr	if (!parms.r_addr) {
572177633Sdfr		CLNT_DESTROY(client);
573177633Sdfr		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
574177633Sdfr		return (FALSE); /* no universal address */
575177633Sdfr	}
576177633Sdfr	parms.r_prog = program;
577177633Sdfr	parms.r_vers = version;
578177633Sdfr	parms.r_netid = nconf->nc_netid;
579177633Sdfr#if 0
580177633Sdfr	/*
581177633Sdfr	 * Though uid is not being used directly, we still send it for
582177633Sdfr	 * completeness.  For non-unix platforms, perhaps some other
583177633Sdfr	 * string or an empty string can be sent.
584177633Sdfr	 */
585177633Sdfr	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
586177633Sdfr	parms.r_owner = uidbuf;
587177633Sdfr#else
588177633Sdfr	parms.r_owner = "";
589177633Sdfr#endif
590177633Sdfr
591177633Sdfr	CLNT_CALL(client, (rpcproc_t)RPCBPROC_SET, (xdrproc_t) xdr_rpcb,
592177633Sdfr	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
593177633Sdfr	    (char *)(void *)&rslt, tottimeout);
594177633Sdfr
595177633Sdfr	CLNT_DESTROY(client);
596177633Sdfr	free(parms.r_addr, M_RPC);
597177633Sdfr	return (rslt);
598177633Sdfr}
599177633Sdfr
600177633Sdfr/*
601177633Sdfr * Remove the mapping between program, version and netbuf address.
602177633Sdfr * Calls the rpcbind service to do the un-mapping.
603177633Sdfr * If netbuf is NULL, unset for all the transports, otherwise unset
604177633Sdfr * only for the given transport.
605177633Sdfr */
606177633Sdfrbool_t
607177633Sdfrrpcb_unset(rpcprog_t program, rpcvers_t version, const struct netconfig *nconf)
608177633Sdfr{
609177633Sdfr	CLIENT *client;
610177633Sdfr	bool_t rslt = FALSE;
611177633Sdfr	RPCB parms;
612177633Sdfr#if 0
613177633Sdfr	char uidbuf[32];
614177633Sdfr#endif
615177633Sdfr
616177633Sdfr	client = local_rpcb();
617177633Sdfr	if (! client) {
618177633Sdfr		return (FALSE);
619177633Sdfr	}
620177633Sdfr
621177633Sdfr	parms.r_prog = program;
622177633Sdfr	parms.r_vers = version;
623177633Sdfr	if (nconf)
624177633Sdfr		parms.r_netid = nconf->nc_netid;
625177633Sdfr	else {
626177633Sdfr		/*LINTED const castaway*/
627177633Sdfr		parms.r_netid = (char *)(uintptr_t) &nullstring[0]; /* unsets  all */
628177633Sdfr	}
629177633Sdfr	/*LINTED const castaway*/
630177633Sdfr	parms.r_addr = (char *)(uintptr_t) &nullstring[0];
631177633Sdfr#if 0
632177633Sdfr	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
633177633Sdfr	parms.r_owner = uidbuf;
634177633Sdfr#else
635177633Sdfr	parms.r_owner = "";
636177633Sdfr#endif
637177633Sdfr
638177633Sdfr	CLNT_CALL(client, (rpcproc_t)RPCBPROC_UNSET, (xdrproc_t) xdr_rpcb,
639177633Sdfr	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
640177633Sdfr	    (char *)(void *)&rslt, tottimeout);
641177633Sdfr
642177633Sdfr	CLNT_DESTROY(client);
643177633Sdfr	return (rslt);
644177633Sdfr}
645177633Sdfr
646177633Sdfr#if 0
647177633Sdfr
648177633Sdfr/*
649177633Sdfr * From the merged list, find the appropriate entry
650177633Sdfr */
651177633Sdfrstatic struct netbuf *
652177633Sdfrgot_entry(relp, nconf)
653177633Sdfr	rpcb_entry_list_ptr relp;
654177633Sdfr	const struct netconfig *nconf;
655177633Sdfr{
656177633Sdfr	struct netbuf *na = NULL;
657177633Sdfr	rpcb_entry_list_ptr sp;
658177633Sdfr	rpcb_entry *rmap;
659177633Sdfr
660177633Sdfr	for (sp = relp; sp != NULL; sp = sp->rpcb_entry_next) {
661177633Sdfr		rmap = &sp->rpcb_entry_map;
662177633Sdfr		if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) &&
663177633Sdfr		    (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) &&
664177633Sdfr		    (nconf->nc_semantics == rmap->r_nc_semantics) &&
665177633Sdfr		    (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != 0)) {
666177633Sdfr			na = uaddr2taddr(nconf, rmap->r_maddr);
667177633Sdfr#ifdef ND_DEBUG
668177633Sdfr			fprintf(stderr, "\tRemote address is [%s].\n",
669177633Sdfr				rmap->r_maddr);
670177633Sdfr			if (!na)
671177633Sdfr				fprintf(stderr,
672177633Sdfr				    "\tCouldn't resolve remote address!\n");
673177633Sdfr#endif
674177633Sdfr			break;
675177633Sdfr		}
676177633Sdfr	}
677177633Sdfr	return (na);
678177633Sdfr}
679177633Sdfr
680177633Sdfr/*
681177633Sdfr * Quick check to see if rpcbind is up.  Tries to connect over
682177633Sdfr * local transport.
683177633Sdfr */
684177633Sdfrstatic bool_t
685177633Sdfr__rpcbind_is_up()
686177633Sdfr{
687177633Sdfr	struct netconfig *nconf;
688177633Sdfr	struct sockaddr_un sun;
689177633Sdfr	void *localhandle;
690177633Sdfr	int sock;
691177633Sdfr
692177633Sdfr	nconf = NULL;
693177633Sdfr	localhandle = setnetconfig();
694177633Sdfr	while ((nconf = getnetconfig(localhandle)) != NULL) {
695177633Sdfr		if (nconf->nc_protofmly != NULL &&
696177633Sdfr		    strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)
697177633Sdfr			 break;
698177633Sdfr	}
699177633Sdfr	if (nconf == NULL)
700177633Sdfr		return (FALSE);
701177633Sdfr
702177633Sdfr	endnetconfig(localhandle);
703177633Sdfr
704177633Sdfr	memset(&sun, 0, sizeof sun);
705177633Sdfr	sock = _socket(AF_LOCAL, SOCK_STREAM, 0);
706177633Sdfr	if (sock < 0)
707177633Sdfr		return (FALSE);
708177633Sdfr	sun.sun_family = AF_LOCAL;
709177633Sdfr	strncpy(sun.sun_path, _PATH_RPCBINDSOCK, sizeof(sun.sun_path));
710177633Sdfr	sun.sun_len = SUN_LEN(&sun);
711177633Sdfr
712177633Sdfr	if (_connect(sock, (struct sockaddr *)&sun, sun.sun_len) < 0) {
713177633Sdfr		_close(sock);
714177633Sdfr		return (FALSE);
715177633Sdfr	}
716177633Sdfr
717177633Sdfr	_close(sock);
718177633Sdfr	return (TRUE);
719177633Sdfr}
720177633Sdfr
721177633Sdfr/*
722177633Sdfr * An internal function which optimizes rpcb_getaddr function.  It also
723177633Sdfr * returns the client handle that it uses to contact the remote rpcbind.
724177633Sdfr *
725177633Sdfr * The algorithm used: If the transports is TCP or UDP, it first tries
726177633Sdfr * version 2 (portmap), 4 and then 3 (svr4).  This order should be
727177633Sdfr * changed in the next OS release to 4, 2 and 3.  We are assuming that by
728177633Sdfr * that time, version 4 would be available on many machines on the network.
729177633Sdfr * With this algorithm, we get performance as well as a plan for
730177633Sdfr * obsoleting version 2.
731177633Sdfr *
732177633Sdfr * For all other transports, the algorithm remains as 4 and then 3.
733177633Sdfr *
734177633Sdfr * XXX: Due to some problems with t_connect(), we do not reuse the same client
735177633Sdfr * handle for COTS cases and hence in these cases we do not return the
736177633Sdfr * client handle.  This code will change if t_connect() ever
737177633Sdfr * starts working properly.  Also look under clnt_vc.c.
738177633Sdfr */
739177633Sdfrstruct netbuf *
740177633Sdfr__rpcb_findaddr_timed(program, version, nconf, host, clpp, tp)
741177633Sdfr	rpcprog_t program;
742177633Sdfr	rpcvers_t version;
743177633Sdfr	const struct netconfig *nconf;
744177633Sdfr	const char *host;
745177633Sdfr	CLIENT **clpp;
746177633Sdfr	struct timeval *tp;
747177633Sdfr{
748177633Sdfr	static bool_t check_rpcbind = TRUE;
749177633Sdfr	CLIENT *client = NULL;
750177633Sdfr	RPCB parms;
751177633Sdfr	enum clnt_stat clnt_st;
752177633Sdfr	char *ua = NULL;
753177633Sdfr	rpcvers_t vers;
754177633Sdfr	struct netbuf *address = NULL;
755177633Sdfr	rpcvers_t start_vers = RPCBVERS4;
756177633Sdfr	struct netbuf servaddr;
757177633Sdfr
758177633Sdfr	/* parameter checking */
759177633Sdfr	if (nconf == NULL) {
760177633Sdfr		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
761177633Sdfr		return (NULL);
762177633Sdfr	}
763177633Sdfr
764177633Sdfr	parms.r_addr = NULL;
765177633Sdfr
766177633Sdfr	/*
767177633Sdfr	 * Use default total timeout if no timeout is specified.
768177633Sdfr	 */
769177633Sdfr	if (tp == NULL)
770177633Sdfr		tp = &tottimeout;
771177633Sdfr
772177633Sdfr#ifdef PORTMAP
773177633Sdfr	/* Try version 2 for TCP or UDP */
774177633Sdfr	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
775177633Sdfr		u_short port = 0;
776177633Sdfr		struct netbuf remote;
777177633Sdfr		rpcvers_t pmapvers = 2;
778177633Sdfr		struct pmap pmapparms;
779177633Sdfr
780177633Sdfr		/*
781177633Sdfr		 * Try UDP only - there are some portmappers out
782177633Sdfr		 * there that use UDP only.
783177633Sdfr		 */
784177633Sdfr		if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
785177633Sdfr			struct netconfig *newnconf;
786177633Sdfr
787177633Sdfr			if ((newnconf = getnetconfigent("udp")) == NULL) {
788177633Sdfr				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
789177633Sdfr				return (NULL);
790177633Sdfr			}
791177633Sdfr			client = getclnthandle(host, newnconf, &parms.r_addr);
792177633Sdfr			freenetconfigent(newnconf);
793177633Sdfr		} else {
794177633Sdfr			client = getclnthandle(host, nconf, &parms.r_addr);
795177633Sdfr		}
796177633Sdfr		if (client == NULL)
797177633Sdfr			return (NULL);
798177633Sdfr
799177633Sdfr		/*
800177633Sdfr		 * Set version and retry timeout.
801177633Sdfr		 */
802177633Sdfr		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
803177633Sdfr		CLNT_CONTROL(client, CLSET_VERS, (char *)&pmapvers);
804177633Sdfr
805177633Sdfr		pmapparms.pm_prog = program;
806177633Sdfr		pmapparms.pm_vers = version;
807177633Sdfr		pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ?
808177633Sdfr					IPPROTO_UDP : IPPROTO_TCP;
809177633Sdfr		pmapparms.pm_port = 0;	/* not needed */
810177633Sdfr		clnt_st = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
811177633Sdfr		    (xdrproc_t) xdr_pmap, (caddr_t)(void *)&pmapparms,
812177633Sdfr		    (xdrproc_t) xdr_u_short, (caddr_t)(void *)&port,
813177633Sdfr		    *tp);
814177633Sdfr		if (clnt_st != RPC_SUCCESS) {
815177633Sdfr			if ((clnt_st == RPC_PROGVERSMISMATCH) ||
816177633Sdfr				(clnt_st == RPC_PROGUNAVAIL))
817177633Sdfr				goto try_rpcbind; /* Try different versions */
818177633Sdfr			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
819177633Sdfr			clnt_geterr(client, &rpc_createerr.cf_error);
820177633Sdfr			goto error;
821177633Sdfr		} else if (port == 0) {
822177633Sdfr			address = NULL;
823177633Sdfr			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
824177633Sdfr			goto error;
825177633Sdfr		}
826177633Sdfr		port = htons(port);
827177633Sdfr		CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)&remote);
828177633Sdfr		if (((address = (struct netbuf *)
829177633Sdfr			malloc(sizeof (struct netbuf))) == NULL) ||
830177633Sdfr		    ((address->buf = (char *)
831177633Sdfr			malloc(remote.len)) == NULL)) {
832177633Sdfr			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
833177633Sdfr			clnt_geterr(client, &rpc_createerr.cf_error);
834177633Sdfr			if (address) {
835177633Sdfr				free(address);
836177633Sdfr				address = NULL;
837177633Sdfr			}
838177633Sdfr			goto error;
839177633Sdfr		}
840177633Sdfr		memcpy(address->buf, remote.buf, remote.len);
841177633Sdfr		memcpy(&((char *)address->buf)[sizeof (short)],
842177633Sdfr				(char *)(void *)&port, sizeof (short));
843177633Sdfr		address->len = address->maxlen = remote.len;
844177633Sdfr		goto done;
845177633Sdfr	}
846177633Sdfr#endif				/* PORTMAP */
847177633Sdfr
848177633Sdfrtry_rpcbind:
849177633Sdfr	/*
850177633Sdfr	 * Check if rpcbind is up.  This prevents needless delays when
851177633Sdfr	 * accessing applications such as the keyserver while booting
852177633Sdfr	 * disklessly.
853177633Sdfr	 */
854177633Sdfr	if (check_rpcbind && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
855177633Sdfr		if (!__rpcbind_is_up()) {
856177633Sdfr			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
857177633Sdfr			rpc_createerr.cf_error.re_errno = 0;
858177633Sdfr			goto error;
859177633Sdfr		}
860177633Sdfr		check_rpcbind = FALSE;
861177633Sdfr	}
862177633Sdfr
863177633Sdfr	/*
864177633Sdfr	 * Now we try version 4 and then 3.
865177633Sdfr	 * We also send the remote system the address we used to
866177633Sdfr	 * contact it in case it can help to connect back with us
867177633Sdfr	 */
868177633Sdfr	parms.r_prog = program;
869177633Sdfr	parms.r_vers = version;
870177633Sdfr	/*LINTED const castaway*/
871177633Sdfr	parms.r_owner = (char *) &nullstring[0];	/* not needed; */
872177633Sdfr							/* just for xdring */
873177633Sdfr	parms.r_netid = nconf->nc_netid; /* not really needed */
874177633Sdfr
875177633Sdfr	/*
876177633Sdfr	 * If a COTS transport is being used, try getting address via CLTS
877177633Sdfr	 * transport.  This works only with version 4.
878177633Sdfr	 */
879177633Sdfr	if (nconf->nc_semantics == NC_TPI_COTS_ORD ||
880177633Sdfr			nconf->nc_semantics == NC_TPI_COTS) {
881177633Sdfr
882177633Sdfr		void *handle;
883177633Sdfr		struct netconfig *nconf_clts;
884177633Sdfr		rpcb_entry_list_ptr relp = NULL;
885177633Sdfr
886177633Sdfr		if (client == NULL) {
887177633Sdfr			/* This did not go through the above PORTMAP/TCP code */
888177633Sdfr			if ((handle = __rpc_setconf("datagram_v")) != NULL) {
889177633Sdfr				while ((nconf_clts = __rpc_getconf(handle))
890177633Sdfr					!= NULL) {
891177633Sdfr					if (strcmp(nconf_clts->nc_protofmly,
892177633Sdfr						nconf->nc_protofmly) != 0) {
893177633Sdfr						continue;
894177633Sdfr					}
895177633Sdfr					client = getclnthandle(host, nconf_clts,
896177633Sdfr							&parms.r_addr);
897177633Sdfr					break;
898177633Sdfr				}
899177633Sdfr				__rpc_endconf(handle);
900177633Sdfr			}
901177633Sdfr			if (client == NULL)
902177633Sdfr				goto regular_rpcbind;	/* Go the regular way */
903177633Sdfr		} else {
904177633Sdfr			/* This is a UDP PORTMAP handle.  Change to version 4 */
905177633Sdfr			vers = RPCBVERS4;
906177633Sdfr			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
907177633Sdfr		}
908177633Sdfr		/*
909177633Sdfr		 * We also send the remote system the address we used to
910177633Sdfr		 * contact it in case it can help it connect back with us
911177633Sdfr		 */
912177633Sdfr		if (parms.r_addr == NULL) {
913177633Sdfr			/*LINTED const castaway*/
914177633Sdfr			parms.r_addr = (char *) &nullstring[0]; /* for XDRing */
915177633Sdfr		}
916177633Sdfr
917177633Sdfr		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
918177633Sdfr
919177633Sdfr		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDRLIST,
920177633Sdfr		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
921177633Sdfr		    (xdrproc_t) xdr_rpcb_entry_list_ptr,
922177633Sdfr		    (char *)(void *)&relp, *tp);
923177633Sdfr		if (clnt_st == RPC_SUCCESS) {
924177633Sdfr			if ((address = got_entry(relp, nconf)) != NULL) {
925177633Sdfr				xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
926177633Sdfr				    (char *)(void *)&relp);
927177633Sdfr				CLNT_CONTROL(client, CLGET_SVC_ADDR,
928177633Sdfr					(char *)(void *)&servaddr);
929177633Sdfr				__rpc_fixup_addr(address, &servaddr);
930177633Sdfr				goto done;
931177633Sdfr			}
932177633Sdfr			/* Entry not found for this transport */
933177633Sdfr			xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
934177633Sdfr			    (char *)(void *)&relp);
935177633Sdfr			/*
936177633Sdfr			 * XXX: should have perhaps returned with error but
937177633Sdfr			 * since the remote machine might not always be able
938177633Sdfr			 * to send the address on all transports, we try the
939177633Sdfr			 * regular way with regular_rpcbind
940177633Sdfr			 */
941177633Sdfr			goto regular_rpcbind;
942177633Sdfr		} else if ((clnt_st == RPC_PROGVERSMISMATCH) ||
943177633Sdfr			(clnt_st == RPC_PROGUNAVAIL)) {
944177633Sdfr			start_vers = RPCBVERS;	/* Try version 3 now */
945177633Sdfr			goto regular_rpcbind; /* Try different versions */
946177633Sdfr		} else {
947177633Sdfr			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
948177633Sdfr			clnt_geterr(client, &rpc_createerr.cf_error);
949177633Sdfr			goto error;
950177633Sdfr		}
951177633Sdfr	}
952177633Sdfr
953177633Sdfrregular_rpcbind:
954177633Sdfr
955177633Sdfr	/* Now the same transport is to be used to get the address */
956177633Sdfr	if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
957177633Sdfr			(nconf->nc_semantics == NC_TPI_COTS))) {
958177633Sdfr		/* A CLTS type of client - destroy it */
959177633Sdfr		CLNT_DESTROY(client);
960177633Sdfr		client = NULL;
961177633Sdfr	}
962177633Sdfr
963177633Sdfr	if (client == NULL) {
964177633Sdfr		client = getclnthandle(host, nconf, &parms.r_addr);
965177633Sdfr		if (client == NULL) {
966177633Sdfr			goto error;
967177633Sdfr		}
968177633Sdfr	}
969177633Sdfr	if (parms.r_addr == NULL) {
970177633Sdfr		/*LINTED const castaway*/
971177633Sdfr		parms.r_addr = (char *) &nullstring[0];
972177633Sdfr	}
973177633Sdfr
974177633Sdfr	/* First try from start_vers and then version 3 (RPCBVERS) */
975177633Sdfr
976177633Sdfr	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *) &rpcbrmttime);
977177633Sdfr	for (vers = start_vers;  vers >= RPCBVERS; vers--) {
978177633Sdfr		/* Set the version */
979177633Sdfr		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
980177633Sdfr		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
981177633Sdfr		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
982177633Sdfr		    (xdrproc_t) xdr_wrapstring, (char *)(void *) &ua, *tp);
983177633Sdfr		if (clnt_st == RPC_SUCCESS) {
984177633Sdfr			if ((ua == NULL) || (ua[0] == 0)) {
985177633Sdfr				/* address unknown */
986177633Sdfr				rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
987177633Sdfr				goto error;
988177633Sdfr			}
989177633Sdfr			address = uaddr2taddr(nconf, ua);
990177633Sdfr#ifdef ND_DEBUG
991177633Sdfr			fprintf(stderr, "\tRemote address is [%s]\n", ua);
992177633Sdfr			if (!address)
993177633Sdfr				fprintf(stderr,
994177633Sdfr					"\tCouldn't resolve remote address!\n");
995177633Sdfr#endif
996177633Sdfr			xdr_free((xdrproc_t)xdr_wrapstring,
997177633Sdfr			    (char *)(void *)&ua);
998177633Sdfr
999177633Sdfr			if (! address) {
1000177633Sdfr				/* We don't know about your universal address */
1001177633Sdfr				rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
1002177633Sdfr				goto error;
1003177633Sdfr			}
1004177633Sdfr			CLNT_CONTROL(client, CLGET_SVC_ADDR,
1005177633Sdfr			    (char *)(void *)&servaddr);
1006177633Sdfr			__rpc_fixup_addr(address, &servaddr);
1007177633Sdfr			goto done;
1008177633Sdfr		} else if (clnt_st == RPC_PROGVERSMISMATCH) {
1009177633Sdfr			struct rpc_err rpcerr;
1010177633Sdfr
1011177633Sdfr			clnt_geterr(client, &rpcerr);
1012177633Sdfr			if (rpcerr.re_vers.low > RPCBVERS4)
1013177633Sdfr				goto error;  /* a new version, can't handle */
1014177633Sdfr		} else if (clnt_st != RPC_PROGUNAVAIL) {
1015177633Sdfr			/* Cant handle this error */
1016177633Sdfr			rpc_createerr.cf_stat = clnt_st;
1017177633Sdfr			clnt_geterr(client, &rpc_createerr.cf_error);
1018177633Sdfr			goto error;
1019177633Sdfr		}
1020177633Sdfr	}
1021177633Sdfr
1022177633Sdfrerror:
1023177633Sdfr	if (client) {
1024177633Sdfr		CLNT_DESTROY(client);
1025177633Sdfr		client = NULL;
1026177633Sdfr	}
1027177633Sdfrdone:
1028177633Sdfr	if (nconf->nc_semantics != NC_TPI_CLTS) {
1029177633Sdfr		/* This client is the connectionless one */
1030177633Sdfr		if (client) {
1031177633Sdfr			CLNT_DESTROY(client);
1032177633Sdfr			client = NULL;
1033177633Sdfr		}
1034177633Sdfr	}
1035177633Sdfr	if (clpp) {
1036177633Sdfr		*clpp = client;
1037177633Sdfr	} else if (client) {
1038177633Sdfr		CLNT_DESTROY(client);
1039177633Sdfr	}
1040177633Sdfr	if (parms.r_addr != NULL && parms.r_addr != nullstring)
1041177633Sdfr		free(parms.r_addr);
1042177633Sdfr	return (address);
1043177633Sdfr}
1044177633Sdfr
1045177633Sdfr
1046177633Sdfr/*
1047177633Sdfr * Find the mapped address for program, version.
1048177633Sdfr * Calls the rpcbind service remotely to do the lookup.
1049177633Sdfr * Uses the transport specified in nconf.
1050177633Sdfr * Returns FALSE (0) if no map exists, else returns 1.
1051177633Sdfr *
1052177633Sdfr * Assuming that the address is all properly allocated
1053177633Sdfr */
1054177633Sdfrint
1055177633Sdfrrpcb_getaddr(program, version, nconf, address, host)
1056177633Sdfr	rpcprog_t program;
1057177633Sdfr	rpcvers_t version;
1058177633Sdfr	const struct netconfig *nconf;
1059177633Sdfr	struct netbuf *address;
1060177633Sdfr	const char *host;
1061177633Sdfr{
1062177633Sdfr	struct netbuf *na;
1063177633Sdfr
1064177633Sdfr	if ((na = __rpcb_findaddr_timed(program, version,
1065177633Sdfr	    (struct netconfig *) nconf, (char *) host,
1066177633Sdfr	    (CLIENT **) NULL, (struct timeval *) NULL)) == NULL)
1067177633Sdfr		return (FALSE);
1068177633Sdfr
1069177633Sdfr	if (na->len > address->maxlen) {
1070177633Sdfr		/* Too long address */
1071177633Sdfr		free(na->buf);
1072177633Sdfr		free(na);
1073177633Sdfr		rpc_createerr.cf_stat = RPC_FAILED;
1074177633Sdfr		return (FALSE);
1075177633Sdfr	}
1076177633Sdfr	memcpy(address->buf, na->buf, (size_t)na->len);
1077177633Sdfr	address->len = na->len;
1078177633Sdfr	free(na->buf);
1079177633Sdfr	free(na);
1080177633Sdfr	return (TRUE);
1081177633Sdfr}
1082177633Sdfr
1083177633Sdfr/*
1084177633Sdfr * Get a copy of the current maps.
1085177633Sdfr * Calls the rpcbind service remotely to get the maps.
1086177633Sdfr *
1087177633Sdfr * It returns only a list of the services
1088177633Sdfr * It returns NULL on failure.
1089177633Sdfr */
1090177633Sdfrrpcblist *
1091177633Sdfrrpcb_getmaps(nconf, host)
1092177633Sdfr	const struct netconfig *nconf;
1093177633Sdfr	const char *host;
1094177633Sdfr{
1095177633Sdfr	rpcblist_ptr head = NULL;
1096177633Sdfr	CLIENT *client;
1097177633Sdfr	enum clnt_stat clnt_st;
1098177633Sdfr	rpcvers_t vers = 0;
1099177633Sdfr
1100177633Sdfr	client = getclnthandle(host, nconf, NULL);
1101177633Sdfr	if (client == NULL) {
1102177633Sdfr		return (head);
1103177633Sdfr	}
1104177633Sdfr	clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
1105177633Sdfr	    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
1106177633Sdfr	    (char *)(void *)&head, tottimeout);
1107177633Sdfr	if (clnt_st == RPC_SUCCESS)
1108177633Sdfr		goto done;
1109177633Sdfr
1110177633Sdfr	if ((clnt_st != RPC_PROGVERSMISMATCH) &&
1111177633Sdfr	    (clnt_st != RPC_PROGUNAVAIL)) {
1112177633Sdfr		rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1113177633Sdfr		clnt_geterr(client, &rpc_createerr.cf_error);
1114177633Sdfr		goto done;
1115177633Sdfr	}
1116177633Sdfr
1117177633Sdfr	/* fall back to earlier version */
1118177633Sdfr	CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
1119177633Sdfr	if (vers == RPCBVERS4) {
1120177633Sdfr		vers = RPCBVERS;
1121177633Sdfr		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
1122177633Sdfr		if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
1123177633Sdfr		    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
1124177633Sdfr		    (char *)(void *)&head, tottimeout) == RPC_SUCCESS)
1125177633Sdfr			goto done;
1126177633Sdfr	}
1127177633Sdfr	rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1128177633Sdfr	clnt_geterr(client, &rpc_createerr.cf_error);
1129177633Sdfr
1130177633Sdfrdone:
1131177633Sdfr	CLNT_DESTROY(client);
1132177633Sdfr	return (head);
1133177633Sdfr}
1134177633Sdfr
1135177633Sdfr/*
1136177633Sdfr * rpcbinder remote-call-service interface.
1137177633Sdfr * This routine is used to call the rpcbind remote call service
1138177633Sdfr * which will look up a service program in the address maps, and then
1139177633Sdfr * remotely call that routine with the given parameters. This allows
1140177633Sdfr * programs to do a lookup and call in one step.
1141177633Sdfr*/
1142177633Sdfrenum clnt_stat
1143177633Sdfrrpcb_rmtcall(nconf, host, prog, vers, proc, xdrargs, argsp,
1144177633Sdfr		xdrres, resp, tout, addr_ptr)
1145177633Sdfr	const struct netconfig *nconf;	/* Netconfig structure */
1146177633Sdfr	const char *host;			/* Remote host name */
1147177633Sdfr	rpcprog_t prog;
1148177633Sdfr	rpcvers_t vers;
1149177633Sdfr	rpcproc_t proc;			/* Remote proc identifiers */
1150177633Sdfr	xdrproc_t xdrargs, xdrres;	/* XDR routines */
1151177633Sdfr	caddr_t argsp, resp;		/* Argument and Result */
1152177633Sdfr	struct timeval tout;		/* Timeout value for this call */
1153177633Sdfr	const struct netbuf *addr_ptr;	/* Preallocated netbuf address */
1154177633Sdfr{
1155177633Sdfr	CLIENT *client;
1156177633Sdfr	enum clnt_stat stat;
1157177633Sdfr	struct r_rpcb_rmtcallargs a;
1158177633Sdfr	struct r_rpcb_rmtcallres r;
1159177633Sdfr	rpcvers_t rpcb_vers;
1160177633Sdfr
1161177633Sdfr	stat = 0;
1162177633Sdfr	client = getclnthandle(host, nconf, NULL);
1163177633Sdfr	if (client == NULL) {
1164177633Sdfr		return (RPC_FAILED);
1165177633Sdfr	}
1166177633Sdfr	/*LINTED const castaway*/
1167177633Sdfr	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)(void *)&rmttimeout);
1168177633Sdfr	a.prog = prog;
1169177633Sdfr	a.vers = vers;
1170177633Sdfr	a.proc = proc;
1171177633Sdfr	a.args.args_val = argsp;
1172177633Sdfr	a.xdr_args = xdrargs;
1173177633Sdfr	r.addr = NULL;
1174177633Sdfr	r.results.results_val = resp;
1175177633Sdfr	r.xdr_res = xdrres;
1176177633Sdfr
1177177633Sdfr	for (rpcb_vers = RPCBVERS4; rpcb_vers >= RPCBVERS; rpcb_vers--) {
1178177633Sdfr		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&rpcb_vers);
1179177633Sdfr		stat = CLNT_CALL(client, (rpcproc_t)RPCBPROC_CALLIT,
1180177633Sdfr		    (xdrproc_t) xdr_rpcb_rmtcallargs, (char *)(void *)&a,
1181177633Sdfr		    (xdrproc_t) xdr_rpcb_rmtcallres, (char *)(void *)&r, tout);
1182177633Sdfr		if ((stat == RPC_SUCCESS) && (addr_ptr != NULL)) {
1183177633Sdfr			struct netbuf *na;
1184177633Sdfr			/*LINTED const castaway*/
1185177633Sdfr			na = uaddr2taddr((struct netconfig *) nconf, r.addr);
1186177633Sdfr			if (!na) {
1187177633Sdfr				stat = RPC_N2AXLATEFAILURE;
1188177633Sdfr				/*LINTED const castaway*/
1189177633Sdfr				((struct netbuf *) addr_ptr)->len = 0;
1190177633Sdfr				goto error;
1191177633Sdfr			}
1192177633Sdfr			if (na->len > addr_ptr->maxlen) {
1193177633Sdfr				/* Too long address */
1194177633Sdfr				stat = RPC_FAILED; /* XXX A better error no */
1195177633Sdfr				free(na->buf);
1196177633Sdfr				free(na);
1197177633Sdfr				/*LINTED const castaway*/
1198177633Sdfr				((struct netbuf *) addr_ptr)->len = 0;
1199177633Sdfr				goto error;
1200177633Sdfr			}
1201177633Sdfr			memcpy(addr_ptr->buf, na->buf, (size_t)na->len);
1202177633Sdfr			/*LINTED const castaway*/
1203177633Sdfr			((struct netbuf *)addr_ptr)->len = na->len;
1204177633Sdfr			free(na->buf);
1205177633Sdfr			free(na);
1206177633Sdfr			break;
1207177633Sdfr		} else if ((stat != RPC_PROGVERSMISMATCH) &&
1208177633Sdfr			    (stat != RPC_PROGUNAVAIL)) {
1209177633Sdfr			goto error;
1210177633Sdfr		}
1211177633Sdfr	}
1212177633Sdfrerror:
1213177633Sdfr	CLNT_DESTROY(client);
1214177633Sdfr	if (r.addr)
1215177633Sdfr		xdr_free((xdrproc_t) xdr_wrapstring, (char *)(void *)&r.addr);
1216177633Sdfr	return (stat);
1217177633Sdfr}
1218177633Sdfr
1219177633Sdfr/*
1220177633Sdfr * Gets the time on the remote host.
1221177633Sdfr * Returns 1 if succeeds else 0.
1222177633Sdfr */
1223177633Sdfrbool_t
1224177633Sdfrrpcb_gettime(host, timep)
1225177633Sdfr	const char *host;
1226177633Sdfr	time_t *timep;
1227177633Sdfr{
1228177633Sdfr	CLIENT *client = NULL;
1229177633Sdfr	void *handle;
1230177633Sdfr	struct netconfig *nconf;
1231177633Sdfr	rpcvers_t vers;
1232177633Sdfr	enum clnt_stat st;
1233177633Sdfr
1234177633Sdfr
1235177633Sdfr	if ((host == NULL) || (host[0] == 0)) {
1236177633Sdfr		time(timep);
1237177633Sdfr		return (TRUE);
1238177633Sdfr	}
1239177633Sdfr
1240177633Sdfr	if ((handle = __rpc_setconf("netpath")) == NULL) {
1241177633Sdfr		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1242177633Sdfr		return (FALSE);
1243177633Sdfr	}
1244177633Sdfr	rpc_createerr.cf_stat = RPC_SUCCESS;
1245177633Sdfr	while (client == NULL) {
1246177633Sdfr		if ((nconf = __rpc_getconf(handle)) == NULL) {
1247177633Sdfr			if (rpc_createerr.cf_stat == RPC_SUCCESS)
1248177633Sdfr				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1249177633Sdfr			break;
1250177633Sdfr		}
1251177633Sdfr		client = getclnthandle(host, nconf, NULL);
1252177633Sdfr		if (client)
1253177633Sdfr			break;
1254177633Sdfr	}
1255177633Sdfr	__rpc_endconf(handle);
1256177633Sdfr	if (client == (CLIENT *) NULL) {
1257177633Sdfr		return (FALSE);
1258177633Sdfr	}
1259177633Sdfr
1260177633Sdfr	st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
1261177633Sdfr		(xdrproc_t) xdr_void, NULL,
1262177633Sdfr		(xdrproc_t) xdr_int, (char *)(void *)timep, tottimeout);
1263177633Sdfr
1264177633Sdfr	if ((st == RPC_PROGVERSMISMATCH) || (st == RPC_PROGUNAVAIL)) {
1265177633Sdfr		CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
1266177633Sdfr		if (vers == RPCBVERS4) {
1267177633Sdfr			/* fall back to earlier version */
1268177633Sdfr			vers = RPCBVERS;
1269177633Sdfr			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
1270177633Sdfr			st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
1271177633Sdfr				(xdrproc_t) xdr_void, NULL,
1272177633Sdfr				(xdrproc_t) xdr_int, (char *)(void *)timep,
1273177633Sdfr				tottimeout);
1274177633Sdfr		}
1275177633Sdfr	}
1276177633Sdfr	CLNT_DESTROY(client);
1277177633Sdfr	return (st == RPC_SUCCESS? TRUE: FALSE);
1278177633Sdfr}
1279177633Sdfr
1280177633Sdfrstatic bool_t
1281177633Sdfrxdr_netbuf(XDR *xdrs, struct netbuf *objp)
1282177633Sdfr{
1283177633Sdfr	bool_t dummy;
1284177633Sdfr	void **pp;
1285177633Sdfr
1286177633Sdfr	if (!xdr_uint32_t(xdrs, (uint32_t *) &objp->maxlen)) {
1287177633Sdfr		return (FALSE);
1288177633Sdfr	}
1289177633Sdfr	pp = &objp->buf;
1290177633Sdfr	dummy = xdr_bytes(xdrs, (char **) pp,
1291177633Sdfr			(u_int *)&(objp->len), objp->maxlen);
1292177633Sdfr	return (dummy);
1293177633Sdfr}
1294177633Sdfr
1295177633Sdfr/*
1296177633Sdfr * Converts taddr to universal address.  This routine should never
1297177633Sdfr * really be called because local n2a libraries are always provided.
1298177633Sdfr */
1299177633Sdfrchar *
1300177633Sdfrrpcb_taddr2uaddr(struct netconfig *nconf, struct netbuf *taddr)
1301177633Sdfr{
1302177633Sdfr	CLIENT *client;
1303177633Sdfr	char *uaddr = NULL;
1304177633Sdfr
1305177633Sdfr
1306177633Sdfr	/* parameter checking */
1307177633Sdfr	if (nconf == NULL) {
1308177633Sdfr		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1309177633Sdfr		return (NULL);
1310177633Sdfr	}
1311177633Sdfr	if (taddr == NULL) {
1312177633Sdfr		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1313177633Sdfr		return (NULL);
1314177633Sdfr	}
1315177633Sdfr	client = local_rpcb();
1316177633Sdfr	if (! client) {
1317177633Sdfr		return (NULL);
1318177633Sdfr	}
1319177633Sdfr
1320177633Sdfr	CLNT_CALL(client, (rpcproc_t)RPCBPROC_TADDR2UADDR,
1321177633Sdfr	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
1322177633Sdfr	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr, tottimeout);
1323177633Sdfr	CLNT_DESTROY(client);
1324177633Sdfr	return (uaddr);
1325177633Sdfr}
1326177633Sdfr
1327177633Sdfr/*
1328177633Sdfr * Converts universal address to netbuf.  This routine should never
1329177633Sdfr * really be called because local n2a libraries are always provided.
1330177633Sdfr */
1331177633Sdfrstruct netbuf *
1332177633Sdfrrpcb_uaddr2taddr(struct netconfig *nconf, char *uaddr)
1333177633Sdfr{
1334177633Sdfr	CLIENT *client;
1335177633Sdfr	struct netbuf *taddr;
1336177633Sdfr
1337177633Sdfr
1338177633Sdfr	/* parameter checking */
1339177633Sdfr	if (nconf == NULL) {
1340177633Sdfr		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1341177633Sdfr		return (NULL);
1342177633Sdfr	}
1343177633Sdfr	if (uaddr == NULL) {
1344177633Sdfr		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1345177633Sdfr		return (NULL);
1346177633Sdfr	}
1347177633Sdfr	client = local_rpcb();
1348177633Sdfr	if (! client) {
1349177633Sdfr		return (NULL);
1350177633Sdfr	}
1351177633Sdfr
1352177633Sdfr	taddr = (struct netbuf *)malloc(sizeof (struct netbuf), M_RPC, M_WAITOK|M_ZERO);
1353177633Sdfr	if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_UADDR2TADDR,
1354177633Sdfr	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr,
1355177633Sdfr	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
1356177633Sdfr	    tottimeout) != RPC_SUCCESS) {
1357177633Sdfr		free(taddr);
1358177633Sdfr		taddr = NULL;
1359177633Sdfr	}
1360177633Sdfr	CLNT_DESTROY(client);
1361177633Sdfr	return (taddr);
1362177633Sdfr}
1363177633Sdfr
1364177633Sdfr#endif
1365