rpcb_clnt.c revision 74462
1/*	$NetBSD: rpcb_clnt.c,v 1.6 2000/07/16 06:41:43 itojun Exp $	*/
2/*	$FreeBSD: head/lib/libc/rpc/rpcb_clnt.c 74462 2001-03-19 12:50:13Z alfred $ */
3
4/*
5 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
6 * unrestricted use provided that this legend is included on all tape
7 * media and as a part of the software program in whole or part.  Users
8 * may copy or modify Sun RPC without charge, but are not authorized
9 * to license or distribute it to anyone else except as part of a product or
10 * program developed by the user.
11 *
12 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
13 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
14 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
15 *
16 * Sun RPC is provided with no support and without any obligation on the
17 * part of Sun Microsystems, Inc. to assist in its use, correction,
18 * modification or enhancement.
19 *
20 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
21 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
22 * OR ANY PART THEREOF.
23 *
24 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
25 * or profits or other special, indirect and consequential damages, even if
26 * Sun has been advised of the possibility of such damages.
27 *
28 * Sun Microsystems, Inc.
29 * 2550 Garcia Avenue
30 * Mountain View, California  94043
31 */
32/*
33 * Copyright (c) 1986-1991 by Sun Microsystems Inc.
34 */
35
36/* #ident	"@(#)rpcb_clnt.c	1.27	94/04/24 SMI" */
37
38
39#if 0
40#if !defined(lint) && defined(SCCSIDS)
41static char sccsid[] = "@(#)rpcb_clnt.c 1.30 89/06/21 Copyr 1988 Sun Micro";
42#endif
43#endif
44
45/*
46 * rpcb_clnt.c
47 * interface to rpcbind rpc service.
48 *
49 * Copyright (C) 1988, Sun Microsystems, Inc.
50 */
51
52#include "reentrant.h"
53#include "namespace.h"
54#include <sys/types.h>
55#include <sys/socket.h>
56#include <sys/un.h>
57#include <sys/utsname.h>
58#include <rpc/rpc.h>
59#include <rpc/rpcb_prot.h>
60#include <rpc/nettype.h>
61#include <netconfig.h>
62#ifdef PORTMAP
63#include <netinet/in.h>		/* FOR IPPROTO_TCP/UDP definitions */
64#include <rpc/pmap_prot.h>
65#endif				/* PORTMAP */
66#include <stdio.h>
67#include <errno.h>
68#include <stdlib.h>
69#include <string.h>
70#include <unistd.h>
71#include <netdb.h>
72#include <syslog.h>
73#include "un-namespace.h"
74
75#include "rpc_com.h"
76
77static struct timeval tottimeout = { 60, 0 };
78static const struct timeval rmttimeout = { 3, 0 };
79
80extern bool_t xdr_wrapstring __P((XDR *, char **));
81
82static const char nullstring[] = "\000";
83
84#define	CACHESIZE 6
85
86struct address_cache {
87	char *ac_host;
88	char *ac_netid;
89	char *ac_uaddr;
90	struct netbuf *ac_taddr;
91	struct address_cache *ac_next;
92};
93
94static struct address_cache *front;
95static int cachesize;
96
97#define	CLCR_GET_RPCB_TIMEOUT	1
98#define	CLCR_SET_RPCB_TIMEOUT	2
99
100
101extern int __rpc_lowvers;
102
103static struct address_cache *check_cache __P((const char *, const char *));
104static void delete_cache __P((struct netbuf *));
105static void add_cache __P((const char *, const char *, struct netbuf *,
106			   char *));
107static CLIENT *getclnthandle __P((const char *, const struct netconfig *,
108				  char **));
109static CLIENT *local_rpcb __P((void));
110static struct netbuf *got_entry __P((rpcb_entry_list_ptr,
111				     const struct netconfig *));
112
113/*
114 * This routine adjusts the timeout used for calls to the remote rpcbind.
115 * Also, this routine can be used to set the use of portmapper version 2
116 * only when doing rpc_broadcasts
117 * These are private routines that may not be provided in future releases.
118 */
119bool_t
120__rpc_control(request, info)
121	int	request;
122	void	*info;
123{
124	switch (request) {
125	case CLCR_GET_RPCB_TIMEOUT:
126		*(struct timeval *)info = tottimeout;
127		break;
128	case CLCR_SET_RPCB_TIMEOUT:
129		tottimeout = *(struct timeval *)info;
130		break;
131	case CLCR_SET_LOWVERS:
132		__rpc_lowvers = *(int *)info;
133		break;
134	case CLCR_GET_LOWVERS:
135		*(int *)info = __rpc_lowvers;
136		break;
137	default:
138		return (FALSE);
139	}
140	return (TRUE);
141}
142
143/*
144 *	It might seem that a reader/writer lock would be more reasonable here.
145 *	However because getclnthandle(), the only user of the cache functions,
146 *	may do a delete_cache() operation if a check_cache() fails to return an
147 *	address useful to clnt_tli_create(), we may as well use a mutex.
148 */
149/*
150 * As it turns out, if the cache lock is *not* a reader/writer lock, we will
151 * block all clnt_create's if we are trying to connect to a host that's down,
152 * since the lock will be held all during that time.
153 */
154extern rwlock_t	rpcbaddr_cache_lock;
155
156/*
157 * The routines check_cache(), add_cache(), delete_cache() manage the
158 * cache of rpcbind addresses for (host, netid).
159 */
160
161static struct address_cache *
162check_cache(host, netid)
163	const char *host, *netid;
164{
165	struct address_cache *cptr;
166
167	/* READ LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
168
169	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
170		if (!strcmp(cptr->ac_host, host) &&
171		    !strcmp(cptr->ac_netid, netid)) {
172#ifdef ND_DEBUG
173			fprintf(stderr, "Found cache entry for %s: %s\n",
174				host, netid);
175#endif
176			return (cptr);
177		}
178	}
179	return ((struct address_cache *) NULL);
180}
181
182static void
183delete_cache(addr)
184	struct netbuf *addr;
185{
186	struct address_cache *cptr, *prevptr = NULL;
187
188	/* WRITE LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
189	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
190		if (!memcmp(cptr->ac_taddr->buf, addr->buf, addr->len)) {
191			free(cptr->ac_host);
192			free(cptr->ac_netid);
193			free(cptr->ac_taddr->buf);
194			free(cptr->ac_taddr);
195			if (cptr->ac_uaddr)
196				free(cptr->ac_uaddr);
197			if (prevptr)
198				prevptr->ac_next = cptr->ac_next;
199			else
200				front = cptr->ac_next;
201			free(cptr);
202			cachesize--;
203			break;
204		}
205		prevptr = cptr;
206	}
207}
208
209static void
210add_cache(host, netid, taddr, uaddr)
211	const char *host, *netid;
212	char *uaddr;
213	struct netbuf *taddr;
214{
215	struct address_cache  *ad_cache, *cptr, *prevptr;
216
217	ad_cache = (struct address_cache *)
218			malloc(sizeof (struct address_cache));
219	if (!ad_cache) {
220		return;
221	}
222	ad_cache->ac_host = strdup(host);
223	ad_cache->ac_netid = strdup(netid);
224	ad_cache->ac_uaddr = uaddr ? strdup(uaddr) : NULL;
225	ad_cache->ac_taddr = (struct netbuf *)malloc(sizeof (struct netbuf));
226	if (!ad_cache->ac_host || !ad_cache->ac_netid || !ad_cache->ac_taddr ||
227		(uaddr && !ad_cache->ac_uaddr)) {
228		return;
229	}
230	ad_cache->ac_taddr->len = ad_cache->ac_taddr->maxlen = taddr->len;
231	ad_cache->ac_taddr->buf = (char *) malloc(taddr->len);
232	if (ad_cache->ac_taddr->buf == NULL) {
233		return;
234	}
235	memcpy(ad_cache->ac_taddr->buf, taddr->buf, taddr->len);
236#ifdef ND_DEBUG
237	fprintf(stderr, "Added to cache: %s : %s\n", host, netid);
238#endif
239
240/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  cptr */
241
242	rwlock_wrlock(&rpcbaddr_cache_lock);
243	if (cachesize < CACHESIZE) {
244		ad_cache->ac_next = front;
245		front = ad_cache;
246		cachesize++;
247	} else {
248		/* Free the last entry */
249		cptr = front;
250		prevptr = NULL;
251		while (cptr->ac_next) {
252			prevptr = cptr;
253			cptr = cptr->ac_next;
254		}
255
256#ifdef ND_DEBUG
257		fprintf(stderr, "Deleted from cache: %s : %s\n",
258			cptr->ac_host, cptr->ac_netid);
259#endif
260		free(cptr->ac_host);
261		free(cptr->ac_netid);
262		free(cptr->ac_taddr->buf);
263		free(cptr->ac_taddr);
264		if (cptr->ac_uaddr)
265			free(cptr->ac_uaddr);
266
267		if (prevptr) {
268			prevptr->ac_next = NULL;
269			ad_cache->ac_next = front;
270			front = ad_cache;
271		} else {
272			front = ad_cache;
273			ad_cache->ac_next = NULL;
274		}
275		free(cptr);
276	}
277	rwlock_unlock(&rpcbaddr_cache_lock);
278}
279
280/*
281 * This routine will return a client handle that is connected to the
282 * rpcbind. Returns NULL on error and free's everything.
283 */
284static CLIENT *
285getclnthandle(host, nconf, targaddr)
286	const char *host;
287	const struct netconfig *nconf;
288	char **targaddr;
289{
290	CLIENT *client;
291	struct netbuf *addr, taddr;
292	struct netbuf addr_to_delete;
293	struct __rpc_sockinfo si;
294	struct addrinfo hints, *res, *tres;
295	struct address_cache *ad_cache;
296	char *tmpaddr;
297
298/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  ad_cache */
299
300	/* Get the address of the rpcbind.  Check cache first */
301	addr_to_delete.len = 0;
302	rwlock_rdlock(&rpcbaddr_cache_lock);
303	ad_cache = check_cache(host, nconf->nc_netid);
304	if (ad_cache != NULL) {
305		addr = ad_cache->ac_taddr;
306		client = clnt_tli_create(RPC_ANYFD, nconf, addr,
307		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
308		if (client != NULL) {
309			if (targaddr)
310				*targaddr = ad_cache->ac_uaddr;
311			rwlock_unlock(&rpcbaddr_cache_lock);
312			return (client);
313		}
314		addr_to_delete.len = addr->len;
315		addr_to_delete.buf = (char *)malloc(addr->len);
316		if (addr_to_delete.buf == NULL) {
317			addr_to_delete.len = 0;
318		} else {
319			memcpy(addr_to_delete.buf, addr->buf, addr->len);
320		}
321	}
322	rwlock_unlock(&rpcbaddr_cache_lock);
323	if (addr_to_delete.len != 0) {
324		/*
325		 * Assume this may be due to cache data being
326		 *  outdated
327		 */
328		rwlock_wrlock(&rpcbaddr_cache_lock);
329		delete_cache(&addr_to_delete);
330		rwlock_unlock(&rpcbaddr_cache_lock);
331		free(addr_to_delete.buf);
332	}
333	if (!__rpc_nconf2sockinfo(nconf, &si)) {
334		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
335		return NULL;
336	}
337
338	memset(&hints, 0, sizeof hints);
339	hints.ai_family = si.si_af;
340	hints.ai_socktype = si.si_socktype;
341	hints.ai_protocol = si.si_proto;
342
343#ifdef CLNT_DEBUG
344	printf("trying netid %s family %d proto %d socktype %d\n",
345	    nconf->nc_netid, si.si_af, si.si_proto, si.si_socktype);
346#endif
347
348	if (getaddrinfo(host, "sunrpc", &hints, &res) != 0) {
349		rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
350		return NULL;
351	}
352
353	for (tres = res; tres != NULL; tres = tres->ai_next) {
354		taddr.buf = tres->ai_addr;
355		taddr.len = taddr.maxlen = tres->ai_addrlen;
356
357#ifdef ND_DEBUG
358		{
359			char *ua;
360
361			ua = taddr2uaddr(nconf, &taddr);
362			fprintf(stderr, "Got it [%s]\n", ua);
363			free(ua);
364		}
365#endif
366
367#ifdef ND_DEBUG
368		{
369			int i;
370
371			fprintf(stderr, "\tnetbuf len = %d, maxlen = %d\n",
372				taddr.len, taddr.maxlen);
373			fprintf(stderr, "\tAddress is ");
374			for (i = 0; i < taddr.len; i++)
375				fprintf(stderr, "%u.", ((char *)(taddr.buf))[i]);
376			fprintf(stderr, "\n");
377		}
378#endif
379		client = clnt_tli_create(RPC_ANYFD, nconf, &taddr,
380		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
381#ifdef ND_DEBUG
382		if (! client) {
383			clnt_pcreateerror("rpcbind clnt interface");
384		}
385#endif
386
387		if (client) {
388			tmpaddr = targaddr ? taddr2uaddr(nconf, &taddr) : NULL;
389			add_cache(host, nconf->nc_netid, &taddr, tmpaddr);
390			if (targaddr)
391				*targaddr = tmpaddr;
392			break;
393		}
394	}
395	freeaddrinfo(res);
396	return (client);
397}
398
399/* XXX */
400#define IN4_LOCALHOST_STRING	"127.0.0.1"
401#define IN6_LOCALHOST_STRING	"::1"
402
403/*
404 * This routine will return a client handle that is connected to the local
405 * rpcbind. Returns NULL on error and free's everything.
406 */
407static CLIENT *
408local_rpcb()
409{
410	CLIENT *client;
411	static struct netconfig *loopnconf;
412	static char *hostname;
413	extern mutex_t loopnconf_lock;
414	int sock;
415	size_t tsize;
416	struct netbuf nbuf;
417	struct sockaddr_un sun;
418
419	/*
420	 * Try connecting to the local rpcbind through a local socket
421	 * first. If this doesn't work, try all transports defined in
422	 * the netconfig file.
423	 */
424	memset(&sun, 0, sizeof sun);
425	sock = _socket(AF_LOCAL, SOCK_STREAM, 0);
426	if (sock < 0)
427		goto try_nconf;
428	sun.sun_family = AF_LOCAL;
429	strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
430	nbuf.len = sun.sun_len = SUN_LEN(&sun);
431	nbuf.maxlen = sizeof (struct sockaddr_un);
432	nbuf.buf = &sun;
433
434	tsize = __rpc_get_t_size(AF_LOCAL, 0, 0);
435	client = clnt_vc_create(sock, &nbuf, (rpcprog_t)RPCBPROG,
436	    (rpcvers_t)RPCBVERS, tsize, tsize);
437
438	if (client != NULL)
439		return client;
440
441try_nconf:
442
443/* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */
444	mutex_lock(&loopnconf_lock);
445	if (loopnconf == NULL) {
446		struct netconfig *nconf, *tmpnconf = NULL;
447		void *nc_handle;
448		int fd;
449
450		nc_handle = setnetconfig();
451		if (nc_handle == NULL) {
452			/* fails to open netconfig file */
453			syslog (LOG_ERR, "rpc: failed to open " NETCONFIG);
454			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
455			mutex_unlock(&loopnconf_lock);
456			return (NULL);
457		}
458		while ((nconf = getnetconfig(nc_handle)) != NULL) {
459#ifdef INET6
460			if ((strcmp(nconf->nc_protofmly, NC_INET6) == 0 ||
461#else
462			if ((
463#endif
464			     strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
465			    (nconf->nc_semantics == NC_TPI_COTS ||
466			     nconf->nc_semantics == NC_TPI_COTS_ORD)) {
467				fd = __rpc_nconf2fd(nconf);
468				/*
469				 * Can't create a socket, assume that
470				 * this family isn't configured in the kernel.
471				 */
472				if (fd < 0)
473					continue;
474				_close(fd);
475				tmpnconf = nconf;
476				if (!strcmp(nconf->nc_protofmly, NC_INET))
477					hostname = IN4_LOCALHOST_STRING;
478				else
479					hostname = IN6_LOCALHOST_STRING;
480			}
481		}
482		if (tmpnconf == NULL) {
483			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
484			mutex_unlock(&loopnconf_lock);
485			return (NULL);
486		}
487		loopnconf = getnetconfigent(tmpnconf->nc_netid);
488		/* loopnconf is never freed */
489		endnetconfig(nc_handle);
490	}
491	mutex_unlock(&loopnconf_lock);
492	client = getclnthandle(hostname, loopnconf, NULL);
493	return (client);
494}
495
496/*
497 * Set a mapping between program, version and address.
498 * Calls the rpcbind service to do the mapping.
499 */
500bool_t
501rpcb_set(program, version, nconf, address)
502	rpcprog_t program;
503	rpcvers_t version;
504	const struct netconfig *nconf;	/* Network structure of transport */
505	const struct netbuf *address;		/* Services netconfig address */
506{
507	CLIENT *client;
508	bool_t rslt = FALSE;
509	RPCB parms;
510	char uidbuf[32];
511
512	/* parameter checking */
513	if (nconf == NULL) {
514		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
515		return (FALSE);
516	}
517	if (address == NULL) {
518		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
519		return (FALSE);
520	}
521	client = local_rpcb();
522	if (! client) {
523		return (FALSE);
524	}
525
526	/* convert to universal */
527	/*LINTED const castaway*/
528	parms.r_addr = taddr2uaddr((struct netconfig *) nconf,
529				   (struct netbuf *)address);
530	if (!parms.r_addr) {
531		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
532		return (FALSE); /* no universal address */
533	}
534	parms.r_prog = program;
535	parms.r_vers = version;
536	parms.r_netid = nconf->nc_netid;
537	/*
538	 * Though uid is not being used directly, we still send it for
539	 * completeness.  For non-unix platforms, perhaps some other
540	 * string or an empty string can be sent.
541	 */
542	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
543	parms.r_owner = uidbuf;
544
545	CLNT_CALL(client, (rpcproc_t)RPCBPROC_SET, (xdrproc_t) xdr_rpcb,
546	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
547	    (char *)(void *)&rslt, tottimeout);
548
549	CLNT_DESTROY(client);
550	free(parms.r_addr);
551	return (rslt);
552}
553
554/*
555 * Remove the mapping between program, version and netbuf address.
556 * Calls the rpcbind service to do the un-mapping.
557 * If netbuf is NULL, unset for all the transports, otherwise unset
558 * only for the given transport.
559 */
560bool_t
561rpcb_unset(program, version, nconf)
562	rpcprog_t program;
563	rpcvers_t version;
564	const struct netconfig *nconf;
565{
566	CLIENT *client;
567	bool_t rslt = FALSE;
568	RPCB parms;
569	char uidbuf[32];
570
571	client = local_rpcb();
572	if (! client) {
573		return (FALSE);
574	}
575
576	parms.r_prog = program;
577	parms.r_vers = version;
578	if (nconf)
579		parms.r_netid = nconf->nc_netid;
580	else {
581		/*LINTED const castaway*/
582		parms.r_netid = (char *) &nullstring[0]; /* unsets  all */
583	}
584	/*LINTED const castaway*/
585	parms.r_addr = (char *) &nullstring[0];
586	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
587	parms.r_owner = uidbuf;
588
589	CLNT_CALL(client, (rpcproc_t)RPCBPROC_UNSET, (xdrproc_t) xdr_rpcb,
590	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
591	    (char *)(void *)&rslt, tottimeout);
592
593	CLNT_DESTROY(client);
594	return (rslt);
595}
596
597/*
598 * From the merged list, find the appropriate entry
599 */
600static struct netbuf *
601got_entry(relp, nconf)
602	rpcb_entry_list_ptr relp;
603	const struct netconfig *nconf;
604{
605	struct netbuf *na = NULL;
606	rpcb_entry_list_ptr sp;
607	rpcb_entry *rmap;
608
609	for (sp = relp; sp != NULL; sp = sp->rpcb_entry_next) {
610		rmap = &sp->rpcb_entry_map;
611		if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) &&
612		    (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) &&
613		    (nconf->nc_semantics == rmap->r_nc_semantics) &&
614		    (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != NULL)) {
615			na = uaddr2taddr(nconf, rmap->r_maddr);
616#ifdef ND_DEBUG
617			fprintf(stderr, "\tRemote address is [%s].\n",
618				rmap->r_maddr);
619			if (!na)
620				fprintf(stderr,
621				    "\tCouldn't resolve remote address!\n");
622#endif
623			break;
624		}
625	}
626	return (na);
627}
628
629/*
630 * An internal function which optimizes rpcb_getaddr function.  It also
631 * returns the client handle that it uses to contact the remote rpcbind.
632 *
633 * The algorithm used: If the transports is TCP or UDP, it first tries
634 * version 2 (portmap), 4 and then 3 (svr4).  This order should be
635 * changed in the next OS release to 4, 2 and 3.  We are assuming that by
636 * that time, version 4 would be available on many machines on the network.
637 * With this algorithm, we get performance as well as a plan for
638 * obsoleting version 2.
639 *
640 * For all other transports, the algorithm remains as 4 and then 3.
641 *
642 * XXX: Due to some problems with t_connect(), we do not reuse the same client
643 * handle for COTS cases and hence in these cases we do not return the
644 * client handle.  This code will change if t_connect() ever
645 * starts working properly.  Also look under clnt_vc.c.
646 */
647struct netbuf *
648__rpcb_findaddr(program, version, nconf, host, clpp)
649	rpcprog_t program;
650	rpcvers_t version;
651	const struct netconfig *nconf;
652	const char *host;
653	CLIENT **clpp;
654{
655	CLIENT *client = NULL;
656	RPCB parms;
657	enum clnt_stat clnt_st;
658	char *ua = NULL;
659	rpcvers_t vers;
660	struct netbuf *address = NULL;
661	rpcvers_t start_vers = RPCBVERS4;
662	struct netbuf servaddr;
663
664	/* parameter checking */
665	if (nconf == NULL) {
666		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
667		return (NULL);
668	}
669
670	parms.r_addr = NULL;
671
672#ifdef PORTMAP
673	/* Try version 2 for TCP or UDP */
674	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
675		u_short port = 0;
676		struct netbuf remote;
677		rpcvers_t pmapvers = 2;
678		struct pmap pmapparms;
679
680		/*
681		 * Try UDP only - there are some portmappers out
682		 * there that use UDP only.
683		 */
684		if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
685			struct netconfig *newnconf;
686
687			if ((newnconf = getnetconfigent("udp")) == NULL) {
688				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
689				return (NULL);
690			}
691			client = getclnthandle(host, newnconf, &parms.r_addr);
692			freenetconfigent(newnconf);
693		} else {
694			client = getclnthandle(host, nconf, &parms.r_addr);
695		}
696		if (client == NULL) {
697			return (NULL);
698		}
699
700		/* Set the version */
701		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&pmapvers);
702		pmapparms.pm_prog = program;
703		pmapparms.pm_vers = version;
704		pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ?
705					IPPROTO_UDP : IPPROTO_TCP;
706		pmapparms.pm_port = 0;	/* not needed */
707		clnt_st = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
708		    (xdrproc_t) xdr_pmap, (caddr_t)(void *)&pmapparms,
709		    (xdrproc_t) xdr_u_short, (caddr_t)(void *)&port,
710		    tottimeout);
711		if (clnt_st != RPC_SUCCESS) {
712			if ((clnt_st == RPC_PROGVERSMISMATCH) ||
713				(clnt_st == RPC_PROGUNAVAIL))
714				goto try_rpcbind; /* Try different versions */
715			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
716			clnt_geterr(client, &rpc_createerr.cf_error);
717			goto error;
718		} else if (port == 0) {
719			address = NULL;
720			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
721			goto error;
722		}
723		port = htons(port);
724		CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)(void *)&remote);
725		if (((address = (struct netbuf *)
726			malloc(sizeof (struct netbuf))) == NULL) ||
727		    ((address->buf = (char *)
728			malloc(remote.len)) == NULL)) {
729			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
730			clnt_geterr(client, &rpc_createerr.cf_error);
731			if (address) {
732				free(address);
733				address = NULL;
734			}
735			goto error;
736		}
737		memcpy(address->buf, remote.buf, remote.len);
738		memcpy(&((char *)address->buf)[sizeof (short)],
739				(char *)(void *)&port, sizeof (short));
740		address->len = address->maxlen = remote.len;
741		goto done;
742	}
743#endif				/* PORTMAP */
744
745try_rpcbind:
746	/*
747	 * Now we try version 4 and then 3.
748	 * We also send the remote system the address we used to
749	 * contact it in case it can help to connect back with us
750	 */
751	parms.r_prog = program;
752	parms.r_vers = version;
753	/*LINTED const castaway*/
754	parms.r_owner = (char *) &nullstring[0];	/* not needed; */
755							/* just for xdring */
756	parms.r_netid = nconf->nc_netid; /* not really needed */
757
758	/*
759	 * If a COTS transport is being used, try getting address via CLTS
760	 * transport.  This works only with version 4.
761	 * NOTE: This is being done for all transports EXCEPT LOOPBACK
762	 * because with loopback the cost to go to a COTS is same as
763	 * the cost to go through CLTS, plus you get the advantage of
764	 * finding out immediately if the local rpcbind process is dead.
765	 */
766#if 1
767	if ((nconf->nc_semantics == NC_TPI_COTS_ORD ||
768			nconf->nc_semantics == NC_TPI_COTS) &&
769	    (strcmp(nconf->nc_protofmly, NC_LOOPBACK) != 0)) {
770#else
771	if (client != NULL) {
772		CLNT_DESTROY(client);
773		client = NULL;
774	}
775	if (nconf->nc_semantics == NC_TPI_CLTS) {
776#endif
777		void *handle;
778		struct netconfig *nconf_clts;
779		rpcb_entry_list_ptr relp = NULL;
780
781		if (client == NULL) {
782			/* This did not go through the above PORTMAP/TCP code */
783#if 1
784			if ((handle = __rpc_setconf("datagram_v")) != NULL) {
785#else
786			if ((handle = __rpc_setconf("circuit_v")) != NULL) {
787#endif
788				while ((nconf_clts = __rpc_getconf(handle))
789					!= NULL) {
790					if (strcmp(nconf_clts->nc_protofmly,
791						nconf->nc_protofmly) != 0) {
792						continue;
793					}
794					client = getclnthandle(host, nconf_clts,
795							&parms.r_addr);
796					break;
797				}
798				__rpc_endconf(handle);
799			}
800			if (client == NULL)
801				goto regular_rpcbind;	/* Go the regular way */
802		} else {
803			/* This is a UDP PORTMAP handle.  Change to version 4 */
804			vers = RPCBVERS4;
805			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
806		}
807		/*
808		 * We also send the remote system the address we used to
809		 * contact it in case it can help it connect back with us
810		 */
811		if (parms.r_addr == NULL) {
812			/*LINTED const castaway*/
813			parms.r_addr = (char *) &nullstring[0]; /* for XDRing */
814		}
815		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDRLIST,
816		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
817		    (xdrproc_t) xdr_rpcb_entry_list_ptr,
818		    (char *)(void *)&relp, tottimeout);
819		if (clnt_st == RPC_SUCCESS) {
820			if ((address = got_entry(relp, nconf)) != NULL) {
821				xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
822				    (char *)(void *)&relp);
823				goto done;
824			}
825			/* Entry not found for this transport */
826			xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
827			    (char *)(void *)&relp);
828			/*
829			 * XXX: should have perhaps returned with error but
830			 * since the remote machine might not always be able
831			 * to send the address on all transports, we try the
832			 * regular way with regular_rpcbind
833			 */
834			goto regular_rpcbind;
835		} else if ((clnt_st == RPC_PROGVERSMISMATCH) ||
836			(clnt_st == RPC_PROGUNAVAIL)) {
837			start_vers = RPCBVERS;	/* Try version 3 now */
838			goto regular_rpcbind; /* Try different versions */
839		} else {
840			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
841			clnt_geterr(client, &rpc_createerr.cf_error);
842			goto error;
843		}
844	}
845
846regular_rpcbind:
847
848	/* Now the same transport is to be used to get the address */
849#if 1
850	if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
851			(nconf->nc_semantics == NC_TPI_COTS))) {
852#else
853	if (client && nconf->nc_semantics == NC_TPI_CLTS) {
854#endif
855		/* A CLTS type of client - destroy it */
856		CLNT_DESTROY(client);
857		client = NULL;
858	}
859
860	if (client == NULL) {
861		client = getclnthandle(host, nconf, &parms.r_addr);
862		if (client == NULL) {
863			goto error;
864		}
865	}
866	if (parms.r_addr == NULL) {
867		/*LINTED const castaway*/
868		parms.r_addr = (char *) &nullstring[0];
869	}
870
871	/* First try from start_vers and then version 3 (RPCBVERS) */
872	for (vers = start_vers;  vers >= RPCBVERS; vers--) {
873		/* Set the version */
874		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
875		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
876		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
877		    (xdrproc_t) xdr_wrapstring, (char *)(void *) &ua,
878		    tottimeout);
879		if (clnt_st == RPC_SUCCESS) {
880			if ((ua == NULL) || (ua[0] == NULL)) {
881				/* address unknown */
882				rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
883				goto error;
884			}
885			address = uaddr2taddr(nconf, ua);
886#ifdef ND_DEBUG
887			fprintf(stderr, "\tRemote address is [%s]\n", ua);
888			if (!address)
889				fprintf(stderr,
890					"\tCouldn't resolve remote address!\n");
891#endif
892			xdr_free((xdrproc_t)xdr_wrapstring,
893			    (char *)(void *)&ua);
894
895			if (! address) {
896				/* We don't know about your universal address */
897				rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
898				goto error;
899			}
900			CLNT_CONTROL(client, CLGET_SVC_ADDR,
901			    (char *)(void *)&servaddr);
902			__rpc_fixup_addr(address, &servaddr);
903			goto done;
904		} else if (clnt_st == RPC_PROGVERSMISMATCH) {
905			struct rpc_err rpcerr;
906
907			clnt_geterr(client, &rpcerr);
908			if (rpcerr.re_vers.low > RPCBVERS4)
909				goto error;  /* a new version, can't handle */
910		} else if (clnt_st != RPC_PROGUNAVAIL) {
911			/* Cant handle this error */
912			rpc_createerr.cf_stat = clnt_st;
913			clnt_geterr(client, &rpc_createerr.cf_error);
914			goto error;
915		}
916	}
917
918	if ((address == NULL) || (address->len == 0)) {
919		rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
920		clnt_geterr(client, &rpc_createerr.cf_error);
921	}
922
923error:
924	if (client) {
925		CLNT_DESTROY(client);
926		client = NULL;
927	}
928done:
929	if (nconf->nc_semantics != NC_TPI_CLTS) {
930		/* This client is the connectionless one */
931		if (client) {
932			CLNT_DESTROY(client);
933			client = NULL;
934		}
935	}
936	if (clpp) {
937		*clpp = client;
938	} else if (client) {
939		CLNT_DESTROY(client);
940	}
941	return (address);
942}
943
944
945/*
946 * Find the mapped address for program, version.
947 * Calls the rpcbind service remotely to do the lookup.
948 * Uses the transport specified in nconf.
949 * Returns FALSE (0) if no map exists, else returns 1.
950 *
951 * Assuming that the address is all properly allocated
952 */
953int
954rpcb_getaddr(program, version, nconf, address, host)
955	rpcprog_t program;
956	rpcvers_t version;
957	const struct netconfig *nconf;
958	struct netbuf *address;
959	const char *host;
960{
961	struct netbuf *na;
962
963	if ((na = __rpcb_findaddr(program, version, nconf,
964				host, (CLIENT **) NULL)) == NULL)
965		return (FALSE);
966
967	if (na->len > address->maxlen) {
968		/* Too long address */
969		free(na->buf);
970		free(na);
971		rpc_createerr.cf_stat = RPC_FAILED;
972		return (FALSE);
973	}
974	memcpy(address->buf, na->buf, (size_t)na->len);
975	address->len = na->len;
976	free(na->buf);
977	free(na);
978	return (TRUE);
979}
980
981/*
982 * Get a copy of the current maps.
983 * Calls the rpcbind service remotely to get the maps.
984 *
985 * It returns only a list of the services
986 * It returns NULL on failure.
987 */
988rpcblist *
989rpcb_getmaps(nconf, host)
990	const struct netconfig *nconf;
991	const char *host;
992{
993	rpcblist_ptr head = NULL;
994	CLIENT *client;
995	enum clnt_stat clnt_st;
996	rpcvers_t vers = 0;
997
998	client = getclnthandle(host, nconf, NULL);
999	if (client == NULL) {
1000		return (head);
1001	}
1002	clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
1003	    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
1004	    (char *)(void *)&head, tottimeout);
1005	if (clnt_st == RPC_SUCCESS)
1006		goto done;
1007
1008	if ((clnt_st != RPC_PROGVERSMISMATCH) &&
1009	    (clnt_st != RPC_PROGUNAVAIL)) {
1010		rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1011		clnt_geterr(client, &rpc_createerr.cf_error);
1012		goto done;
1013	}
1014
1015	/* fall back to earlier version */
1016	CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
1017	if (vers == RPCBVERS4) {
1018		vers = RPCBVERS;
1019		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
1020		if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
1021		    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
1022		    (char *)(void *)&head, tottimeout) == RPC_SUCCESS)
1023			goto done;
1024	}
1025	rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1026	clnt_geterr(client, &rpc_createerr.cf_error);
1027
1028done:
1029	CLNT_DESTROY(client);
1030	return (head);
1031}
1032
1033/*
1034 * rpcbinder remote-call-service interface.
1035 * This routine is used to call the rpcbind remote call service
1036 * which will look up a service program in the address maps, and then
1037 * remotely call that routine with the given parameters. This allows
1038 * programs to do a lookup and call in one step.
1039*/
1040enum clnt_stat
1041rpcb_rmtcall(nconf, host, prog, vers, proc, xdrargs, argsp,
1042		xdrres, resp, tout, addr_ptr)
1043	const struct netconfig *nconf;	/* Netconfig structure */
1044	const char *host;			/* Remote host name */
1045	rpcprog_t prog;
1046	rpcvers_t vers;
1047	rpcproc_t proc;			/* Remote proc identifiers */
1048	xdrproc_t xdrargs, xdrres;	/* XDR routines */
1049	caddr_t argsp, resp;		/* Argument and Result */
1050	struct timeval tout;		/* Timeout value for this call */
1051	const struct netbuf *addr_ptr;	/* Preallocated netbuf address */
1052{
1053	CLIENT *client;
1054	enum clnt_stat stat;
1055	struct r_rpcb_rmtcallargs a;
1056	struct r_rpcb_rmtcallres r;
1057	rpcvers_t rpcb_vers;
1058
1059
1060	client = getclnthandle(host, nconf, NULL);
1061	if (client == NULL) {
1062		return (RPC_FAILED);
1063	}
1064	/*LINTED const castaway*/
1065	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)(void *)&rmttimeout);
1066	a.prog = prog;
1067	a.vers = vers;
1068	a.proc = proc;
1069	a.args.args_val = argsp;
1070	a.xdr_args = xdrargs;
1071	r.addr = NULL;
1072	r.results.results_val = resp;
1073	r.xdr_res = xdrres;
1074
1075	for (rpcb_vers = RPCBVERS4; rpcb_vers >= RPCBVERS; rpcb_vers--) {
1076		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&rpcb_vers);
1077		stat = CLNT_CALL(client, (rpcproc_t)RPCBPROC_CALLIT,
1078		    (xdrproc_t) xdr_rpcb_rmtcallargs, (char *)(void *)&a,
1079		    (xdrproc_t) xdr_rpcb_rmtcallres, (char *)(void *)&r, tout);
1080		if ((stat == RPC_SUCCESS) && (addr_ptr != NULL)) {
1081			struct netbuf *na;
1082			/*LINTED const castaway*/
1083			na = uaddr2taddr((struct netconfig *) nconf, r.addr);
1084			if (!na) {
1085				stat = RPC_N2AXLATEFAILURE;
1086				/*LINTED const castaway*/
1087				((struct netbuf *) addr_ptr)->len = 0;
1088				goto error;
1089			}
1090			if (na->len > addr_ptr->maxlen) {
1091				/* Too long address */
1092				stat = RPC_FAILED; /* XXX A better error no */
1093				free(na->buf);
1094				free(na);
1095				/*LINTED const castaway*/
1096				((struct netbuf *) addr_ptr)->len = 0;
1097				goto error;
1098			}
1099			memcpy(addr_ptr->buf, na->buf, (size_t)na->len);
1100			/*LINTED const castaway*/
1101			((struct netbuf *)addr_ptr)->len = na->len;
1102			free(na->buf);
1103			free(na);
1104			break;
1105		} else if ((stat != RPC_PROGVERSMISMATCH) &&
1106			    (stat != RPC_PROGUNAVAIL)) {
1107			goto error;
1108		}
1109	}
1110error:
1111	CLNT_DESTROY(client);
1112	if (r.addr)
1113		xdr_free((xdrproc_t) xdr_wrapstring, (char *)(void *)&r.addr);
1114	return (stat);
1115}
1116
1117/*
1118 * Gets the time on the remote host.
1119 * Returns 1 if succeeds else 0.
1120 */
1121bool_t
1122rpcb_gettime(host, timep)
1123	const char *host;
1124	time_t *timep;
1125{
1126	CLIENT *client = NULL;
1127	void *handle;
1128	struct netconfig *nconf;
1129	rpcvers_t vers;
1130	enum clnt_stat st;
1131
1132
1133	if ((host == NULL) || (host[0] == NULL)) {
1134		time(timep);
1135		return (TRUE);
1136	}
1137
1138	if ((handle = __rpc_setconf("netpath")) == NULL) {
1139		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1140		return (FALSE);
1141	}
1142	rpc_createerr.cf_stat = RPC_SUCCESS;
1143	while (client == NULL) {
1144		if ((nconf = __rpc_getconf(handle)) == NULL) {
1145			if (rpc_createerr.cf_stat == RPC_SUCCESS)
1146				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1147			break;
1148		}
1149		client = getclnthandle(host, nconf, NULL);
1150		if (client)
1151			break;
1152	}
1153	__rpc_endconf(handle);
1154	if (client == (CLIENT *) NULL) {
1155		return (FALSE);
1156	}
1157
1158	st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
1159		(xdrproc_t) xdr_void, NULL,
1160		(xdrproc_t) xdr_int, (char *)(void *)timep, tottimeout);
1161
1162	if ((st == RPC_PROGVERSMISMATCH) || (st == RPC_PROGUNAVAIL)) {
1163		CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
1164		if (vers == RPCBVERS4) {
1165			/* fall back to earlier version */
1166			vers = RPCBVERS;
1167			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
1168			st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
1169				(xdrproc_t) xdr_void, NULL,
1170				(xdrproc_t) xdr_int, (char *)(void *)timep,
1171				tottimeout);
1172		}
1173	}
1174	CLNT_DESTROY(client);
1175	return (st == RPC_SUCCESS? TRUE: FALSE);
1176}
1177
1178/*
1179 * Converts taddr to universal address.  This routine should never
1180 * really be called because local n2a libraries are always provided.
1181 */
1182char *
1183rpcb_taddr2uaddr(nconf, taddr)
1184	struct netconfig *nconf;
1185	struct netbuf *taddr;
1186{
1187	CLIENT *client;
1188	char *uaddr = NULL;
1189
1190
1191	/* parameter checking */
1192	if (nconf == NULL) {
1193		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1194		return (NULL);
1195	}
1196	if (taddr == NULL) {
1197		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1198		return (NULL);
1199	}
1200	client = local_rpcb();
1201	if (! client) {
1202		return (NULL);
1203	}
1204
1205	CLNT_CALL(client, (rpcproc_t)RPCBPROC_TADDR2UADDR,
1206	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
1207	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr, tottimeout);
1208	CLNT_DESTROY(client);
1209	return (uaddr);
1210}
1211
1212/*
1213 * Converts universal address to netbuf.  This routine should never
1214 * really be called because local n2a libraries are always provided.
1215 */
1216struct netbuf *
1217rpcb_uaddr2taddr(nconf, uaddr)
1218	struct netconfig *nconf;
1219	char *uaddr;
1220{
1221	CLIENT *client;
1222	struct netbuf *taddr;
1223
1224
1225	/* parameter checking */
1226	if (nconf == NULL) {
1227		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1228		return (NULL);
1229	}
1230	if (uaddr == NULL) {
1231		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1232		return (NULL);
1233	}
1234	client = local_rpcb();
1235	if (! client) {
1236		return (NULL);
1237	}
1238
1239	taddr = (struct netbuf *)calloc(1, sizeof (struct netbuf));
1240	if (taddr == NULL) {
1241		CLNT_DESTROY(client);
1242		return (NULL);
1243	}
1244	if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_UADDR2TADDR,
1245	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr,
1246	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
1247	    tottimeout) != RPC_SUCCESS) {
1248		free(taddr);
1249		taddr = NULL;
1250	}
1251	CLNT_DESTROY(client);
1252	return (taddr);
1253}
1254