rpcb_clnt.c revision 90735
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 90735 2002-02-16 17:05:49Z iedowse $ */
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 "namespace.h"
53#include "reentrant.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. If targaddr is non-NULL, the "universal address" of the
283 * host will be stored in *targaddr; the caller is responsible for
284 * freeing this string.
285 * On error, returns NULL and free's everything.
286 */
287static CLIENT *
288getclnthandle(host, nconf, targaddr)
289	const char *host;
290	const struct netconfig *nconf;
291	char **targaddr;
292{
293	CLIENT *client;
294	struct netbuf *addr, taddr;
295	struct netbuf addr_to_delete;
296	struct __rpc_sockinfo si;
297	struct addrinfo hints, *res, *tres;
298	struct address_cache *ad_cache;
299	char *tmpaddr;
300
301/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  ad_cache */
302
303	/* Get the address of the rpcbind.  Check cache first */
304	client = NULL;
305	addr_to_delete.len = 0;
306	rwlock_rdlock(&rpcbaddr_cache_lock);
307	ad_cache = NULL;
308	if (host != NULL)
309		ad_cache = check_cache(host, nconf->nc_netid);
310	if (ad_cache != NULL) {
311		addr = ad_cache->ac_taddr;
312		client = clnt_tli_create(RPC_ANYFD, nconf, addr,
313		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
314		if (client != NULL) {
315			if (targaddr)
316				*targaddr = strdup(ad_cache->ac_uaddr);
317			rwlock_unlock(&rpcbaddr_cache_lock);
318			return (client);
319		}
320		addr_to_delete.len = addr->len;
321		addr_to_delete.buf = (char *)malloc(addr->len);
322		if (addr_to_delete.buf == NULL) {
323			addr_to_delete.len = 0;
324		} else {
325			memcpy(addr_to_delete.buf, addr->buf, addr->len);
326		}
327	}
328	rwlock_unlock(&rpcbaddr_cache_lock);
329	if (addr_to_delete.len != 0) {
330		/*
331		 * Assume this may be due to cache data being
332		 *  outdated
333		 */
334		rwlock_wrlock(&rpcbaddr_cache_lock);
335		delete_cache(&addr_to_delete);
336		rwlock_unlock(&rpcbaddr_cache_lock);
337		free(addr_to_delete.buf);
338	}
339	if (!__rpc_nconf2sockinfo(nconf, &si)) {
340		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
341		return NULL;
342	}
343
344	memset(&hints, 0, sizeof hints);
345	hints.ai_family = si.si_af;
346	hints.ai_socktype = si.si_socktype;
347	hints.ai_protocol = si.si_proto;
348
349#ifdef CLNT_DEBUG
350	printf("trying netid %s family %d proto %d socktype %d\n",
351	    nconf->nc_netid, si.si_af, si.si_proto, si.si_socktype);
352#endif
353
354	if (nconf->nc_protofmly != NULL && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
355		client = local_rpcb();
356		if (! client) {
357#ifdef ND_DEBUG
358			clnt_pcreateerror("rpcbind clnt interface");
359#endif
360			return (NULL);
361		} else {
362			struct sockaddr_un sun;
363
364			*targaddr = malloc(sizeof(sun.sun_path));
365			strncpy(*targaddr, _PATH_RPCBINDSOCK,
366			    sizeof(sun.sun_path));
367			return (client);
368		}
369	} else {
370		if (getaddrinfo(host, "sunrpc", &hints, &res) != 0) {
371			rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
372			return NULL;
373		}
374	}
375
376	for (tres = res; tres != NULL; tres = tres->ai_next) {
377		taddr.buf = tres->ai_addr;
378		taddr.len = taddr.maxlen = tres->ai_addrlen;
379
380#ifdef ND_DEBUG
381		{
382			char *ua;
383
384			ua = taddr2uaddr(nconf, &taddr);
385			fprintf(stderr, "Got it [%s]\n", ua);
386			free(ua);
387		}
388#endif
389
390#ifdef ND_DEBUG
391		{
392			int i;
393
394			fprintf(stderr, "\tnetbuf len = %d, maxlen = %d\n",
395				taddr.len, taddr.maxlen);
396			fprintf(stderr, "\tAddress is ");
397			for (i = 0; i < taddr.len; i++)
398				fprintf(stderr, "%u.", ((char *)(taddr.buf))[i]);
399			fprintf(stderr, "\n");
400		}
401#endif
402		client = clnt_tli_create(RPC_ANYFD, nconf, &taddr,
403		    (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
404#ifdef ND_DEBUG
405		if (! client) {
406			clnt_pcreateerror("rpcbind clnt interface");
407		}
408#endif
409
410		if (client) {
411			tmpaddr = targaddr ? taddr2uaddr(nconf, &taddr) : NULL;
412			add_cache(host, nconf->nc_netid, &taddr, tmpaddr);
413			if (targaddr)
414				*targaddr = tmpaddr;
415			break;
416		}
417	}
418	if (res)
419		freeaddrinfo(res);
420	return (client);
421}
422
423/* XXX */
424#define IN4_LOCALHOST_STRING	"127.0.0.1"
425#define IN6_LOCALHOST_STRING	"::1"
426
427/*
428 * This routine will return a client handle that is connected to the local
429 * rpcbind. Returns NULL on error and free's everything.
430 */
431static CLIENT *
432local_rpcb()
433{
434	CLIENT *client;
435	static struct netconfig *loopnconf;
436	static char *hostname;
437	extern mutex_t loopnconf_lock;
438	int sock;
439	size_t tsize;
440	struct netbuf nbuf;
441	struct sockaddr_un sun;
442
443	/*
444	 * Try connecting to the local rpcbind through a local socket
445	 * first. If this doesn't work, try all transports defined in
446	 * the netconfig file.
447	 */
448	memset(&sun, 0, sizeof sun);
449	sock = _socket(AF_LOCAL, SOCK_STREAM, 0);
450	if (sock < 0)
451		goto try_nconf;
452	sun.sun_family = AF_LOCAL;
453	strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
454	nbuf.len = sun.sun_len = SUN_LEN(&sun);
455	nbuf.maxlen = sizeof (struct sockaddr_un);
456	nbuf.buf = &sun;
457
458	tsize = __rpc_get_t_size(AF_LOCAL, 0, 0);
459	client = clnt_vc_create(sock, &nbuf, (rpcprog_t)RPCBPROG,
460	    (rpcvers_t)RPCBVERS, tsize, tsize);
461
462	if (client != NULL) {
463		/* Mark the socket to be closed in destructor */
464		(void) CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
465		return client;
466	}
467
468	/* Nobody needs this socket anymore; free the descriptor. */
469	_close(sock);
470
471try_nconf:
472
473/* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */
474	mutex_lock(&loopnconf_lock);
475	if (loopnconf == NULL) {
476		struct netconfig *nconf, *tmpnconf = NULL;
477		void *nc_handle;
478		int fd;
479
480		nc_handle = setnetconfig();
481		if (nc_handle == NULL) {
482			/* fails to open netconfig file */
483			syslog (LOG_ERR, "rpc: failed to open " NETCONFIG);
484			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
485			mutex_unlock(&loopnconf_lock);
486			return (NULL);
487		}
488		while ((nconf = getnetconfig(nc_handle)) != NULL) {
489#ifdef INET6
490			if ((strcmp(nconf->nc_protofmly, NC_INET6) == 0 ||
491#else
492			if ((
493#endif
494			     strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
495			    (nconf->nc_semantics == NC_TPI_COTS ||
496			     nconf->nc_semantics == NC_TPI_COTS_ORD)) {
497				fd = __rpc_nconf2fd(nconf);
498				/*
499				 * Can't create a socket, assume that
500				 * this family isn't configured in the kernel.
501				 */
502				if (fd < 0)
503					continue;
504				_close(fd);
505				tmpnconf = nconf;
506				if (!strcmp(nconf->nc_protofmly, NC_INET))
507					hostname = IN4_LOCALHOST_STRING;
508				else
509					hostname = IN6_LOCALHOST_STRING;
510			}
511		}
512		if (tmpnconf == NULL) {
513			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
514			mutex_unlock(&loopnconf_lock);
515			return (NULL);
516		}
517		loopnconf = getnetconfigent(tmpnconf->nc_netid);
518		/* loopnconf is never freed */
519		endnetconfig(nc_handle);
520	}
521	mutex_unlock(&loopnconf_lock);
522	client = getclnthandle(hostname, loopnconf, NULL);
523	return (client);
524}
525
526/*
527 * Set a mapping between program, version and address.
528 * Calls the rpcbind service to do the mapping.
529 */
530bool_t
531rpcb_set(program, version, nconf, address)
532	rpcprog_t program;
533	rpcvers_t version;
534	const struct netconfig *nconf;	/* Network structure of transport */
535	const struct netbuf *address;		/* Services netconfig address */
536{
537	CLIENT *client;
538	bool_t rslt = FALSE;
539	RPCB parms;
540	char uidbuf[32];
541
542	/* parameter checking */
543	if (nconf == NULL) {
544		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
545		return (FALSE);
546	}
547	if (address == NULL) {
548		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
549		return (FALSE);
550	}
551	client = local_rpcb();
552	if (! client) {
553		return (FALSE);
554	}
555
556	/* convert to universal */
557	/*LINTED const castaway*/
558	parms.r_addr = taddr2uaddr((struct netconfig *) nconf,
559				   (struct netbuf *)address);
560	if (!parms.r_addr) {
561		CLNT_DESTROY(client);
562		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
563		return (FALSE); /* no universal address */
564	}
565	parms.r_prog = program;
566	parms.r_vers = version;
567	parms.r_netid = nconf->nc_netid;
568	/*
569	 * Though uid is not being used directly, we still send it for
570	 * completeness.  For non-unix platforms, perhaps some other
571	 * string or an empty string can be sent.
572	 */
573	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
574	parms.r_owner = uidbuf;
575
576	CLNT_CALL(client, (rpcproc_t)RPCBPROC_SET, (xdrproc_t) xdr_rpcb,
577	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
578	    (char *)(void *)&rslt, tottimeout);
579
580	CLNT_DESTROY(client);
581	free(parms.r_addr);
582	return (rslt);
583}
584
585/*
586 * Remove the mapping between program, version and netbuf address.
587 * Calls the rpcbind service to do the un-mapping.
588 * If netbuf is NULL, unset for all the transports, otherwise unset
589 * only for the given transport.
590 */
591bool_t
592rpcb_unset(program, version, nconf)
593	rpcprog_t program;
594	rpcvers_t version;
595	const struct netconfig *nconf;
596{
597	CLIENT *client;
598	bool_t rslt = FALSE;
599	RPCB parms;
600	char uidbuf[32];
601
602	client = local_rpcb();
603	if (! client) {
604		return (FALSE);
605	}
606
607	parms.r_prog = program;
608	parms.r_vers = version;
609	if (nconf)
610		parms.r_netid = nconf->nc_netid;
611	else {
612		/*LINTED const castaway*/
613		parms.r_netid = (char *) &nullstring[0]; /* unsets  all */
614	}
615	/*LINTED const castaway*/
616	parms.r_addr = (char *) &nullstring[0];
617	(void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
618	parms.r_owner = uidbuf;
619
620	CLNT_CALL(client, (rpcproc_t)RPCBPROC_UNSET, (xdrproc_t) xdr_rpcb,
621	    (char *)(void *)&parms, (xdrproc_t) xdr_bool,
622	    (char *)(void *)&rslt, tottimeout);
623
624	CLNT_DESTROY(client);
625	return (rslt);
626}
627
628/*
629 * From the merged list, find the appropriate entry
630 */
631static struct netbuf *
632got_entry(relp, nconf)
633	rpcb_entry_list_ptr relp;
634	const struct netconfig *nconf;
635{
636	struct netbuf *na = NULL;
637	rpcb_entry_list_ptr sp;
638	rpcb_entry *rmap;
639
640	for (sp = relp; sp != NULL; sp = sp->rpcb_entry_next) {
641		rmap = &sp->rpcb_entry_map;
642		if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) &&
643		    (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) &&
644		    (nconf->nc_semantics == rmap->r_nc_semantics) &&
645		    (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != NULL)) {
646			na = uaddr2taddr(nconf, rmap->r_maddr);
647#ifdef ND_DEBUG
648			fprintf(stderr, "\tRemote address is [%s].\n",
649				rmap->r_maddr);
650			if (!na)
651				fprintf(stderr,
652				    "\tCouldn't resolve remote address!\n");
653#endif
654			break;
655		}
656	}
657	return (na);
658}
659
660/*
661 * An internal function which optimizes rpcb_getaddr function.  It also
662 * returns the client handle that it uses to contact the remote rpcbind.
663 *
664 * The algorithm used: If the transports is TCP or UDP, it first tries
665 * version 2 (portmap), 4 and then 3 (svr4).  This order should be
666 * changed in the next OS release to 4, 2 and 3.  We are assuming that by
667 * that time, version 4 would be available on many machines on the network.
668 * With this algorithm, we get performance as well as a plan for
669 * obsoleting version 2.
670 *
671 * For all other transports, the algorithm remains as 4 and then 3.
672 *
673 * XXX: Due to some problems with t_connect(), we do not reuse the same client
674 * handle for COTS cases and hence in these cases we do not return the
675 * client handle.  This code will change if t_connect() ever
676 * starts working properly.  Also look under clnt_vc.c.
677 */
678struct netbuf *
679__rpcb_findaddr(program, version, nconf, host, clpp)
680	rpcprog_t program;
681	rpcvers_t version;
682	const struct netconfig *nconf;
683	const char *host;
684	CLIENT **clpp;
685{
686	CLIENT *client = NULL;
687	RPCB parms;
688	enum clnt_stat clnt_st;
689	char *ua = NULL;
690	rpcvers_t vers;
691	struct netbuf *address = NULL;
692	rpcvers_t start_vers = RPCBVERS4;
693	struct netbuf servaddr;
694
695	/* parameter checking */
696	if (nconf == NULL) {
697		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
698		return (NULL);
699	}
700
701	parms.r_addr = NULL;
702
703#ifdef PORTMAP
704	/* Try version 2 for TCP or UDP */
705	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
706		u_short port = 0;
707		struct netbuf remote;
708		rpcvers_t pmapvers = 2;
709		struct pmap pmapparms;
710
711		/*
712		 * Try UDP only - there are some portmappers out
713		 * there that use UDP only.
714		 */
715		if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
716			struct netconfig *newnconf;
717
718			if ((newnconf = getnetconfigent("udp")) == NULL) {
719				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
720				return (NULL);
721			}
722			client = getclnthandle(host, newnconf, &parms.r_addr);
723			freenetconfigent(newnconf);
724		} else {
725			client = getclnthandle(host, nconf, &parms.r_addr);
726		}
727		if (client == NULL) {
728			return (NULL);
729		}
730
731		/* Set the version */
732		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&pmapvers);
733		pmapparms.pm_prog = program;
734		pmapparms.pm_vers = version;
735		pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ?
736					IPPROTO_UDP : IPPROTO_TCP;
737		pmapparms.pm_port = 0;	/* not needed */
738		clnt_st = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
739		    (xdrproc_t) xdr_pmap, (caddr_t)(void *)&pmapparms,
740		    (xdrproc_t) xdr_u_short, (caddr_t)(void *)&port,
741		    tottimeout);
742		if (clnt_st != RPC_SUCCESS) {
743			if ((clnt_st == RPC_PROGVERSMISMATCH) ||
744				(clnt_st == RPC_PROGUNAVAIL))
745				goto try_rpcbind; /* Try different versions */
746			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
747			clnt_geterr(client, &rpc_createerr.cf_error);
748			goto error;
749		} else if (port == 0) {
750			address = NULL;
751			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
752			goto error;
753		}
754		port = htons(port);
755		CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)(void *)&remote);
756		if (((address = (struct netbuf *)
757			malloc(sizeof (struct netbuf))) == NULL) ||
758		    ((address->buf = (char *)
759			malloc(remote.len)) == NULL)) {
760			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
761			clnt_geterr(client, &rpc_createerr.cf_error);
762			if (address) {
763				free(address);
764				address = NULL;
765			}
766			goto error;
767		}
768		memcpy(address->buf, remote.buf, remote.len);
769		memcpy(&((char *)address->buf)[sizeof (short)],
770				(char *)(void *)&port, sizeof (short));
771		address->len = address->maxlen = remote.len;
772		goto done;
773	}
774#endif				/* PORTMAP */
775
776try_rpcbind:
777	/*
778	 * Now we try version 4 and then 3.
779	 * We also send the remote system the address we used to
780	 * contact it in case it can help to connect back with us
781	 */
782	parms.r_prog = program;
783	parms.r_vers = version;
784	/*LINTED const castaway*/
785	parms.r_owner = (char *) &nullstring[0];	/* not needed; */
786							/* just for xdring */
787	parms.r_netid = nconf->nc_netid; /* not really needed */
788
789	/*
790	 * If a COTS transport is being used, try getting address via CLTS
791	 * transport.  This works only with version 4.
792	 * NOTE: This is being done for all transports EXCEPT LOOPBACK
793	 * because with loopback the cost to go to a COTS is same as
794	 * the cost to go through CLTS, plus you get the advantage of
795	 * finding out immediately if the local rpcbind process is dead.
796	 */
797#if 1
798	if ((nconf->nc_semantics == NC_TPI_COTS_ORD ||
799			nconf->nc_semantics == NC_TPI_COTS) &&
800	    (strcmp(nconf->nc_protofmly, NC_LOOPBACK) != 0)) {
801#else
802	if (client != NULL) {
803		CLNT_DESTROY(client);
804		client = NULL;
805	}
806	if (nconf->nc_semantics == NC_TPI_CLTS) {
807#endif
808		void *handle;
809		struct netconfig *nconf_clts;
810		rpcb_entry_list_ptr relp = NULL;
811
812		if (client == NULL) {
813			/* This did not go through the above PORTMAP/TCP code */
814#if 1
815			if ((handle = __rpc_setconf("datagram_v")) != NULL) {
816#else
817			if ((handle = __rpc_setconf("circuit_v")) != NULL) {
818#endif
819				while ((nconf_clts = __rpc_getconf(handle))
820					!= NULL) {
821					if (strcmp(nconf_clts->nc_protofmly,
822						nconf->nc_protofmly) != 0) {
823						continue;
824					}
825					client = getclnthandle(host, nconf_clts,
826							&parms.r_addr);
827					break;
828				}
829				__rpc_endconf(handle);
830			}
831			if (client == NULL)
832				goto regular_rpcbind;	/* Go the regular way */
833		} else {
834			/* This is a UDP PORTMAP handle.  Change to version 4 */
835			vers = RPCBVERS4;
836			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
837		}
838		/*
839		 * We also send the remote system the address we used to
840		 * contact it in case it can help it connect back with us
841		 */
842		if (parms.r_addr == NULL) {
843			/*LINTED const castaway*/
844			parms.r_addr = (char *) &nullstring[0]; /* for XDRing */
845		}
846		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDRLIST,
847		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
848		    (xdrproc_t) xdr_rpcb_entry_list_ptr,
849		    (char *)(void *)&relp, tottimeout);
850		if (clnt_st == RPC_SUCCESS) {
851			if ((address = got_entry(relp, nconf)) != NULL) {
852				xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
853				    (char *)(void *)&relp);
854				goto done;
855			}
856			/* Entry not found for this transport */
857			xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
858			    (char *)(void *)&relp);
859			/*
860			 * XXX: should have perhaps returned with error but
861			 * since the remote machine might not always be able
862			 * to send the address on all transports, we try the
863			 * regular way with regular_rpcbind
864			 */
865			goto regular_rpcbind;
866		} else if ((clnt_st == RPC_PROGVERSMISMATCH) ||
867			(clnt_st == RPC_PROGUNAVAIL)) {
868			start_vers = RPCBVERS;	/* Try version 3 now */
869			goto regular_rpcbind; /* Try different versions */
870		} else {
871			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
872			clnt_geterr(client, &rpc_createerr.cf_error);
873			goto error;
874		}
875	}
876
877regular_rpcbind:
878
879	/* Now the same transport is to be used to get the address */
880#if 1
881	if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
882			(nconf->nc_semantics == NC_TPI_COTS))) {
883#else
884	if (client && nconf->nc_semantics == NC_TPI_CLTS) {
885#endif
886		/* A CLTS type of client - destroy it */
887		CLNT_DESTROY(client);
888		client = NULL;
889	}
890
891	if (client == NULL) {
892		client = getclnthandle(host, nconf, &parms.r_addr);
893		if (client == NULL) {
894			goto error;
895		}
896	}
897	if (parms.r_addr == NULL) {
898		/*LINTED const castaway*/
899		parms.r_addr = (char *) &nullstring[0];
900	}
901
902	/* First try from start_vers and then version 3 (RPCBVERS) */
903	for (vers = start_vers;  vers >= RPCBVERS; vers--) {
904		/* Set the version */
905		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
906		clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
907		    (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
908		    (xdrproc_t) xdr_wrapstring, (char *)(void *) &ua,
909		    tottimeout);
910		if (clnt_st == RPC_SUCCESS) {
911			if ((ua == NULL) || (ua[0] == NULL)) {
912				/* address unknown */
913				rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
914				goto error;
915			}
916			address = uaddr2taddr(nconf, ua);
917#ifdef ND_DEBUG
918			fprintf(stderr, "\tRemote address is [%s]\n", ua);
919			if (!address)
920				fprintf(stderr,
921					"\tCouldn't resolve remote address!\n");
922#endif
923			xdr_free((xdrproc_t)xdr_wrapstring,
924			    (char *)(void *)&ua);
925
926			if (! address) {
927				/* We don't know about your universal address */
928				rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
929				goto error;
930			}
931			CLNT_CONTROL(client, CLGET_SVC_ADDR,
932			    (char *)(void *)&servaddr);
933			__rpc_fixup_addr(address, &servaddr);
934			goto done;
935		} else if (clnt_st == RPC_PROGVERSMISMATCH) {
936			struct rpc_err rpcerr;
937
938			clnt_geterr(client, &rpcerr);
939			if (rpcerr.re_vers.low > RPCBVERS4)
940				goto error;  /* a new version, can't handle */
941		} else if (clnt_st != RPC_PROGUNAVAIL) {
942			/* Cant handle this error */
943			rpc_createerr.cf_stat = clnt_st;
944			clnt_geterr(client, &rpc_createerr.cf_error);
945			goto error;
946		}
947	}
948
949	if ((address == NULL) || (address->len == 0)) {
950		rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
951		clnt_geterr(client, &rpc_createerr.cf_error);
952	}
953
954error:
955	if (client) {
956		CLNT_DESTROY(client);
957		client = NULL;
958	}
959done:
960	if (nconf->nc_semantics != NC_TPI_CLTS) {
961		/* This client is the connectionless one */
962		if (client) {
963			CLNT_DESTROY(client);
964			client = NULL;
965		}
966	}
967	if (clpp) {
968		*clpp = client;
969	} else if (client) {
970		CLNT_DESTROY(client);
971	}
972	if (parms.r_addr != NULL && parms.r_addr != nullstring)
973		free(parms.r_addr);
974	return (address);
975}
976
977
978/*
979 * Find the mapped address for program, version.
980 * Calls the rpcbind service remotely to do the lookup.
981 * Uses the transport specified in nconf.
982 * Returns FALSE (0) if no map exists, else returns 1.
983 *
984 * Assuming that the address is all properly allocated
985 */
986int
987rpcb_getaddr(program, version, nconf, address, host)
988	rpcprog_t program;
989	rpcvers_t version;
990	const struct netconfig *nconf;
991	struct netbuf *address;
992	const char *host;
993{
994	struct netbuf *na;
995
996	if ((na = __rpcb_findaddr(program, version, nconf,
997				host, (CLIENT **) NULL)) == NULL)
998		return (FALSE);
999
1000	if (na->len > address->maxlen) {
1001		/* Too long address */
1002		free(na->buf);
1003		free(na);
1004		rpc_createerr.cf_stat = RPC_FAILED;
1005		return (FALSE);
1006	}
1007	memcpy(address->buf, na->buf, (size_t)na->len);
1008	address->len = na->len;
1009	free(na->buf);
1010	free(na);
1011	return (TRUE);
1012}
1013
1014/*
1015 * Get a copy of the current maps.
1016 * Calls the rpcbind service remotely to get the maps.
1017 *
1018 * It returns only a list of the services
1019 * It returns NULL on failure.
1020 */
1021rpcblist *
1022rpcb_getmaps(nconf, host)
1023	const struct netconfig *nconf;
1024	const char *host;
1025{
1026	rpcblist_ptr head = NULL;
1027	CLIENT *client;
1028	enum clnt_stat clnt_st;
1029	rpcvers_t vers = 0;
1030
1031	client = getclnthandle(host, nconf, NULL);
1032	if (client == NULL) {
1033		return (head);
1034	}
1035	clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
1036	    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
1037	    (char *)(void *)&head, tottimeout);
1038	if (clnt_st == RPC_SUCCESS)
1039		goto done;
1040
1041	if ((clnt_st != RPC_PROGVERSMISMATCH) &&
1042	    (clnt_st != RPC_PROGUNAVAIL)) {
1043		rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1044		clnt_geterr(client, &rpc_createerr.cf_error);
1045		goto done;
1046	}
1047
1048	/* fall back to earlier version */
1049	CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
1050	if (vers == RPCBVERS4) {
1051		vers = RPCBVERS;
1052		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
1053		if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
1054		    (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
1055		    (char *)(void *)&head, tottimeout) == RPC_SUCCESS)
1056			goto done;
1057	}
1058	rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1059	clnt_geterr(client, &rpc_createerr.cf_error);
1060
1061done:
1062	CLNT_DESTROY(client);
1063	return (head);
1064}
1065
1066/*
1067 * rpcbinder remote-call-service interface.
1068 * This routine is used to call the rpcbind remote call service
1069 * which will look up a service program in the address maps, and then
1070 * remotely call that routine with the given parameters. This allows
1071 * programs to do a lookup and call in one step.
1072*/
1073enum clnt_stat
1074rpcb_rmtcall(nconf, host, prog, vers, proc, xdrargs, argsp,
1075		xdrres, resp, tout, addr_ptr)
1076	const struct netconfig *nconf;	/* Netconfig structure */
1077	const char *host;			/* Remote host name */
1078	rpcprog_t prog;
1079	rpcvers_t vers;
1080	rpcproc_t proc;			/* Remote proc identifiers */
1081	xdrproc_t xdrargs, xdrres;	/* XDR routines */
1082	caddr_t argsp, resp;		/* Argument and Result */
1083	struct timeval tout;		/* Timeout value for this call */
1084	const struct netbuf *addr_ptr;	/* Preallocated netbuf address */
1085{
1086	CLIENT *client;
1087	enum clnt_stat stat;
1088	struct r_rpcb_rmtcallargs a;
1089	struct r_rpcb_rmtcallres r;
1090	rpcvers_t rpcb_vers;
1091
1092	stat = 0;
1093	client = getclnthandle(host, nconf, NULL);
1094	if (client == NULL) {
1095		return (RPC_FAILED);
1096	}
1097	/*LINTED const castaway*/
1098	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)(void *)&rmttimeout);
1099	a.prog = prog;
1100	a.vers = vers;
1101	a.proc = proc;
1102	a.args.args_val = argsp;
1103	a.xdr_args = xdrargs;
1104	r.addr = NULL;
1105	r.results.results_val = resp;
1106	r.xdr_res = xdrres;
1107
1108	for (rpcb_vers = RPCBVERS4; rpcb_vers >= RPCBVERS; rpcb_vers--) {
1109		CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&rpcb_vers);
1110		stat = CLNT_CALL(client, (rpcproc_t)RPCBPROC_CALLIT,
1111		    (xdrproc_t) xdr_rpcb_rmtcallargs, (char *)(void *)&a,
1112		    (xdrproc_t) xdr_rpcb_rmtcallres, (char *)(void *)&r, tout);
1113		if ((stat == RPC_SUCCESS) && (addr_ptr != NULL)) {
1114			struct netbuf *na;
1115			/*LINTED const castaway*/
1116			na = uaddr2taddr((struct netconfig *) nconf, r.addr);
1117			if (!na) {
1118				stat = RPC_N2AXLATEFAILURE;
1119				/*LINTED const castaway*/
1120				((struct netbuf *) addr_ptr)->len = 0;
1121				goto error;
1122			}
1123			if (na->len > addr_ptr->maxlen) {
1124				/* Too long address */
1125				stat = RPC_FAILED; /* XXX A better error no */
1126				free(na->buf);
1127				free(na);
1128				/*LINTED const castaway*/
1129				((struct netbuf *) addr_ptr)->len = 0;
1130				goto error;
1131			}
1132			memcpy(addr_ptr->buf, na->buf, (size_t)na->len);
1133			/*LINTED const castaway*/
1134			((struct netbuf *)addr_ptr)->len = na->len;
1135			free(na->buf);
1136			free(na);
1137			break;
1138		} else if ((stat != RPC_PROGVERSMISMATCH) &&
1139			    (stat != RPC_PROGUNAVAIL)) {
1140			goto error;
1141		}
1142	}
1143error:
1144	CLNT_DESTROY(client);
1145	if (r.addr)
1146		xdr_free((xdrproc_t) xdr_wrapstring, (char *)(void *)&r.addr);
1147	return (stat);
1148}
1149
1150/*
1151 * Gets the time on the remote host.
1152 * Returns 1 if succeeds else 0.
1153 */
1154bool_t
1155rpcb_gettime(host, timep)
1156	const char *host;
1157	time_t *timep;
1158{
1159	CLIENT *client = NULL;
1160	void *handle;
1161	struct netconfig *nconf;
1162	rpcvers_t vers;
1163	enum clnt_stat st;
1164
1165
1166	if ((host == NULL) || (host[0] == NULL)) {
1167		time(timep);
1168		return (TRUE);
1169	}
1170
1171	if ((handle = __rpc_setconf("netpath")) == NULL) {
1172		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1173		return (FALSE);
1174	}
1175	rpc_createerr.cf_stat = RPC_SUCCESS;
1176	while (client == NULL) {
1177		if ((nconf = __rpc_getconf(handle)) == NULL) {
1178			if (rpc_createerr.cf_stat == RPC_SUCCESS)
1179				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1180			break;
1181		}
1182		client = getclnthandle(host, nconf, NULL);
1183		if (client)
1184			break;
1185	}
1186	__rpc_endconf(handle);
1187	if (client == (CLIENT *) NULL) {
1188		return (FALSE);
1189	}
1190
1191	st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
1192		(xdrproc_t) xdr_void, NULL,
1193		(xdrproc_t) xdr_int, (char *)(void *)timep, tottimeout);
1194
1195	if ((st == RPC_PROGVERSMISMATCH) || (st == RPC_PROGUNAVAIL)) {
1196		CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
1197		if (vers == RPCBVERS4) {
1198			/* fall back to earlier version */
1199			vers = RPCBVERS;
1200			CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
1201			st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
1202				(xdrproc_t) xdr_void, NULL,
1203				(xdrproc_t) xdr_int, (char *)(void *)timep,
1204				tottimeout);
1205		}
1206	}
1207	CLNT_DESTROY(client);
1208	return (st == RPC_SUCCESS? TRUE: FALSE);
1209}
1210
1211/*
1212 * Converts taddr to universal address.  This routine should never
1213 * really be called because local n2a libraries are always provided.
1214 */
1215char *
1216rpcb_taddr2uaddr(nconf, taddr)
1217	struct netconfig *nconf;
1218	struct netbuf *taddr;
1219{
1220	CLIENT *client;
1221	char *uaddr = NULL;
1222
1223
1224	/* parameter checking */
1225	if (nconf == NULL) {
1226		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1227		return (NULL);
1228	}
1229	if (taddr == NULL) {
1230		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1231		return (NULL);
1232	}
1233	client = local_rpcb();
1234	if (! client) {
1235		return (NULL);
1236	}
1237
1238	CLNT_CALL(client, (rpcproc_t)RPCBPROC_TADDR2UADDR,
1239	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
1240	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr, tottimeout);
1241	CLNT_DESTROY(client);
1242	return (uaddr);
1243}
1244
1245/*
1246 * Converts universal address to netbuf.  This routine should never
1247 * really be called because local n2a libraries are always provided.
1248 */
1249struct netbuf *
1250rpcb_uaddr2taddr(nconf, uaddr)
1251	struct netconfig *nconf;
1252	char *uaddr;
1253{
1254	CLIENT *client;
1255	struct netbuf *taddr;
1256
1257
1258	/* parameter checking */
1259	if (nconf == NULL) {
1260		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1261		return (NULL);
1262	}
1263	if (uaddr == NULL) {
1264		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1265		return (NULL);
1266	}
1267	client = local_rpcb();
1268	if (! client) {
1269		return (NULL);
1270	}
1271
1272	taddr = (struct netbuf *)calloc(1, sizeof (struct netbuf));
1273	if (taddr == NULL) {
1274		CLNT_DESTROY(client);
1275		return (NULL);
1276	}
1277	if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_UADDR2TADDR,
1278	    (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr,
1279	    (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
1280	    tottimeout) != RPC_SUCCESS) {
1281		free(taddr);
1282		taddr = NULL;
1283	}
1284	CLNT_DESTROY(client);
1285	return (taddr);
1286}
1287