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