rpcb_clnt.c revision 1219:f89f56c2d9ac
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23/*
24 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
29/* All Rights Reserved */
30/*
31 * Portions of this source code were derived from Berkeley
32 * 4.3 BSD under license from the Regents of the University of
33 * California.
34 */
35
36#pragma ident	"%Z%%M%	%I%	%E% SMI"
37
38/*
39 * interface to rpcbind rpc service.
40 */
41
42#include "mt.h"
43#include "rpc_mt.h"
44#include <assert.h>
45#include <rpc/rpc.h>
46#include <rpc/rpcb_prot.h>
47#include <netconfig.h>
48#include <netdir.h>
49#include <rpc/nettype.h>
50#include <syslog.h>
51#ifdef PORTMAP
52#include <netinet/in.h>		/* FOR IPPROTO_TCP/UDP definitions */
53#include <rpc/pmap_prot.h>
54#endif
55#ifdef ND_DEBUG
56#include <stdio.h>
57#endif
58#include <sys/utsname.h>
59#include <errno.h>
60#include <stdlib.h>
61#include <string.h>
62#include <unistd.h>
63
64static struct timeval tottimeout = { 60, 0 };
65static const struct timeval rmttimeout = { 3, 0 };
66static struct timeval rpcbrmttime = { 15, 0 };
67
68extern bool_t xdr_wrapstring(XDR *, char **);
69
70static const char nullstring[] = "\000";
71
72extern CLIENT *_clnt_tli_create_timed(int, const struct netconfig *,
73			struct netbuf *, rpcprog_t, rpcvers_t, uint_t, uint_t,
74			const struct timeval *);
75
76static CLIENT *_getclnthandle_timed(char *, struct netconfig *, char **,
77			struct timeval *);
78
79
80/*
81 * The life time of a cached entry should not exceed 5 minutes
82 * since automountd attempts an unmount every 5 minutes.
83 * It is arbitrarily set a little lower (3 min = 180 sec)
84 * to reduce the time during which an entry is stale.
85 */
86#define	CACHE_TTL 180
87#define	CACHESIZE 6
88
89struct address_cache {
90	char *ac_host;
91	char *ac_netid;
92	char *ac_uaddr;
93	struct netbuf *ac_taddr;
94	struct address_cache *ac_next;
95	time_t ac_maxtime;
96};
97
98static struct address_cache *front;
99static int cachesize;
100
101extern int lowvers;
102extern int authdes_cachesz;
103/*
104 * This routine adjusts the timeout used for calls to the remote rpcbind.
105 * Also, this routine can be used to set the use of portmapper version 2
106 * only when doing rpc_broadcasts
107 * These are private routines that may not be provided in future releases.
108 */
109bool_t
110__rpc_control(int request, void *info)
111{
112	switch (request) {
113	case CLCR_GET_RPCB_TIMEOUT:
114		*(struct timeval *)info = tottimeout;
115		break;
116	case CLCR_SET_RPCB_TIMEOUT:
117		tottimeout = *(struct timeval *)info;
118		break;
119	case CLCR_GET_LOWVERS:
120		*(int *)info = lowvers;
121		break;
122	case CLCR_SET_LOWVERS:
123		lowvers = *(int *)info;
124		break;
125	case CLCR_GET_RPCB_RMTTIME:
126		*(struct timeval *)info = rpcbrmttime;
127		break;
128	case CLCR_SET_RPCB_RMTTIME:
129		rpcbrmttime = *(struct timeval *)info;
130		break;
131	case CLCR_GET_CRED_CACHE_SZ:
132		*(int *)info = authdes_cachesz;
133		break;
134	case CLCR_SET_CRED_CACHE_SZ:
135		authdes_cachesz = *(int *)info;
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(char *host, char *netid)
163{
164	struct address_cache *cptr;
165
166	/* READ LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
167
168	assert(RW_READ_HELD(&rpcbaddr_cache_lock));
169	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
170		if ((strcmp(cptr->ac_host, host) == 0) &&
171		    (strcmp(cptr->ac_netid, netid) == 0) &&
172			(time(NULL) <= cptr->ac_maxtime)) {
173#ifdef ND_DEBUG
174			fprintf(stderr, "Found cache entry for %s: %s\n",
175				host, netid);
176#endif
177			return (cptr);
178		}
179	}
180	return (NULL);
181}
182
183static void
184delete_cache(struct netbuf *addr)
185{
186	struct address_cache *cptr, *prevptr = NULL;
187
188	/* WRITE LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
189	assert(RW_WRITE_HELD(&rpcbaddr_cache_lock));
190	for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
191		if (!memcmp(cptr->ac_taddr->buf, addr->buf, addr->len)) {
192			free(cptr->ac_host);
193			free(cptr->ac_netid);
194			free(cptr->ac_taddr->buf);
195			free(cptr->ac_taddr);
196			if (cptr->ac_uaddr)
197				free(cptr->ac_uaddr);
198			if (prevptr)
199				prevptr->ac_next = cptr->ac_next;
200			else
201				front = cptr->ac_next;
202			free(cptr);
203			cachesize--;
204			break;
205		}
206		prevptr = cptr;
207	}
208}
209
210static void
211add_cache(char *host, char *netid, struct netbuf *taddr, char *uaddr)
212{
213	struct address_cache  *ad_cache, *cptr, *prevptr;
214
215	ad_cache = malloc(sizeof (struct address_cache));
216	if (!ad_cache) {
217		goto memerr;
218	}
219	ad_cache->ac_maxtime = time(NULL) + CACHE_TTL;
220	ad_cache->ac_host = strdup(host);
221	ad_cache->ac_netid = strdup(netid);
222	ad_cache->ac_uaddr = uaddr ? strdup(uaddr) : NULL;
223	ad_cache->ac_taddr = malloc(sizeof (struct netbuf));
224	if (!ad_cache->ac_host || !ad_cache->ac_netid || !ad_cache->ac_taddr ||
225		(uaddr && !ad_cache->ac_uaddr)) {
226		goto memerr1;
227	}
228
229	ad_cache->ac_taddr->len = ad_cache->ac_taddr->maxlen = taddr->len;
230	ad_cache->ac_taddr->buf = malloc(taddr->len);
231	if (ad_cache->ac_taddr->buf == NULL) {
232		goto memerr1;
233	}
234
235	(void) memcpy(ad_cache->ac_taddr->buf, taddr->buf, taddr->len);
236#ifdef ND_DEBUG
237	(void) fprintf(stderr, "Added to cache: %s : %s\n", host, netid);
238#endif
239
240/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  cptr */
241
242	(void) rw_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	(void) rw_unlock(&rpcbaddr_cache_lock);
278	return;
279memerr1:
280	if (ad_cache->ac_host)
281		free(ad_cache->ac_host);
282	if (ad_cache->ac_netid)
283		free(ad_cache->ac_netid);
284	if (ad_cache->ac_uaddr)
285		free(ad_cache->ac_uaddr);
286	if (ad_cache->ac_taddr)
287		free(ad_cache->ac_taddr);
288	free(ad_cache);
289memerr:
290	syslog(LOG_ERR, "add_cache : out of memory.");
291}
292
293/*
294 * This routine will return a client handle that is connected to the
295 * rpcbind. Returns NULL on error and free's everything.
296 */
297static CLIENT *
298getclnthandle(char *host, struct netconfig *nconf, char **targaddr)
299{
300	return (_getclnthandle_timed(host, nconf, targaddr, NULL));
301}
302
303/*
304 * Same as getclnthandle() except it takes an extra timeout argument.
305 * This is for bug 4049792: clnt_create_timed does not timeout.
306 *
307 * If tp is NULL, use default timeout to get a client handle.
308 */
309static CLIENT *
310_getclnthandle_timed(char *host, struct netconfig *nconf, char **targaddr,
311							struct timeval *tp)
312{
313	CLIENT *client = NULL;
314	struct netbuf *addr;
315	struct netbuf addr_to_delete;
316	struct nd_addrlist *nas;
317	struct nd_hostserv rpcbind_hs;
318	struct address_cache *ad_cache;
319	char *tmpaddr;
320	int neterr;
321	int j;
322
323/* VARIABLES PROTECTED BY rpcbaddr_cache_lock:  ad_cache */
324
325	/* Get the address of the rpcbind.  Check cache first */
326	addr_to_delete.len = 0;
327	(void) rw_rdlock(&rpcbaddr_cache_lock);
328	ad_cache = check_cache(host, nconf->nc_netid);
329	if (ad_cache != NULL) {
330		addr = ad_cache->ac_taddr;
331		client = _clnt_tli_create_timed(RPC_ANYFD, nconf, addr,
332				RPCBPROG, RPCBVERS4, 0, 0, tp);
333		if (client != NULL) {
334			if (targaddr) {
335				/*
336				 * case where a client handle is created
337				 * without a targaddr and the handle is
338				 * requested with a targaddr
339				 */
340				if (ad_cache->ac_uaddr != NULL) {
341					*targaddr = strdup(ad_cache->ac_uaddr);
342					if (*targaddr == NULL) {
343						syslog(LOG_ERR,
344						"_getclnthandle_timed: strdup "
345						"failed.");
346						rpc_createerr.cf_stat =
347							RPC_SYSTEMERROR;
348						(void) rw_unlock(
349							&rpcbaddr_cache_lock);
350						return (NULL);
351					}
352				} else {
353					*targaddr = NULL;
354				}
355			}
356			(void) rw_unlock(&rpcbaddr_cache_lock);
357			return (client);
358		}
359		if (rpc_createerr.cf_stat == RPC_SYSTEMERROR) {
360			(void) rw_unlock(&rpcbaddr_cache_lock);
361			return (NULL);
362		}
363		addr_to_delete.len = addr->len;
364		addr_to_delete.buf = malloc(addr->len);
365		if (addr_to_delete.buf == NULL) {
366			addr_to_delete.len = 0;
367		} else {
368			(void) memcpy(addr_to_delete.buf, addr->buf, addr->len);
369		}
370	}
371	(void) rw_unlock(&rpcbaddr_cache_lock);
372	if (addr_to_delete.len != 0) {
373		/*
374		 * Assume this may be due to cache data being
375		 *  outdated
376		 */
377		(void) rw_wrlock(&rpcbaddr_cache_lock);
378		delete_cache(&addr_to_delete);
379		(void) rw_unlock(&rpcbaddr_cache_lock);
380		free(addr_to_delete.buf);
381	}
382	rpcbind_hs.h_host = host;
383	rpcbind_hs.h_serv = "rpcbind";
384#ifdef ND_DEBUG
385	fprintf(stderr, "rpcbind client routines: diagnostics :\n");
386	fprintf(stderr, "\tGetting address for (%s, %s, %s) ... \n",
387		rpcbind_hs.h_host, rpcbind_hs.h_serv, nconf->nc_netid);
388#endif
389
390	if ((neterr = netdir_getbyname(nconf, &rpcbind_hs, &nas)) != 0) {
391		if (neterr == ND_NOHOST)
392			rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
393		else
394			rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
395		return (NULL);
396	}
397	/* XXX nas should perhaps be cached for better performance */
398
399	for (j = 0; j < nas->n_cnt; j++) {
400		addr = &(nas->n_addrs[j]);
401#ifdef ND_DEBUG
402{
403	int i;
404	char *ua;
405
406	ua = taddr2uaddr(nconf, &(nas->n_addrs[j]));
407	fprintf(stderr, "Got it [%s]\n", ua);
408	free(ua);
409
410	fprintf(stderr, "\tnetbuf len = %d, maxlen = %d\n",
411		addr->len, addr->maxlen);
412	fprintf(stderr, "\tAddress is ");
413	for (i = 0; i < addr->len; i++)
414		fprintf(stderr, "%u.", addr->buf[i]);
415	fprintf(stderr, "\n");
416}
417#endif
418	client = _clnt_tli_create_timed(RPC_ANYFD, nconf, addr, RPCBPROG,
419				RPCBVERS4, 0, 0, tp);
420	if (client)
421		break;
422	}
423#ifdef ND_DEBUG
424	if (!client) {
425		clnt_pcreateerror("rpcbind clnt interface");
426	}
427#endif
428
429	if (client) {
430		tmpaddr = targaddr ? taddr2uaddr(nconf, addr) : NULL;
431		add_cache(host, nconf->nc_netid, addr, tmpaddr);
432		if (targaddr) {
433			*targaddr = tmpaddr;
434		}
435	}
436	netdir_free((char *)nas, ND_ADDRLIST);
437	return (client);
438}
439
440/*
441 * This routine will return a client handle that is connected to the local
442 * rpcbind. Returns NULL on error and free's everything.
443 */
444static CLIENT *
445local_rpcb(void)
446{
447	static struct netconfig *loopnconf;
448	static char *hostname;
449	extern mutex_t loopnconf_lock;
450
451/* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */
452	(void) mutex_lock(&loopnconf_lock);
453	if (loopnconf == NULL) {
454		struct utsname utsname;
455		struct netconfig *nconf, *tmpnconf = NULL;
456		void *nc_handle;
457
458		if (hostname == NULL) {
459#if defined(__i386) && !defined(__amd64)
460			if ((_nuname(&utsname) == -1) ||
461#else
462			if ((uname(&utsname) == -1) ||
463#endif
464			    ((hostname = strdup(utsname.nodename)) == NULL)) {
465				syslog(LOG_ERR, "local_rpcb : strdup failed.");
466				rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
467				(void) mutex_unlock(&loopnconf_lock);
468				return (NULL);
469			}
470		}
471		nc_handle = setnetconfig();
472		if (nc_handle == NULL) {
473			/* fails to open netconfig file */
474			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
475			(void) mutex_unlock(&loopnconf_lock);
476			return (NULL);
477		}
478		while (nconf = getnetconfig(nc_handle)) {
479			if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
480				tmpnconf = nconf;
481				if (nconf->nc_semantics == NC_TPI_CLTS)
482					break;
483			}
484		}
485		if (tmpnconf == NULL) {
486			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
487			(void) mutex_unlock(&loopnconf_lock);
488			return (NULL);
489		}
490		loopnconf = getnetconfigent(tmpnconf->nc_netid);
491		/* loopnconf is never freed */
492		(void) endnetconfig(nc_handle);
493	}
494	(void) mutex_unlock(&loopnconf_lock);
495	return (getclnthandle(hostname, loopnconf, NULL));
496}
497
498/*
499 * Set a mapping between program, version and address.
500 * Calls the rpcbind service to do the mapping.
501 */
502bool_t
503rpcb_set(const rpcprog_t program, const rpcvers_t version,
504		const struct netconfig *nconf, const struct netbuf *address)
505{
506	CLIENT *client;
507	bool_t rslt = FALSE;
508	RPCB parms;
509	char uidbuf[32];
510
511	/* parameter checking */
512	if (nconf == NULL) {
513		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
514		return (FALSE);
515	}
516	if (address == NULL) {
517		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
518		return (FALSE);
519	}
520	client = local_rpcb();
521	if (!client)
522		return (FALSE);
523
524	parms.r_addr = taddr2uaddr((struct netconfig *)nconf,
525			(struct netbuf *)address); /* convert to universal */
526	if (!parms.r_addr) {
527		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
528		return (FALSE); /* no universal address */
529	}
530	parms.r_prog = program;
531	parms.r_vers = version;
532	parms.r_netid = nconf->nc_netid;
533	/*
534	 * Though uid is not being used directly, we still send it for
535	 * completeness.  For non-unix platforms, perhaps some other
536	 * string or an empty string can be sent.
537	 */
538	(void) sprintf(uidbuf, "%d", (int)geteuid());
539	parms.r_owner = uidbuf;
540
541	CLNT_CALL(client, RPCBPROC_SET, (xdrproc_t)xdr_rpcb, (char *)&parms,
542			(xdrproc_t)xdr_bool, (char *)&rslt, tottimeout);
543
544	CLNT_DESTROY(client);
545	free(parms.r_addr);
546	return (rslt);
547}
548
549/*
550 * Remove the mapping between program, version and netbuf address.
551 * Calls the rpcbind service to do the un-mapping.
552 * If netbuf is NULL, unset for all the transports, otherwise unset
553 * only for the given transport.
554 */
555bool_t
556rpcb_unset(const rpcprog_t program, const rpcvers_t version,
557						const struct netconfig *nconf)
558{
559	CLIENT *client;
560	bool_t rslt = FALSE;
561	RPCB parms;
562	char uidbuf[32];
563
564	client = local_rpcb();
565	if (!client)
566		return (FALSE);
567
568	parms.r_prog = program;
569	parms.r_vers = version;
570	if (nconf)
571		parms.r_netid = nconf->nc_netid;
572	else
573		parms.r_netid = (char *)&nullstring[0]; /* unsets  all */
574	parms.r_addr = (char *)&nullstring[0];
575	(void) sprintf(uidbuf, "%d", (int)geteuid());
576	parms.r_owner = uidbuf;
577
578	CLNT_CALL(client, RPCBPROC_UNSET, (xdrproc_t)xdr_rpcb, (char *)&parms,
579			(xdrproc_t)xdr_bool, (char *)&rslt, tottimeout);
580
581	CLNT_DESTROY(client);
582	return (rslt);
583}
584
585/*
586 * From the merged list, find the appropriate entry
587 */
588static struct netbuf *
589got_entry(rpcb_entry_list_ptr relp, struct netconfig *nconf)
590{
591	struct netbuf *na = NULL;
592	rpcb_entry_list_ptr sp;
593	rpcb_entry *rmap;
594
595	for (sp = relp; sp != NULL; sp = sp->rpcb_entry_next) {
596		rmap = &sp->rpcb_entry_map;
597		if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) &&
598		    (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) &&
599		    (nconf->nc_semantics == rmap->r_nc_semantics) &&
600		    (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != NULL)) {
601			na = uaddr2taddr(nconf, rmap->r_maddr);
602#ifdef ND_DEBUG
603			fprintf(stderr, "\tRemote address is [%s].\n",
604				rmap->r_maddr);
605			if (!na)
606				fprintf(stderr,
607				    "\tCouldn't resolve remote address!\n");
608#endif
609			break;
610		}
611	}
612	return (na);
613}
614
615/*
616 * Quick check to see if rpcbind is up.  Tries to connect over
617 * local transport.
618 */
619bool_t
620__rpcbind_is_up(void)
621{
622	struct utsname name;
623	char uaddr[SYS_NMLN];
624	struct netbuf *addr;
625	int fd;
626	struct t_call *sndcall;
627	struct netconfig *netconf;
628	bool_t res;
629
630#if defined(__i386) && !defined(__amd64)
631	if (_nuname(&name) == -1)
632#else
633	if (uname(&name) == -1)
634#endif
635		return (TRUE);
636
637	if ((fd = t_open("/dev/ticotsord", O_RDWR, NULL)) == -1)
638		return (TRUE);
639
640	if (t_bind(fd, NULL, NULL) == -1) {
641		(void) t_close(fd);
642		return (TRUE);
643	}
644
645	/* LINTED pointer cast */
646	if ((sndcall = (struct t_call *)t_alloc(fd, T_CALL, 0)) == NULL) {
647		(void) t_close(fd);
648		return (TRUE);
649	}
650
651	uaddr[0] = '\0';
652	(void) strcpy(uaddr, name.nodename);
653	(void) strcat(uaddr, ".rpc");
654	if ((netconf = getnetconfigent("ticotsord")) == NULL) {
655		(void) t_free((char *)sndcall, T_CALL);
656		(void) t_close(fd);
657		return (FALSE);
658	}
659	addr = uaddr2taddr(netconf, uaddr);
660	freenetconfigent(netconf);
661	if (addr == NULL || addr->buf == NULL) {
662		if (addr)
663			free(addr);
664		(void) t_free((char *)sndcall, T_CALL);
665		(void) t_close(fd);
666		return (FALSE);
667	}
668	sndcall->addr.maxlen = addr->maxlen;
669	sndcall->addr.len = addr->len;
670	sndcall->addr.buf = addr->buf;
671
672	if (t_connect(fd, sndcall, NULL) == -1)
673		res = FALSE;
674	else
675		res = TRUE;
676
677	sndcall->addr.maxlen = sndcall->addr.len = 0;
678	sndcall->addr.buf = NULL;
679	(void) t_free((char *)sndcall, T_CALL);
680	free(addr->buf);
681	free(addr);
682	(void) t_close(fd);
683
684	return (res);
685}
686
687
688/*
689 * An internal function which optimizes rpcb_getaddr function.  It also
690 * returns the client handle that it uses to contact the remote rpcbind.
691 *
692 * The algorithm used: If the transports is TCP or UDP, it first tries
693 * version 2 (portmap), 4 and then 3 (svr4).  This order should be
694 * changed in the next OS release to 4, 2 and 3.  We are assuming that by
695 * that time, version 4 would be available on many machines on the network.
696 * With this algorithm, we get performance as well as a plan for
697 * obsoleting version 2.
698 *
699 * For all other transports, the algorithm remains as 4 and then 3.
700 *
701 * XXX: Due to some problems with t_connect(), we do not reuse the same client
702 * handle for COTS cases and hence in these cases we do not return the
703 * client handle.  This code will change if t_connect() ever
704 * starts working properly.  Also look under clnt_vc.c.
705 */
706struct netbuf *
707__rpcb_findaddr_timed(rpcprog_t program, rpcvers_t version,
708	struct netconfig *nconf, char *host, CLIENT **clpp, struct timeval *tp)
709{
710	static bool_t check_rpcbind = TRUE;
711	CLIENT *client = NULL;
712	RPCB parms;
713	enum clnt_stat clnt_st;
714	char *ua = NULL;
715	uint_t vers;
716	struct netbuf *address = NULL;
717	uint_t start_vers = RPCBVERS4;
718
719	/* parameter checking */
720	if (nconf == NULL) {
721		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
722		return (NULL);
723	}
724
725	parms.r_addr = NULL;
726
727	/*
728	 * Use default total timeout if no timeout is specified.
729	 */
730	if (tp == NULL)
731		tp = &tottimeout;
732
733#ifdef PORTMAP
734	/* Try version 2 for TCP or UDP */
735	if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
736		ushort_t port = 0;
737		struct netbuf remote;
738		uint_t pmapvers = 2;
739		struct pmap pmapparms;
740
741		/*
742		 * Try UDP only - there are some portmappers out
743		 * there that use UDP only.
744		 */
745		if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
746			struct netconfig *newnconf;
747			void *handle;
748
749			if ((handle = __rpc_setconf("udp")) == NULL) {
750				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
751				return (NULL);
752			}
753
754			/*
755			 * The following to reinforce that you can
756			 * only request for remote address through
757			 * the same transport you are requesting.
758			 * ie. requesting unversial address
759			 * of IPv4 has to be carried through IPv4.
760			 * Can't use IPv6 to send out the request.
761			 * The mergeaddr in rpcbind can't handle
762			 * this.
763			 */
764			for (;;) {
765				if ((newnconf = __rpc_getconf(handle))
766								    == NULL) {
767					__rpc_endconf(handle);
768					rpc_createerr.cf_stat =
769					    RPC_UNKNOWNPROTO;
770					return (NULL);
771				}
772				/*
773				 * here check the protocol family to
774				 * be consistent with the request one
775				 */
776				if (strcmp(newnconf->nc_protofmly,
777				    nconf->nc_protofmly) == NULL)
778					break;
779			}
780
781			client = _getclnthandle_timed(host, newnconf,
782					&parms.r_addr, tp);
783			__rpc_endconf(handle);
784		} else {
785			client = _getclnthandle_timed(host, nconf,
786					&parms.r_addr, tp);
787		}
788		if (client == NULL)
789			return (NULL);
790
791		/*
792		 * Set version and retry timeout.
793		 */
794		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
795		CLNT_CONTROL(client, CLSET_VERS, (char *)&pmapvers);
796
797		pmapparms.pm_prog = program;
798		pmapparms.pm_vers = version;
799		pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ?
800				    IPPROTO_UDP : IPPROTO_TCP;
801		pmapparms.pm_port = 0;	/* not needed */
802		clnt_st = CLNT_CALL(client, PMAPPROC_GETPORT,
803				    (xdrproc_t)xdr_pmap, (caddr_t)&pmapparms,
804				    (xdrproc_t)xdr_u_short, (caddr_t)&port,
805				    *tp);
806		if (clnt_st != RPC_SUCCESS) {
807			if ((clnt_st == RPC_PROGVERSMISMATCH) ||
808			    (clnt_st == RPC_PROGUNAVAIL))
809				goto try_rpcbind; /* Try different versions */
810			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
811			clnt_geterr(client, &rpc_createerr.cf_error);
812			goto error;
813		} else if (port == 0) {
814			address = NULL;
815			rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
816			goto error;
817		}
818		port = htons(port);
819		CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)&remote);
820		if (((address = malloc(sizeof (struct netbuf))) == NULL) ||
821		    ((address->buf = malloc(remote.len)) == NULL)) {
822			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
823			clnt_geterr(client, &rpc_createerr.cf_error);
824			if (address) {
825				free(address);
826				address = NULL;
827			}
828			goto error;
829		}
830		(void) memcpy(address->buf, remote.buf, remote.len);
831		(void) memcpy(&address->buf[sizeof (short)], &port,
832								sizeof (short));
833		address->len = address->maxlen = remote.len;
834		goto done;
835	}
836#endif
837
838try_rpcbind:
839	/*
840	 * Check if rpcbind is up.  This prevents needless delays when
841	 * accessing applications such as the keyserver while booting
842	 * disklessly.
843	 */
844	if (check_rpcbind && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
845		if (!__rpcbind_is_up()) {
846			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
847			rpc_createerr.cf_error.re_errno = 0;
848			rpc_createerr.cf_error.re_terrno = 0;
849			goto error;
850		}
851		check_rpcbind = FALSE;
852	}
853
854	/*
855	 * Now we try version 4 and then 3.
856	 * We also send the remote system the address we used to
857	 * contact it in case it can help to connect back with us
858	 */
859	parms.r_prog = program;
860	parms.r_vers = version;
861	parms.r_owner = (char *)&nullstring[0];	/* not needed; */
862	/* just for xdring */
863	parms.r_netid = nconf->nc_netid; /* not really needed */
864
865	/*
866	 * If a COTS transport is being used, try getting address via CLTS
867	 * transport.  This works only with version 4.
868	 */
869	if (nconf->nc_semantics == NC_TPI_COTS_ORD ||
870	    nconf->nc_semantics == NC_TPI_COTS) {
871		void *handle;
872		struct netconfig *nconf_clts;
873		rpcb_entry_list_ptr relp = NULL;
874
875		if (client == NULL) {
876			/* This did not go through the above PORTMAP/TCP code */
877			if ((handle = __rpc_setconf("datagram_v")) != NULL) {
878				while ((nconf_clts = __rpc_getconf(handle))
879				    != NULL) {
880					if (strcmp(nconf_clts->nc_protofmly,
881					    nconf->nc_protofmly) != 0) {
882						continue;
883					}
884					client = _getclnthandle_timed(host,
885						nconf_clts, &parms.r_addr,
886						tp);
887					break;
888				}
889				__rpc_endconf(handle);
890			}
891			if (client == NULL)
892				goto regular_rpcbind;	/* Go the regular way */
893		} else {
894			/* This is a UDP PORTMAP handle.  Change to version 4 */
895			vers = RPCBVERS4;
896			CLNT_CONTROL(client, CLSET_VERS, (char *)&vers);
897		}
898		/*
899		 * We also send the remote system the address we used to
900		 * contact it in case it can help it connect back with us
901		 */
902		if (parms.r_addr == NULL) {
903			parms.r_addr = strdup(""); /* for XDRing */
904			if (parms.r_addr == NULL) {
905				syslog(LOG_ERR, "__rpcb_findaddr_timed: "
906					"strdup failed.");
907				rpc_createerr.cf_stat = RPC_SYSTEMERROR;
908				address = NULL;
909				goto error;
910			}
911		}
912
913		CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
914
915		clnt_st = CLNT_CALL(client, RPCBPROC_GETADDRLIST,
916				    (xdrproc_t)xdr_rpcb, (char *)&parms,
917				    (xdrproc_t)xdr_rpcb_entry_list_ptr,
918				    (char *)&relp, *tp);
919		if (clnt_st == RPC_SUCCESS) {
920			if (address = got_entry(relp, nconf)) {
921				xdr_free((xdrproc_t)xdr_rpcb_entry_list_ptr,
922					(char *)&relp);
923				goto done;
924			}
925			/* Entry not found for this transport */
926			xdr_free((xdrproc_t)xdr_rpcb_entry_list_ptr,
927				    (char *)&relp);
928			/*
929			 * XXX: should have perhaps returned with error but
930			 * since the remote machine might not always be able
931			 * to send the address on all transports, we try the
932			 * regular way with regular_rpcbind
933			 */
934			goto regular_rpcbind;
935		} else if ((clnt_st == RPC_PROGVERSMISMATCH) ||
936			    (clnt_st == RPC_PROGUNAVAIL)) {
937			start_vers = RPCBVERS;	/* Try version 3 now */
938			goto regular_rpcbind; /* Try different versions */
939		} else {
940			rpc_createerr.cf_stat = RPC_PMAPFAILURE;
941			clnt_geterr(client, &rpc_createerr.cf_error);
942			goto error;
943		}
944	}
945
946regular_rpcbind:
947
948	/* Now the same transport is to be used to get the address */
949	if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
950	    (nconf->nc_semantics == NC_TPI_COTS))) {
951		/* A CLTS type of client - destroy it */
952		CLNT_DESTROY(client);
953		client = NULL;
954		free(parms.r_addr);
955		parms.r_addr = NULL;
956	}
957
958	if (client == NULL) {
959		client = _getclnthandle_timed(host, nconf, &parms.r_addr, tp);
960		if (client == NULL) {
961			address = NULL;
962			goto error;
963		}
964	}
965	if (parms.r_addr == NULL) {
966		parms.r_addr = strdup("");	/* for XDRing */
967		if (parms.r_addr == NULL) {
968			syslog(LOG_ERR, "__rpcb_findaddr_timed: "
969				"strdup failed.");
970			address = NULL;
971			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
972			goto error;
973		}
974	}
975
976	/* First try from start_vers and then version 3 (RPCBVERS) */
977
978	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
979	for (vers = start_vers;  vers >= RPCBVERS; vers--) {
980		/* Set the version */
981		CLNT_CONTROL(client, CLSET_VERS, (char *)&vers);
982		clnt_st = CLNT_CALL(client, RPCBPROC_GETADDR,
983				    (xdrproc_t)xdr_rpcb, (char *)&parms,
984				    (xdrproc_t)xdr_wrapstring,
985				    (char *)&ua, *tp);
986		if (clnt_st == RPC_SUCCESS) {
987			if ((ua == NULL) || (ua[0] == NULL)) {
988				if (ua != NULL)
989					xdr_free(xdr_wrapstring, (char *)&ua);
990
991				/* address unknown */
992				rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
993				goto error;
994			}
995			address = uaddr2taddr(nconf, ua);
996#ifdef ND_DEBUG
997			fprintf(stderr, "\tRemote address is [%s]\n", ua);
998			if (!address)
999				fprintf(stderr,
1000					"\tCouldn't resolve remote address!\n");
1001#endif
1002			xdr_free((xdrproc_t)xdr_wrapstring, (char *)&ua);
1003
1004			if (!address) {
1005				/* We don't know about your universal address */
1006				rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
1007				goto error;
1008			}
1009			goto done;
1010		}
1011		if (clnt_st == RPC_PROGVERSMISMATCH) {
1012			struct rpc_err rpcerr;
1013
1014			clnt_geterr(client, &rpcerr);
1015			if (rpcerr.re_vers.low > RPCBVERS4)
1016				goto error;  /* a new version, can't handle */
1017		} else if (clnt_st != RPC_PROGUNAVAIL) {
1018			/* Cant handle this error */
1019			goto error;
1020		}
1021	}
1022
1023	if ((address == NULL) || (address->len == 0)) {
1024		rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
1025		clnt_geterr(client, &rpc_createerr.cf_error);
1026	}
1027
1028error:
1029	if (client) {
1030		CLNT_DESTROY(client);
1031		client = NULL;
1032	}
1033done:
1034	if (nconf->nc_semantics != NC_TPI_CLTS) {
1035		/* This client is the connectionless one */
1036		if (client) {
1037			CLNT_DESTROY(client);
1038			client = NULL;
1039		}
1040	}
1041	if (clpp) {
1042		*clpp = client;
1043	} else if (client) {
1044		CLNT_DESTROY(client);
1045	}
1046	if (parms.r_addr)
1047		free(parms.r_addr);
1048	return (address);
1049}
1050
1051
1052/*
1053 * Find the mapped address for program, version.
1054 * Calls the rpcbind service remotely to do the lookup.
1055 * Uses the transport specified in nconf.
1056 * Returns FALSE (0) if no map exists, else returns 1.
1057 *
1058 * Assuming that the address is all properly allocated
1059 */
1060int
1061rpcb_getaddr(const rpcprog_t program, const rpcvers_t version,
1062	const struct netconfig *nconf, struct netbuf *address, const char *host)
1063{
1064	struct netbuf *na;
1065
1066	if ((na = __rpcb_findaddr_timed(program, version,
1067	    (struct netconfig *)nconf, (char *)host, NULL, NULL)) == NULL)
1068		return (FALSE);
1069
1070	if (na->len > address->maxlen) {
1071		/* Too long address */
1072		netdir_free((char *)na, ND_ADDR);
1073		rpc_createerr.cf_stat = RPC_FAILED;
1074		return (FALSE);
1075	}
1076	(void) memcpy(address->buf, na->buf, (int)na->len);
1077	address->len = na->len;
1078	netdir_free((char *)na, ND_ADDR);
1079	return (TRUE);
1080}
1081
1082/*
1083 * Get a copy of the current maps.
1084 * Calls the rpcbind service remotely to get the maps.
1085 *
1086 * It returns only a list of the services
1087 * It returns NULL on failure.
1088 */
1089rpcblist *
1090rpcb_getmaps(const struct netconfig *nconf, const char *host)
1091{
1092	rpcblist_ptr head = NULL;
1093	CLIENT *client;
1094	enum clnt_stat clnt_st;
1095	int vers = 0;
1096
1097	client = getclnthandle((char *)host,
1098			(struct netconfig *)nconf, NULL);
1099	if (client == NULL)
1100		return (NULL);
1101
1102	clnt_st = CLNT_CALL(client, RPCBPROC_DUMP,
1103			(xdrproc_t)xdr_void, NULL,
1104			(xdrproc_t)xdr_rpcblist_ptr,
1105			(char *)&head, tottimeout);
1106	if (clnt_st == RPC_SUCCESS)
1107		goto done;
1108
1109	if ((clnt_st != RPC_PROGVERSMISMATCH) &&
1110		    (clnt_st != RPC_PROGUNAVAIL)) {
1111		rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1112		clnt_geterr(client, &rpc_createerr.cf_error);
1113		goto done;
1114	}
1115
1116	/* fall back to earlier version */
1117	CLNT_CONTROL(client, CLGET_VERS, (char *)&vers);
1118	if (vers == RPCBVERS4) {
1119		vers = RPCBVERS;
1120		CLNT_CONTROL(client, CLSET_VERS, (char *)&vers);
1121		if (CLNT_CALL(client, RPCBPROC_DUMP,
1122			(xdrproc_t)xdr_void,
1123			NULL, (xdrproc_t)xdr_rpcblist_ptr,
1124			(char *)&head, tottimeout) == RPC_SUCCESS)
1125				goto done;
1126	}
1127	rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1128	clnt_geterr(client, &rpc_createerr.cf_error);
1129
1130done:
1131	CLNT_DESTROY(client);
1132	return (head);
1133}
1134
1135/*
1136 * rpcbinder remote-call-service interface.
1137 * This routine is used to call the rpcbind remote call service
1138 * which will look up a service program in the address maps, and then
1139 * remotely call that routine with the given parameters. This allows
1140 * programs to do a lookup and call in one step.
1141 */
1142enum clnt_stat
1143rpcb_rmtcall(const struct netconfig *nconf, const char *host,
1144	const rpcprog_t prog, const rpcvers_t vers, const rpcproc_t proc,
1145	const xdrproc_t xdrargs, const caddr_t argsp, const xdrproc_t xdrres,
1146	const caddr_t resp, const struct timeval tout, struct netbuf *addr_ptr)
1147{
1148	CLIENT *client;
1149	enum clnt_stat stat;
1150	struct r_rpcb_rmtcallargs a;
1151	struct r_rpcb_rmtcallres r;
1152	int rpcb_vers;
1153
1154	client = getclnthandle((char *)host, (struct netconfig *)nconf, NULL);
1155	if (client == NULL)
1156		return (RPC_FAILED);
1157	CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rmttimeout);
1158	a.prog = prog;
1159	a.vers = vers;
1160	a.proc = proc;
1161	a.args.args_val = argsp;
1162	a.xdr_args = xdrargs;
1163	r.addr = NULL;
1164	r.results.results_val = resp;
1165	r.xdr_res = xdrres;
1166
1167	for (rpcb_vers = RPCBVERS4; rpcb_vers >= RPCBVERS; rpcb_vers--) {
1168		CLNT_CONTROL(client, CLSET_VERS, (char *)&rpcb_vers);
1169		stat = CLNT_CALL(client, RPCBPROC_CALLIT,
1170			(xdrproc_t)xdr_rpcb_rmtcallargs, (char *)&a,
1171			(xdrproc_t)xdr_rpcb_rmtcallres, (char *)&r, tout);
1172		if ((stat == RPC_SUCCESS) && (addr_ptr != NULL)) {
1173			struct netbuf *na;
1174
1175			na = uaddr2taddr((struct netconfig *)nconf, r.addr);
1176			if (!na) {
1177				stat = RPC_N2AXLATEFAILURE;
1178				((struct netbuf *)addr_ptr)->len = 0;
1179				goto error;
1180			}
1181			if (na->len > addr_ptr->maxlen) {
1182				/* Too long address */
1183				stat = RPC_FAILED; /* XXX A better error no */
1184				netdir_free((char *)na, ND_ADDR);
1185				((struct netbuf *)addr_ptr)->len = 0;
1186				goto error;
1187			}
1188			(void) memcpy(addr_ptr->buf, na->buf, (int)na->len);
1189			((struct netbuf *)addr_ptr)->len = na->len;
1190			netdir_free((char *)na, ND_ADDR);
1191			break;
1192		}
1193		if ((stat != RPC_PROGVERSMISMATCH) &&
1194			    (stat != RPC_PROGUNAVAIL))
1195			goto error;
1196	}
1197error:
1198	CLNT_DESTROY(client);
1199	if (r.addr)
1200		xdr_free((xdrproc_t)xdr_wrapstring, (char *)&r.addr);
1201	return (stat);
1202}
1203
1204/*
1205 * Gets the time on the remote host.
1206 * Returns 1 if succeeds else 0.
1207 */
1208bool_t
1209rpcb_gettime(const char *host, time_t *timep)
1210{
1211	CLIENT *client = NULL;
1212	void *handle;
1213	struct netconfig *nconf;
1214	int vers;
1215	enum clnt_stat st;
1216
1217	if ((host == NULL) || (host[0] == NULL)) {
1218		(void) time(timep);
1219		return (TRUE);
1220	}
1221
1222	if ((handle = __rpc_setconf("netpath")) == NULL) {
1223		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1224		return (FALSE);
1225	}
1226	rpc_createerr.cf_stat = RPC_SUCCESS;
1227	while (client == NULL) {
1228		if ((nconf = __rpc_getconf(handle)) == NULL) {
1229			if (rpc_createerr.cf_stat == RPC_SUCCESS)
1230				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1231			break;
1232		}
1233		client = getclnthandle((char *)host, nconf, NULL);
1234		if (client)
1235			break;
1236	}
1237	__rpc_endconf(handle);
1238	if (client == NULL)
1239		return (FALSE);
1240
1241	st = CLNT_CALL(client, RPCBPROC_GETTIME,
1242		(xdrproc_t)xdr_void, NULL,
1243		(xdrproc_t)xdr_time_t, (char *)timep, tottimeout);
1244
1245	if ((st == RPC_PROGVERSMISMATCH) || (st == RPC_PROGUNAVAIL)) {
1246		CLNT_CONTROL(client, CLGET_VERS, (char *)&vers);
1247		if (vers == RPCBVERS4) {
1248			/* fall back to earlier version */
1249			vers = RPCBVERS;
1250			CLNT_CONTROL(client, CLSET_VERS, (char *)&vers);
1251			st = CLNT_CALL(client, RPCBPROC_GETTIME,
1252				(xdrproc_t)xdr_void, NULL,
1253				(xdrproc_t)xdr_time_t, (char *)timep,
1254				tottimeout);
1255		}
1256	}
1257	CLNT_DESTROY(client);
1258	return (st == RPC_SUCCESS? TRUE : FALSE);
1259}
1260
1261/*
1262 * Converts taddr to universal address.  This routine should never
1263 * really be called because local n2a libraries are always provided.
1264 */
1265char *
1266rpcb_taddr2uaddr(struct netconfig *nconf, struct netbuf *taddr)
1267{
1268	CLIENT *client;
1269	char *uaddr = NULL;
1270
1271	/* parameter checking */
1272	if (nconf == NULL) {
1273		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1274		return (NULL);
1275	}
1276	if (taddr == NULL) {
1277		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1278		return (NULL);
1279	}
1280	client = local_rpcb();
1281	if (!client)
1282		return (NULL);
1283
1284	CLNT_CALL(client, RPCBPROC_TADDR2UADDR, (xdrproc_t)xdr_netbuf,
1285		(char *)taddr, (xdrproc_t)xdr_wrapstring, (char *)&uaddr,
1286		tottimeout);
1287	CLNT_DESTROY(client);
1288	return (uaddr);
1289}
1290
1291/*
1292 * Converts universal address to netbuf.  This routine should never
1293 * really be called because local n2a libraries are always provided.
1294 */
1295struct netbuf *
1296rpcb_uaddr2taddr(struct netconfig *nconf, char *uaddr)
1297{
1298	CLIENT *client;
1299	struct netbuf *taddr;
1300
1301	/* parameter checking */
1302	if (nconf == NULL) {
1303		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1304		return (NULL);
1305	}
1306	if (uaddr == NULL) {
1307		rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1308		return (NULL);
1309	}
1310	client = local_rpcb();
1311	if (!client)
1312		return (NULL);
1313
1314	taddr = calloc(1, sizeof (struct netbuf));
1315	if (taddr == NULL) {
1316		CLNT_DESTROY(client);
1317		return (NULL);
1318	}
1319
1320	if (CLNT_CALL(client, RPCBPROC_UADDR2TADDR, (xdrproc_t)xdr_wrapstring,
1321		(char *)&uaddr, (xdrproc_t)xdr_netbuf, (char *)taddr,
1322		tottimeout) != RPC_SUCCESS) {
1323		free(taddr);
1324		taddr = NULL;
1325	}
1326	CLNT_DESTROY(client);
1327	return (taddr);
1328}
1329