175295Sdes/*	$NetBSD: rpcb_clnt.c,v 1.6 2000/07/16 06:41:43 itojun Exp $	*/
2230132Suqs
375295Sdes/*-
475295Sdes * SPDX-License-Identifier: BSD-3-Clause
575295Sdes *
675295Sdes * Copyright (c) 2010, Oracle America, Inc.
775295Sdes * All rights reserved.
875295Sdes *
975295Sdes * Redistribution and use in source and binary forms, with or without
1075295Sdes * modification, are permitted provided that the following conditions are met:
1175295Sdes * - Redistributions of source code must retain the above copyright notice,
1275295Sdes *   this list of conditions and the following disclaimer.
1375295Sdes * - Redistributions in binary form must reproduce the above copyright notice,
1475295Sdes *   this list of conditions and the following disclaimer in the documentation
1575295Sdes *   and/or other materials provided with the distribution.
1675295Sdes * - Neither the name of the "Oracle America, Inc." nor the names of its
1775295Sdes *   contributors may be used to endorse or promote products derived
1875295Sdes *   from this software without specific prior written permission.
1975295Sdes *
2075295Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2175295Sdes * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2275295Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2375295Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
2475295Sdes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2575295Sdes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2675295Sdes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2775295Sdes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2875295Sdes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29143592Sdes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30143592Sdes * POSSIBILITY OF SUCH DAMAGE.
31143592Sdes */
32143592Sdes
33143592Sdes/*
3475295Sdes * rpcb_clnt.c
3575295Sdes * interface to rpcbind rpc service.
3675295Sdes */
3784811Sjhb
3875295Sdes#include "namespace.h"
3975295Sdes#include "reentrant.h"
4075295Sdes#include <sys/types.h>
4177965Sdes#include <sys/socket.h>
4275295Sdes#include <sys/un.h>
4375295Sdes#include <sys/utsname.h>
4475295Sdes#include <rpc/rpc.h>
4575295Sdes#include <rpc/rpcb_prot.h>
4675295Sdes#include <rpc/nettype.h>
4775295Sdes#include <netconfig.h>
4875295Sdes#ifdef PORTMAP
4975295Sdes#include <netinet/in.h>		/* FOR IPPROTO_TCP/UDP definitions */
5085128Sdes#include <rpc/pmap_prot.h>
5185128Sdes#endif				/* PORTMAP */
5275295Sdes#include <stdio.h>
5375295Sdes#include <errno.h>
5475295Sdes#include <stdlib.h>
55168764Sdes#include <string.h>
56168764Sdes#include <unistd.h>
57168764Sdes#include <netdb.h>
58168764Sdes#include <syslog.h>
5985940Sdes#include "un-namespace.h"
6085940Sdes
6185940Sdes#include "rpc_com.h"
6285940Sdes#include "mt_misc.h"
6375295Sdes
64168764Sdesstatic struct timeval tottimeout = { 60, 0 };
65168764Sdesstatic const struct timeval rmttimeout = { 3, 0 };
66168764Sdesstatic struct timeval rpcbrmttime = { 15, 0 };
67168764Sdes
68168764Sdesextern bool_t xdr_wrapstring(XDR *, char **);
69168764Sdes
70168764Sdesstatic const char nullstring[] = "\000";
71168764Sdes
72168764Sdes#define	CACHESIZE 6
73168764Sdes
74184205Sdesstruct address_cache {
75168764Sdes	char *ac_host;
76168764Sdes	char *ac_netid;
77168764Sdes	char *ac_uaddr;
78168764Sdes	struct netbuf *ac_taddr;
79168764Sdes	struct address_cache *ac_next;
80168764Sdes};
81168764Sdes
82168764Sdesstatic struct address_cache *front;
83168764Sdesstatic int cachesize;
8485128Sdes
8585128Sdes#define	CLCR_GET_RPCB_TIMEOUT	1
86168764Sdes#define	CLCR_SET_RPCB_TIMEOUT	2
87168764Sdes
8885128Sdes
89168764Sdesextern int __rpc_lowvers;
90168764Sdes
91168764Sdesstatic struct address_cache *check_cache(const char *, const char *);
92168764Sdesstatic void delete_cache(struct netbuf *);
9385128Sdesstatic void add_cache(const char *, const char *, struct netbuf *, char *);
9487599Sobrienstatic CLIENT *getclnthandle(const char *, const struct netconfig *, char **);
95168764Sdesstatic CLIENT *local_rpcb(void);
96168764Sdesstatic struct netbuf *got_entry(rpcb_entry_list_ptr, const struct netconfig *);
9785128Sdes
9887599Sobrien/*
9985128Sdes * This routine adjusts the timeout used for calls to the remote rpcbind.
10085128Sdes * Also, this routine can be used to set the use of portmapper version 2
10185128Sdes * only when doing rpc_broadcasts
10287599Sobrien * These are private routines that may not be provided in future releases.
10385128Sdes */
104168764Sdesbool_t
105168764Sdes__rpc_control(int request, void *info)
106168764Sdes{
107168764Sdes	switch (request) {
108168764Sdes	case CLCR_GET_RPCB_TIMEOUT:
109168764Sdes		*(struct timeval *)info = tottimeout;
110168764Sdes		break;
111168764Sdes	case CLCR_SET_RPCB_TIMEOUT:
112168764Sdes		tottimeout = *(struct timeval *)info;
113168764Sdes		break;
114168764Sdes	case CLCR_SET_LOWVERS:
115168764Sdes		__rpc_lowvers = *(int *)info;
116168764Sdes		break;
117168764Sdes	case CLCR_GET_LOWVERS:
11897940Sdes		*(int *)info = __rpc_lowvers;
119168764Sdes		break;
120168764Sdes	default:
121168764Sdes		return (FALSE);
122168764Sdes	}
123168764Sdes	return (TRUE);
124103314Snjl}
125168764Sdes
126168764Sdes/*
127168764Sdes *	It might seem that a reader/writer lock would be more reasonable here.
12885128Sdes *	However because getclnthandle(), the only user of the cache functions,
12985128Sdes *	may do a delete_cache() operation if a check_cache() fails to return an
13085128Sdes *	address useful to clnt_tli_create(), we may as well use a mutex.
131168764Sdes */
13285128Sdes/*
133168764Sdes * As it turns out, if the cache lock is *not* a reader/writer lock, we will
134168764Sdes * block all clnt_create's if we are trying to connect to a host that's down,
13585128Sdes * since the lock will be held all during that time.
136168764Sdes */
137168764Sdes
13897940Sdes/*
139168764Sdes * The routines check_cache(), add_cache(), delete_cache() manage the
140168764Sdes * cache of rpcbind addresses for (host, netid).
141168764Sdes */
14297940Sdes
143168764Sdesstatic struct address_cache *
144168764Sdescheck_cache(const char *host, const char *netid)
145168764Sdes{
146168764Sdes	struct address_cache *cptr;
147168764Sdes
148168764Sdes	/* READ LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
149168764Sdes
150168764Sdes	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
15185128Sdes		if (!strcmp(cptr->ac_host, host) &&
152168764Sdes		    !strcmp(cptr->ac_netid, netid)) {
153168764Sdes#ifdef ND_DEBUG
154168764Sdes			fprintf(stderr, "Found cache entry for %s: %s\n",
15597940Sdes				host, netid);
156168764Sdes#endif
157168764Sdes			return (cptr);
158168764Sdes		}
159168764Sdes	}
160168764Sdes	return ((struct address_cache *) NULL);
161168764Sdes}
162168764Sdes
16397940Sdesstatic void
164168764Sdesdelete_cache(struct netbuf *addr)
165168764Sdes{
166168764Sdes	struct address_cache *cptr, *prevptr = NULL;
167168764Sdes
16885128Sdes	/* WRITE LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
16985128Sdes	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
17085128Sdes		if (!memcmp(cptr->ac_taddr->buf, addr->buf, addr->len)) {
17185128Sdes			free(cptr->ac_host);
17285128Sdes			free(cptr->ac_netid);
17385128Sdes			free(cptr->ac_taddr->buf);
174123248Sdes			free(cptr->ac_taddr);
175167482Sdes			free(cptr->ac_uaddr);
176167482Sdes			if (prevptr)
17785128Sdes				prevptr->ac_next = cptr->ac_next;
178168764Sdes			else
17985128Sdes				front = cptr->ac_next;
180168764Sdes			free(cptr);
181168764Sdes			cachesize--;
182168764Sdes			break;
183168764Sdes		}
184168764Sdes		prevptr = cptr;
185168764Sdes	}
186168764Sdes}
187168764Sdes
18885128Sdesstatic void
189168764Sdesadd_cache(const char *host, const char *netid, struct netbuf *taddr,
19085128Sdes    char *uaddr)
19185128Sdes{
19285128Sdes	struct address_cache  *ad_cache, *cptr, *prevptr;
19385128Sdes
19485128Sdes	ad_cache = (struct address_cache *)
19585128Sdes			malloc(sizeof (struct address_cache));
196123248Sdes	if (!ad_cache) {
197167482Sdes		return;
198167482Sdes	}
19985128Sdes	ad_cache->ac_host = strdup(host);
200168764Sdes	ad_cache->ac_netid = strdup(netid);
20185128Sdes	ad_cache->ac_uaddr = uaddr ? strdup(uaddr) : NULL;
202168764Sdes	ad_cache->ac_taddr = (struct netbuf *)malloc(sizeof (struct netbuf));
203168764Sdes	if (!ad_cache->ac_host || !ad_cache->ac_netid || !ad_cache->ac_taddr ||
204168764Sdes		(uaddr && !ad_cache->ac_uaddr)) {
205168764Sdes		goto out;
206168764Sdes	}
207168764Sdes	ad_cache->ac_taddr->len = ad_cache->ac_taddr->maxlen = taddr->len;
208168764Sdes	ad_cache->ac_taddr->buf = (char *) malloc(taddr->len);
20985128Sdes	if (ad_cache->ac_taddr->buf == NULL) {
210168764Sdesout:
21185128Sdes		free(ad_cache->ac_host);
21285128Sdes		free(ad_cache->ac_netid);
21385128Sdes		free(ad_cache->ac_uaddr);
21485128Sdes		free(ad_cache->ac_taddr);
21585128Sdes		free(ad_cache);
21685128Sdes		return;
217123248Sdes	}
218168387Sdes	memcpy(ad_cache->ac_taddr->buf, taddr->buf, taddr->len);
219167482Sdes#ifdef ND_DEBUG
22085128Sdes	fprintf(stderr, "Added to cache: %s : %s\n", host, netid);
221168764Sdes#endif
22285128Sdes
223168764Sdes/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  cptr */
224168764Sdes
225168764Sdes	rwlock_wrlock(&rpcbaddr_cache_lock);
226168764Sdes	if (cachesize < CACHESIZE) {
227168764Sdes		ad_cache->ac_next = front;
228168764Sdes		front = ad_cache;
229168764Sdes		cachesize++;
230168764Sdes	} else {
231168764Sdes		/* Free the last entry */
23285128Sdes		cptr = front;
23385128Sdes		prevptr = NULL;
23485128Sdes		while (cptr->ac_next) {
235123248Sdes			prevptr = cptr;
236123248Sdes			cptr = cptr->ac_next;
237123248Sdes		}
238123248Sdes
239123248Sdes#ifdef ND_DEBUG
240168764Sdes		fprintf(stderr, "Deleted from cache: %s : %s\n",
241123248Sdes			cptr->ac_host, cptr->ac_netid);
242168764Sdes#endif
243168764Sdes		free(cptr->ac_host);
244168764Sdes		free(cptr->ac_netid);
245168720Sdes		free(cptr->ac_taddr->buf);
246168764Sdes		free(cptr->ac_taddr);
247168764Sdes		free(cptr->ac_uaddr);
248123248Sdes
249123248Sdes		if (prevptr) {
250123248Sdes			prevptr->ac_next = NULL;
251168764Sdes			ad_cache->ac_next = front;
252168764Sdes			front = ad_cache;
25385128Sdes		} else {
25485128Sdes			front = ad_cache;
255168764Sdes			ad_cache->ac_next = NULL;
25685128Sdes		}
257168764Sdes		free(cptr);
25897940Sdes	}
259168764Sdes	rwlock_unlock(&rpcbaddr_cache_lock);
26087599Sobrien}
261168764Sdes
26287599Sobrien/*
26385128Sdes * This routine will return a client handle that is connected to the
264168764Sdes * rpcbind. If targaddr is non-NULL, the "universal address" of the
265168764Sdes * host will be stored in *targaddr; the caller is responsible for
266168764Sdes * freeing this string.
26785128Sdes * On error, returns NULL and free's everything.
268168764Sdes */
269168764Sdesstatic CLIENT *
270168764Sdesgetclnthandle(const char *host, const struct netconfig *nconf, char **targaddr)
271168764Sdes{
272168764Sdes	CLIENT *client;
273168764Sdes	struct netbuf *addr, taddr;
274168764Sdes	struct netbuf addr_to_delete;
275168764Sdes	struct __rpc_sockinfo si;
276168764Sdes	struct addrinfo hints, *res, *tres;
277168764Sdes	struct address_cache *ad_cache;
278168764Sdes	char *tmpaddr;
27985128Sdes
280168764Sdes/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  ad_cache */
28185128Sdes
28285128Sdes	/* Get the address of the rpcbind.  Check cache first */
283168764Sdes	client = NULL;
284168764Sdes	addr_to_delete.len = 0;
285168764Sdes	rwlock_rdlock(&rpcbaddr_cache_lock);
286167482Sdes	ad_cache = NULL;
287168764Sdes	if (host != NULL)
288168764Sdes		ad_cache = check_cache(host, nconf->nc_netid);
289168387Sdes	if (ad_cache != NULL) {
290168764Sdes		addr = ad_cache->ac_taddr;
291168764Sdes		client = clnt_tli_create(RPC_ANYFD, nconf, addr,
292168764Sdes		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
293184205Sdes		if (client != NULL) {
29485128Sdes			if (targaddr)
29585128Sdes				*targaddr = strdup(ad_cache->ac_uaddr);
29685128Sdes			rwlock_unlock(&rpcbaddr_cache_lock);
29785128Sdes			return (client);
29885128Sdes		}
29975295Sdes		addr_to_delete.len = addr->len;
30075295Sdes		addr_to_delete.buf = (char *)malloc(addr->len);
30175295Sdes		if (addr_to_delete.buf == NULL) {
302191990Sattilio			addr_to_delete.len = 0;
30375295Sdes		} else {
30475295Sdes			memcpy(addr_to_delete.buf, addr->buf, addr->len);
30597940Sdes		}
30675295Sdes	}
30775295Sdes	rwlock_unlock(&rpcbaddr_cache_lock);
30897940Sdes	if (addr_to_delete.len != 0) {
309168637Sdes		/*
31075295Sdes		 * Assume this may be due to cache data being
311168637Sdes		 *  outdated
312172697Salfred		 */
31375295Sdes		rwlock_wrlock(&rpcbaddr_cache_lock);
31475295Sdes		delete_cache(&addr_to_delete);
31575295Sdes		rwlock_unlock(&rpcbaddr_cache_lock);
316138495Sphk		free(addr_to_delete.buf);
31775295Sdes	}
31875295Sdes	if (!__rpc_nconf2sockinfo(nconf, &si)) {
31975295Sdes		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
32075295Sdes		return NULL;
32175295Sdes	}
32275295Sdes
32375295Sdes	memset(&hints, 0, sizeof hints);
32475295Sdes	hints.ai_family = si.si_af;
32575295Sdes	hints.ai_socktype = si.si_socktype;
32675295Sdes	hints.ai_protocol = si.si_proto;
32775295Sdes
32875295Sdes#ifdef CLNT_DEBUG
329168764Sdes	printf("trying netid %s family %d proto %d socktype %d\n",
330158611Skbyanc	    nconf->nc_netid, si.si_af, si.si_proto, si.si_socktype);
331158611Skbyanc#endif
332230249Smckusick
333158611Skbyanc	if (nconf->nc_protofmly != NULL && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
334168764Sdes		client = local_rpcb();
335168764Sdes		if (! client) {
336168764Sdes#ifdef ND_DEBUG
337168764Sdes			clnt_pcreateerror("rpcbind clnt interface");
338158611Skbyanc#endif
339158611Skbyanc			return (NULL);
340158611Skbyanc		} else {
34175295Sdes			struct sockaddr_un sun;
34275295Sdes			if (targaddr) {
34375295Sdes			    *targaddr = malloc(sizeof(sun.sun_path));
344191990Sattilio			    if (*targaddr == NULL) {
34575295Sdes				CLNT_DESTROY(client);
34675295Sdes				return (NULL);
34775295Sdes			    }
348191990Sattilio			    strncpy(*targaddr, _PATH_RPCBINDSOCK,
349191990Sattilio				sizeof(sun.sun_path));
35075295Sdes			}
35175295Sdes			return (client);
35275295Sdes		}
35375295Sdes	} else {
35475295Sdes		if (getaddrinfo(host, "sunrpc", &hints, &res) != 0) {
35575295Sdes			rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
35675295Sdes			return NULL;
357191990Sattilio		}
35875295Sdes	}
35975295Sdes
36075295Sdes	for (tres = res; tres != NULL; tres = tres->ai_next) {
36175295Sdes		taddr.buf = tres->ai_addr;
362168764Sdes		taddr.len = taddr.maxlen = tres->ai_addrlen;
36375295Sdes
36475295Sdes#ifdef ND_DEBUG
36575295Sdes		{
36675295Sdes			char *ua;
36775295Sdes
36875295Sdes			ua = taddr2uaddr(nconf, &taddr);
369191990Sattilio			fprintf(stderr, "Got it [%s]\n", ua);
37075295Sdes			free(ua);
371138495Sphk		}
37275295Sdes#endif
37375295Sdes
37475295Sdes#ifdef ND_DEBUG
37575295Sdes		{
37675295Sdes			int i;
37775295Sdes
37875295Sdes			fprintf(stderr, "\tnetbuf len = %d, maxlen = %d\n",
37975295Sdes				taddr.len, taddr.maxlen);
38075295Sdes			fprintf(stderr, "\tAddress is ");
38185128Sdes			for (i = 0; i < taddr.len; i++)
38285128Sdes				fprintf(stderr, "%u.", ((char *)(taddr.buf))[i]);
38385128Sdes			fprintf(stderr, "\n");
384168720Sdes		}
38597940Sdes#endif
386168764Sdes		client = clnt_tli_create(RPC_ANYFD, nconf, &taddr,
387168764Sdes		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
38885128Sdes#ifdef ND_DEBUG
389168764Sdes		if (! client) {
39085128Sdes			clnt_pcreateerror("rpcbind clnt interface");
391168764Sdes		}
392168764Sdes#endif
39385128Sdes
39485128Sdes		if (client) {
39585128Sdes			tmpaddr = targaddr ? taddr2uaddr(nconf, &taddr) : NULL;
39685128Sdes			add_cache(host, nconf->nc_netid, &taddr, tmpaddr);
39785128Sdes			if (targaddr)
39885128Sdes				*targaddr = tmpaddr;
39985128Sdes			break;
40085128Sdes		}
40197940Sdes	}
40284383Sdes	if (res)
40384383Sdes		freeaddrinfo(res);
40475295Sdes	return (client);
40575295Sdes}
40675295Sdes
40775295Sdes/* XXX */
40875295Sdes#define IN4_LOCALHOST_STRING	"127.0.0.1"
40975295Sdes#define IN6_LOCALHOST_STRING	"::1"
41075295Sdes
41175295Sdes/*
41275295Sdes * This routine will return a client handle that is connected to the local
41385128Sdes * rpcbind. Returns NULL on error and free's everything.
41497940Sdes */
415168720Sdesstatic CLIENT *
416168720Sdeslocal_rpcb(void)
41785128Sdes{
41885128Sdes	CLIENT *client;
419168637Sdes	static struct netconfig *loopnconf;
42084383Sdes	static char *hostname;
42184383Sdes	int sock;
42285128Sdes	size_t tsize;
42385128Sdes	struct netbuf nbuf;
42475295Sdes	struct sockaddr_un sun;
42575295Sdes
42675295Sdes	/*
42775295Sdes	 * Try connecting to the local rpcbind through a local socket
42875295Sdes	 * first. If this doesn't work, try all transports defined in
42975295Sdes	 * the netconfig file.
43075295Sdes	 */
43175295Sdes	memset(&sun, 0, sizeof sun);
43275295Sdes	sock = _socket(AF_LOCAL, SOCK_STREAM, 0);
43375295Sdes	if (sock < 0)
43475295Sdes		goto try_nconf;
43575295Sdes	sun.sun_family = AF_LOCAL;
43675295Sdes	strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
43775295Sdes	nbuf.len = sun.sun_len = SUN_LEN(&sun);
43875295Sdes	nbuf.maxlen = sizeof (struct sockaddr_un);
43975295Sdes	nbuf.buf = &sun;
44075295Sdes
441132199Sphk	tsize = __rpc_get_t_size(AF_LOCAL, 0, 0);
44275295Sdes	client = clnt_vc_create(sock, &nbuf, (rpcprog_t)RPCBPROG,
44375295Sdes	    (rpcvers_t)RPCBVERS, tsize, tsize);
44475295Sdes
44575295Sdes	if (client != NULL) {
44675295Sdes		/* Mark the socket to be closed in destructor */
44775295Sdes		(void) CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
44875295Sdes		return client;
44975295Sdes	}
45075295Sdes
45175295Sdes	/* Nobody needs this socket anymore; free the descriptor. */
45275295Sdes	_close(sock);
45375295Sdes
45475295Sdestry_nconf:
45575295Sdes
45686969Sdes/* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */
457	mutex_lock(&loopnconf_lock);
458	if (loopnconf == NULL) {
459		struct netconfig *nconf, *tmpnconf = NULL;
460		void *nc_handle;
461		int fd;
462
463		nc_handle = setnetconfig();
464		if (nc_handle == NULL) {
465			/* fails to open netconfig file */
466			syslog (LOG_ERR, "rpc: failed to open " NETCONFIG);
467			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
468			mutex_unlock(&loopnconf_lock);
469			return (NULL);
470		}
471		while ((nconf = getnetconfig(nc_handle)) != NULL) {
472#ifdef INET6
473			if ((strcmp(nconf->nc_protofmly, NC_INET6) == 0 ||
474#else
475			if ((
476#endif
477			     strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
478			    (nconf->nc_semantics == NC_TPI_COTS ||
479			     nconf->nc_semantics == NC_TPI_COTS_ORD)) {
480				fd = __rpc_nconf2fd(nconf);
481				/*
482				 * Can't create a socket, assume that
483				 * this family isn't configured in the kernel.
484				 */
485				if (fd < 0)
486					continue;
487				_close(fd);
488				tmpnconf = nconf;
489				if (!strcmp(nconf->nc_protofmly, NC_INET))
490					hostname = IN4_LOCALHOST_STRING;
491				else
492					hostname = IN6_LOCALHOST_STRING;
493			}
494		}
495		if (tmpnconf == NULL) {
496			endnetconfig(nc_handle);
497			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
498			mutex_unlock(&loopnconf_lock);
499			return (NULL);
500		}
501		loopnconf = getnetconfigent(tmpnconf->nc_netid);
502		/* loopnconf is never freed */
503		endnetconfig(nc_handle);
504	}
505	mutex_unlock(&loopnconf_lock);
506	client = getclnthandle(hostname, loopnconf, NULL);
507	return (client);
508}
509
510/*
511 * Set a mapping between program, version and address.
512 * Calls the rpcbind service to do the mapping.
513 *
514 * nconf   - Network structure of transport
515 * address - Services netconfig address
516 */
517bool_t
518rpcb_set(rpcprog_t program, rpcvers_t version, const struct netconfig *nconf,
519    const struct netbuf *address)
520{
521	CLIENT *client;
522	bool_t rslt = FALSE;
523	RPCB parms;
524	char uidbuf[32];
525
526	/* parameter checking */
527	if (nconf == NULL) {
528		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
529		return (FALSE);
530	}
531	if (address == NULL) {
532		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
533		return (FALSE);
534	}
535	client = local_rpcb();
536	if (! client) {
537		return (FALSE);
538	}
539
540	/* convert to universal */
541	/*LINTED const castaway*/
542	parms.r_addr = taddr2uaddr((struct netconfig *) nconf,
543				   (struct netbuf *)address);
544	if (!parms.r_addr) {
545		CLNT_DESTROY(client);
546		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
547		return (FALSE); /* no universal address */
548	}
549	parms.r_prog = program;
550	parms.r_vers = version;
551	parms.r_netid = nconf->nc_netid;
552	/*
553	 * Though uid is not being used directly, we still send it for
554	 * completeness.  For non-unix platforms, perhaps some other
555	 * string or an empty string can be sent.
556	 */
557	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
558	parms.r_owner = uidbuf;
559
560	CLNT_CALL(client, (rpcproc_t)RPCBPROC_SET, (xdrproc_t) xdr_rpcb,
561	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
562	    (char *)(void *)&rslt, tottimeout);
563
564	CLNT_DESTROY(client);
565	free(parms.r_addr);
566	return (rslt);
567}
568
569/*
570 * Remove the mapping between program, version and netbuf address.
571 * Calls the rpcbind service to do the un-mapping.
572 * If netbuf is NULL, unset for all the transports, otherwise unset
573 * only for the given transport.
574 */
575bool_t
576rpcb_unset(rpcprog_t program, rpcvers_t version, const struct netconfig *nconf)
577{
578	CLIENT *client;
579	bool_t rslt = FALSE;
580	RPCB parms;
581	char uidbuf[32];
582
583	client = local_rpcb();
584	if (! client) {
585		return (FALSE);
586	}
587
588	parms.r_prog = program;
589	parms.r_vers = version;
590	if (nconf)
591		parms.r_netid = nconf->nc_netid;
592	else {
593		/*LINTED const castaway*/
594		parms.r_netid = (char *) &nullstring[0]; /* unsets  all */
595	}
596	/*LINTED const castaway*/
597	parms.r_addr = (char *) &nullstring[0];
598	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
599	parms.r_owner = uidbuf;
600
601	CLNT_CALL(client, (rpcproc_t)RPCBPROC_UNSET, (xdrproc_t) xdr_rpcb,
602	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
603	    (char *)(void *)&rslt, tottimeout);
604
605	CLNT_DESTROY(client);
606	return (rslt);
607}
608
609/*
610 * From the merged list, find the appropriate entry
611 */
612static struct netbuf *
613got_entry(rpcb_entry_list_ptr relp, const struct netconfig *nconf)
614{
615	struct netbuf *na = NULL;
616	rpcb_entry_list_ptr sp;
617	rpcb_entry *rmap;
618
619	for (sp = relp; sp != NULL; sp = sp->rpcb_entry_next) {
620		rmap = &sp->rpcb_entry_map;
621		if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) &&
622		    (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) &&
623		    (nconf->nc_semantics == rmap->r_nc_semantics) &&
624		    (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != 0)) {
625			na = uaddr2taddr(nconf, rmap->r_maddr);
626#ifdef ND_DEBUG
627			fprintf(stderr, "\tRemote address is [%s].\n",
628				rmap->r_maddr);
629			if (!na)
630				fprintf(stderr,
631				    "\tCouldn't resolve remote address!\n");
632#endif
633			break;
634		}
635	}
636	return (na);
637}
638
639/*
640 * Quick check to see if rpcbind is up.  Tries to connect over
641 * local transport.
642 */
643static bool_t
644__rpcbind_is_up(void)
645{
646	struct netconfig *nconf;
647	struct sockaddr_un sun;
648	void *localhandle;
649	int sock;
650
651	nconf = NULL;
652	localhandle = setnetconfig();
653	while ((nconf = getnetconfig(localhandle)) != NULL) {
654		if (nconf->nc_protofmly != NULL &&
655		    strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)
656			 break;
657	}
658	endnetconfig(localhandle);
659
660	if (nconf == NULL)
661		return (FALSE);
662
663	memset(&sun, 0, sizeof sun);
664	sock = _socket(AF_LOCAL, SOCK_STREAM, 0);
665	if (sock < 0)
666		return (FALSE);
667	sun.sun_family = AF_LOCAL;
668	strncpy(sun.sun_path, _PATH_RPCBINDSOCK, sizeof(sun.sun_path));
669	sun.sun_len = SUN_LEN(&sun);
670
671	if (_connect(sock, (struct sockaddr *)&sun, sun.sun_len) < 0) {
672		_close(sock);
673		return (FALSE);
674	}
675
676	_close(sock);
677	return (TRUE);
678}
679
680/*
681 * An internal function which optimizes rpcb_getaddr function.  It also
682 * returns the client handle that it uses to contact the remote rpcbind.
683 *
684 * The algorithm used: If the transports is TCP or UDP, it first tries
685 * version 2 (portmap), 4 and then 3 (svr4).  This order should be
686 * changed in the next OS release to 4, 2 and 3.  We are assuming that by
687 * that time, version 4 would be available on many machines on the network.
688 * With this algorithm, we get performance as well as a plan for
689 * obsoleting version 2.
690 *
691 * For all other transports, the algorithm remains as 4 and then 3.
692 *
693 * XXX: Due to some problems with t_connect(), we do not reuse the same client
694 * handle for COTS cases and hence in these cases we do not return the
695 * client handle.  This code will change if t_connect() ever
696 * starts working properly.  Also look under clnt_vc.c.
697 */
698struct netbuf *
699__rpcb_findaddr_timed(rpcprog_t program, rpcvers_t version,
700    const struct netconfig *nconf, const char *host,
701    CLIENT **clpp, struct timeval *tp)
702{
703	static bool_t check_rpcbind = TRUE;
704	CLIENT *client = NULL;
705	RPCB parms;
706	enum clnt_stat clnt_st;
707	char *ua = NULL;
708	rpcvers_t vers;
709	struct netbuf *address = NULL;
710	rpcvers_t start_vers = RPCBVERS4;
711	struct netbuf servaddr;
712
713	/* parameter checking */
714	if (nconf == NULL) {
715		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
716		return (NULL);
717	}
718
719	parms.r_addr = NULL;
720
721	/*
722	 * Use default total timeout if no timeout is specified.
723	 */
724	if (tp == NULL)
725		tp = &tottimeout;
726
727#ifdef PORTMAP
728	/* Try version 2 for TCP or UDP */
729	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
730		u_short port = 0;
731		struct netbuf remote;
732		rpcvers_t pmapvers = 2;
733		struct pmap pmapparms;
734
735		/*
736		 * The comment below is now very old, having
737		 * been committed to FreeBSD during an import
738		 * from NetBSD in 2001.  I do not believe there
739		 * will still be any rpcbind servers that do
740		 * UDP only and, since Azure requires use of
741		 * TCP for NFSv3 mounts, comment this out
742		 * so that NFSv3 mounts on Azure can work.
743		 */
744#ifdef notnow
745		/*
746		 * Try UDP only - there are some portmappers out
747		 * there that use UDP only.
748		 */
749		if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
750			struct netconfig *newnconf;
751
752			if ((newnconf = getnetconfigent("udp")) == NULL) {
753				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
754				return (NULL);
755			}
756			client = getclnthandle(host, newnconf, &parms.r_addr);
757			freenetconfigent(newnconf);
758		} else
759#endif
760			client = getclnthandle(host, nconf, &parms.r_addr);
761		if (client == NULL)
762			return (NULL);
763
764		/*
765		 * Set version and retry timeout.
766		 */
767		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
768		CLNT_CONTROL(client, CLSET_VERS, (char *)&pmapvers);
769
770		pmapparms.pm_prog = program;
771		pmapparms.pm_vers = version;
772		pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ?
773					IPPROTO_UDP : IPPROTO_TCP;
774		pmapparms.pm_port = 0;	/* not needed */
775		clnt_st = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
776		    (xdrproc_t) xdr_pmap, (caddr_t)(void *)&pmapparms,
777		    (xdrproc_t) xdr_u_short, (caddr_t)(void *)&port,
778		    *tp);
779		if (clnt_st != RPC_SUCCESS) {
780			if ((clnt_st == RPC_PROGVERSMISMATCH) ||
781				(clnt_st == RPC_PROGUNAVAIL))
782				goto try_rpcbind; /* Try different versions */
783			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
784			clnt_geterr(client, &rpc_createerr.cf_error);
785			goto error;
786		} else if (port == 0) {
787			address = NULL;
788			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
789			goto error;
790		}
791		port = htons(port);
792		CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)&remote);
793		if (((address = (struct netbuf *)
794			malloc(sizeof (struct netbuf))) == NULL) ||
795		    ((address->buf = (char *)
796			malloc(remote.len)) == NULL)) {
797			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
798			clnt_geterr(client, &rpc_createerr.cf_error);
799			free(address);
800			address = NULL;
801			goto error;
802		}
803		memcpy(address->buf, remote.buf, remote.len);
804		memcpy(&((char *)address->buf)[sizeof (short)],
805				(char *)(void *)&port, sizeof (short));
806		address->len = address->maxlen = remote.len;
807		goto done;
808	}
809#endif				/* PORTMAP */
810
811try_rpcbind:
812	/*
813	 * Check if rpcbind is up.  This prevents needless delays when
814	 * accessing applications such as the keyserver while booting
815	 * disklessly.
816	 */
817	if (check_rpcbind && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
818		if (!__rpcbind_is_up()) {
819			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
820			rpc_createerr.cf_error.re_errno = 0;
821			goto error;
822		}
823		check_rpcbind = FALSE;
824	}
825
826	/*
827	 * Now we try version 4 and then 3.
828	 * We also send the remote system the address we used to
829	 * contact it in case it can help to connect back with us
830	 */
831	parms.r_prog = program;
832	parms.r_vers = version;
833	/*LINTED const castaway*/
834	parms.r_owner = (char *) &nullstring[0];	/* not needed; */
835							/* just for xdring */
836	parms.r_netid = nconf->nc_netid; /* not really needed */
837
838	/*
839	 * If a COTS transport is being used, try getting address via CLTS
840	 * transport.  This works only with version 4.
841	 */
842	if (nconf->nc_semantics == NC_TPI_COTS_ORD ||
843			nconf->nc_semantics == NC_TPI_COTS) {
844
845		void *handle;
846		struct netconfig *nconf_clts;
847		rpcb_entry_list_ptr relp = NULL;
848
849		if (client == NULL) {
850			/* This did not go through the above PORTMAP/TCP code */
851			if ((handle = __rpc_setconf("datagram_v")) != NULL) {
852				while ((nconf_clts = __rpc_getconf(handle))
853					!= NULL) {
854					if (strcmp(nconf_clts->nc_protofmly,
855						nconf->nc_protofmly) != 0) {
856						continue;
857					}
858					client = getclnthandle(host, nconf_clts,
859							&parms.r_addr);
860					break;
861				}
862				__rpc_endconf(handle);
863			}
864			if (client == NULL)
865				goto regular_rpcbind;	/* Go the regular way */
866		} else {
867			/* This is a UDP PORTMAP handle.  Change to version 4 */
868			vers = RPCBVERS4;
869			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
870		}
871		/*
872		 * We also send the remote system the address we used to
873		 * contact it in case it can help it connect back with us
874		 */
875		if (parms.r_addr == NULL) {
876			/*LINTED const castaway*/
877			parms.r_addr = (char *) &nullstring[0]; /* for XDRing */
878		}
879
880		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
881
882		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDRLIST,
883		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
884		    (xdrproc_t) xdr_rpcb_entry_list_ptr,
885		    (char *)(void *)&relp, *tp);
886		if (clnt_st == RPC_SUCCESS) {
887			if ((address = got_entry(relp, nconf)) != NULL) {
888				xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
889				    (char *)(void *)&relp);
890				CLNT_CONTROL(client, CLGET_SVC_ADDR,
891					(char *)(void *)&servaddr);
892				__rpc_fixup_addr(address, &servaddr);
893				goto done;
894			}
895			/* Entry not found for this transport */
896			xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
897			    (char *)(void *)&relp);
898			/*
899			 * XXX: should have perhaps returned with error but
900			 * since the remote machine might not always be able
901			 * to send the address on all transports, we try the
902			 * regular way with regular_rpcbind
903			 */
904			goto regular_rpcbind;
905		} else if ((clnt_st == RPC_PROGVERSMISMATCH) ||
906			(clnt_st == RPC_PROGUNAVAIL)) {
907			start_vers = RPCBVERS;	/* Try version 3 now */
908			goto regular_rpcbind; /* Try different versions */
909		} else {
910			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
911			clnt_geterr(client, &rpc_createerr.cf_error);
912			goto error;
913		}
914	}
915
916regular_rpcbind:
917
918	/* Now the same transport is to be used to get the address */
919	if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
920			(nconf->nc_semantics == NC_TPI_COTS))) {
921		/* A CLTS type of client - destroy it */
922		CLNT_DESTROY(client);
923		client = NULL;
924	}
925
926	if (client == NULL) {
927		client = getclnthandle(host, nconf, &parms.r_addr);
928		if (client == NULL) {
929			goto error;
930		}
931	}
932	if (parms.r_addr == NULL) {
933		/*LINTED const castaway*/
934		parms.r_addr = (char *) &nullstring[0];
935	}
936
937	/* First try from start_vers and then version 3 (RPCBVERS) */
938
939	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *) &rpcbrmttime);
940	for (vers = start_vers;  vers >= RPCBVERS; vers--) {
941		/* Set the version */
942		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
943		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
944		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
945		    (xdrproc_t) xdr_wrapstring, (char *)(void *) &ua, *tp);
946		if (clnt_st == RPC_SUCCESS) {
947			if ((ua == NULL) || (ua[0] == 0)) {
948				/* address unknown */
949				rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
950				goto error;
951			}
952			address = uaddr2taddr(nconf, ua);
953#ifdef ND_DEBUG
954			fprintf(stderr, "\tRemote address is [%s]\n", ua);
955			if (!address)
956				fprintf(stderr,
957					"\tCouldn't resolve remote address!\n");
958#endif
959			xdr_free((xdrproc_t)xdr_wrapstring,
960			    (char *)(void *)&ua);
961
962			if (! address) {
963				/* We don't know about your universal address */
964				rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
965				goto error;
966			}
967			CLNT_CONTROL(client, CLGET_SVC_ADDR,
968			    (char *)(void *)&servaddr);
969			__rpc_fixup_addr(address, &servaddr);
970			goto done;
971		} else if (clnt_st == RPC_PROGVERSMISMATCH) {
972			struct rpc_err rpcerr;
973
974			clnt_geterr(client, &rpcerr);
975			if (rpcerr.re_vers.low > RPCBVERS4)
976				goto error;  /* a new version, can't handle */
977		} else if (clnt_st != RPC_PROGUNAVAIL) {
978			/* Cant handle this error */
979			rpc_createerr.cf_stat = clnt_st;
980			clnt_geterr(client, &rpc_createerr.cf_error);
981			goto error;
982		}
983	}
984
985error:
986	if (client) {
987		CLNT_DESTROY(client);
988		client = NULL;
989	}
990done:
991	if (nconf->nc_semantics != NC_TPI_CLTS) {
992		/* This client is the connectionless one */
993		if (client) {
994			CLNT_DESTROY(client);
995			client = NULL;
996		}
997	}
998	if (clpp) {
999		*clpp = client;
1000	} else if (client) {
1001		CLNT_DESTROY(client);
1002	}
1003	if (parms.r_addr != NULL && parms.r_addr != nullstring)
1004		free(parms.r_addr);
1005	return (address);
1006}
1007
1008
1009/*
1010 * Find the mapped address for program, version.
1011 * Calls the rpcbind service remotely to do the lookup.
1012 * Uses the transport specified in nconf.
1013 * Returns FALSE (0) if no map exists, else returns 1.
1014 *
1015 * Assuming that the address is all properly allocated
1016 */
1017bool_t
1018rpcb_getaddr(rpcprog_t program, rpcvers_t version, const struct netconfig *nconf,
1019    struct netbuf *address, const char *host)
1020{
1021	struct netbuf *na;
1022
1023	if ((na = __rpcb_findaddr_timed(program, version,
1024	    (struct netconfig *) nconf, (char *) host,
1025	    (CLIENT **) NULL, (struct timeval *) NULL)) == NULL)
1026		return (FALSE);
1027
1028	if (na->len > address->maxlen) {
1029		/* Too long address */
1030		free(na->buf);
1031		free(na);
1032		rpc_createerr.cf_stat = RPC_FAILED;
1033		return (FALSE);
1034	}
1035	memcpy(address->buf, na->buf, (size_t)na->len);
1036	address->len = na->len;
1037	free(na->buf);
1038	free(na);
1039	return (TRUE);
1040}
1041
1042/*
1043 * Get a copy of the current maps.
1044 * Calls the rpcbind service remotely to get the maps.
1045 *
1046 * It returns only a list of the services
1047 * It returns NULL on failure.
1048 */
1049rpcblist *
1050rpcb_getmaps(const struct netconfig *nconf, const char *host)
1051{
1052	rpcblist_ptr head = NULL;
1053	CLIENT *client;
1054	enum clnt_stat clnt_st;
1055	rpcvers_t vers = 0;
1056
1057	client = getclnthandle(host, nconf, NULL);
1058	if (client == NULL) {
1059		return (head);
1060	}
1061	clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
1062	    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
1063	    (char *)(void *)&head, tottimeout);
1064	if (clnt_st == RPC_SUCCESS)
1065		goto done;
1066
1067	if ((clnt_st != RPC_PROGVERSMISMATCH) &&
1068	    (clnt_st != RPC_PROGUNAVAIL)) {
1069		rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1070		clnt_geterr(client, &rpc_createerr.cf_error);
1071		goto done;
1072	}
1073
1074	/* fall back to earlier version */
1075	CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
1076	if (vers == RPCBVERS4) {
1077		vers = RPCBVERS;
1078		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
1079		if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
1080		    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
1081		    (char *)(void *)&head, tottimeout) == RPC_SUCCESS)
1082			goto done;
1083	}
1084	rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1085	clnt_geterr(client, &rpc_createerr.cf_error);
1086
1087done:
1088	CLNT_DESTROY(client);
1089	return (head);
1090}
1091
1092/*
1093 * rpcbinder remote-call-service interface.
1094 * This routine is used to call the rpcbind remote call service
1095 * which will look up a service program in the address maps, and then
1096 * remotely call that routine with the given parameters. This allows
1097 * programs to do a lookup and call in one step.
1098 *
1099 * nconf    -Netconfig structure
1100 * host     - Remote host name
1101 * proc     - Remote proc identifiers
1102 * xdrargs, xdrres;  XDR routines
1103 * argsp, resp - Argument and Result
1104 * tout     - Timeout value for this call
1105 * addr_ptr - Preallocated netbuf address
1106 */
1107enum clnt_stat
1108rpcb_rmtcall(const struct netconfig *nconf, const char *host, rpcprog_t prog,
1109    rpcvers_t vers, rpcproc_t proc, xdrproc_t xdrargs, caddr_t argsp,
1110    xdrproc_t xdrres, caddr_t resp, struct timeval tout,
1111    const struct netbuf *addr_ptr)
1112{
1113	CLIENT *client;
1114	enum clnt_stat stat;
1115	struct r_rpcb_rmtcallargs a;
1116	struct r_rpcb_rmtcallres r;
1117	rpcvers_t rpcb_vers;
1118
1119	stat = 0;
1120	client = getclnthandle(host, nconf, NULL);
1121	if (client == NULL) {
1122		return (RPC_FAILED);
1123	}
1124	/*LINTED const castaway*/
1125	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)(void *)&rmttimeout);
1126	a.prog = prog;
1127	a.vers = vers;
1128	a.proc = proc;
1129	a.args.args_val = argsp;
1130	a.xdr_args = xdrargs;
1131	r.addr = NULL;
1132	r.results.results_val = resp;
1133	r.xdr_res = xdrres;
1134
1135	for (rpcb_vers = RPCBVERS4; rpcb_vers >= RPCBVERS; rpcb_vers--) {
1136		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&rpcb_vers);
1137		stat = CLNT_CALL(client, (rpcproc_t)RPCBPROC_CALLIT,
1138		    (xdrproc_t) xdr_rpcb_rmtcallargs, (char *)(void *)&a,
1139		    (xdrproc_t) xdr_rpcb_rmtcallres, (char *)(void *)&r, tout);
1140		if ((stat == RPC_SUCCESS) && (addr_ptr != NULL)) {
1141			struct netbuf *na;
1142			/*LINTED const castaway*/
1143			na = uaddr2taddr((struct netconfig *) nconf, r.addr);
1144			if (!na) {
1145				stat = RPC_N2AXLATEFAILURE;
1146				/*LINTED const castaway*/
1147				((struct netbuf *) addr_ptr)->len = 0;
1148				goto error;
1149			}
1150			if (na->len > addr_ptr->maxlen) {
1151				/* Too long address */
1152				stat = RPC_FAILED; /* XXX A better error no */
1153				free(na->buf);
1154				free(na);
1155				/*LINTED const castaway*/
1156				((struct netbuf *) addr_ptr)->len = 0;
1157				goto error;
1158			}
1159			memcpy(addr_ptr->buf, na->buf, (size_t)na->len);
1160			/*LINTED const castaway*/
1161			((struct netbuf *)addr_ptr)->len = na->len;
1162			free(na->buf);
1163			free(na);
1164			break;
1165		} else if ((stat != RPC_PROGVERSMISMATCH) &&
1166			    (stat != RPC_PROGUNAVAIL)) {
1167			goto error;
1168		}
1169	}
1170error:
1171	CLNT_DESTROY(client);
1172	if (r.addr)
1173		xdr_free((xdrproc_t) xdr_wrapstring, (char *)(void *)&r.addr);
1174	return (stat);
1175}
1176
1177/*
1178 * Gets the time on the remote host.
1179 * Returns 1 if succeeds else 0.
1180 */
1181bool_t
1182rpcb_gettime(const char *host, time_t *timep)
1183{
1184	CLIENT *client = NULL;
1185	void *handle;
1186	struct netconfig *nconf;
1187	rpcvers_t vers;
1188	enum clnt_stat st;
1189
1190
1191	if ((host == NULL) || (host[0] == 0)) {
1192		time(timep);
1193		return (TRUE);
1194	}
1195
1196	if ((handle = __rpc_setconf("netpath")) == NULL) {
1197		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1198		return (FALSE);
1199	}
1200	rpc_createerr.cf_stat = RPC_SUCCESS;
1201	while (client == NULL) {
1202		if ((nconf = __rpc_getconf(handle)) == NULL) {
1203			if (rpc_createerr.cf_stat == RPC_SUCCESS)
1204				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1205			break;
1206		}
1207		client = getclnthandle(host, nconf, NULL);
1208		if (client)
1209			break;
1210	}
1211	__rpc_endconf(handle);
1212	if (client == (CLIENT *) NULL) {
1213		return (FALSE);
1214	}
1215
1216	st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
1217		(xdrproc_t) xdr_void, NULL,
1218		(xdrproc_t) xdr_int, (char *)(void *)timep, tottimeout);
1219
1220	if ((st == RPC_PROGVERSMISMATCH) || (st == RPC_PROGUNAVAIL)) {
1221		CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
1222		if (vers == RPCBVERS4) {
1223			/* fall back to earlier version */
1224			vers = RPCBVERS;
1225			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
1226			st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
1227				(xdrproc_t) xdr_void, NULL,
1228				(xdrproc_t) xdr_int, (char *)(void *)timep,
1229				tottimeout);
1230		}
1231	}
1232	CLNT_DESTROY(client);
1233	return (st == RPC_SUCCESS? TRUE: FALSE);
1234}
1235
1236/*
1237 * Converts taddr to universal address.  This routine should never
1238 * really be called because local n2a libraries are always provided.
1239 */
1240char *
1241rpcb_taddr2uaddr(struct netconfig *nconf, struct netbuf *taddr)
1242{
1243	CLIENT *client;
1244	char *uaddr = NULL;
1245
1246
1247	/* parameter checking */
1248	if (nconf == NULL) {
1249		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1250		return (NULL);
1251	}
1252	if (taddr == NULL) {
1253		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1254		return (NULL);
1255	}
1256	client = local_rpcb();
1257	if (! client) {
1258		return (NULL);
1259	}
1260
1261	CLNT_CALL(client, (rpcproc_t)RPCBPROC_TADDR2UADDR,
1262	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
1263	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr, tottimeout);
1264	CLNT_DESTROY(client);
1265	return (uaddr);
1266}
1267
1268/*
1269 * Converts universal address to netbuf.  This routine should never
1270 * really be called because local n2a libraries are always provided.
1271 */
1272struct netbuf *
1273rpcb_uaddr2taddr(struct netconfig *nconf, char *uaddr)
1274{
1275	CLIENT *client;
1276	struct netbuf *taddr;
1277
1278
1279	/* parameter checking */
1280	if (nconf == NULL) {
1281		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1282		return (NULL);
1283	}
1284	if (uaddr == NULL) {
1285		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1286		return (NULL);
1287	}
1288	client = local_rpcb();
1289	if (! client) {
1290		return (NULL);
1291	}
1292
1293	taddr = (struct netbuf *)calloc(1, sizeof (struct netbuf));
1294	if (taddr == NULL) {
1295		CLNT_DESTROY(client);
1296		return (NULL);
1297	}
1298	if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_UADDR2TADDR,
1299	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr,
1300	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
1301	    tottimeout) != RPC_SUCCESS) {
1302		free(taddr);
1303		taddr = NULL;
1304	}
1305	CLNT_DESTROY(client);
1306	return (taddr);
1307}
1308