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