xref: /illumos-gate/usr/src/lib/libnsl/rpc/rpcb_clnt.c (revision d583b39b)
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 
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
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 *
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 #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 
183 static void
184 delete_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 
210 static void
211 add_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;
279 memerr1:
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);
289 memerr:
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  */
297 static CLIENT *
298 getclnthandle(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  */
309 static 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  */
444 static CLIENT *
445 local_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  */
502 bool_t
503 rpcb_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  */
555 bool_t
556 rpcb_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  */
588 static struct netbuf *
589 got_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  */
619 bool_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  */
706 struct 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 
838 try_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 
946 regular_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 
1028 error:
1029 	if (client) {
1030 		CLNT_DESTROY(client);
1031 		client = NULL;
1032 	}
1033 done:
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  */
1060 int
1061 rpcb_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  */
1089 rpcblist *
1090 rpcb_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 
1130 done:
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  */
1142 enum clnt_stat
1143 rpcb_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 	}
1197 error:
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  */
1208 bool_t
1209 rpcb_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  */
1265 char *
1266 rpcb_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  */
1295 struct netbuf *
1296 rpcb_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