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