import less(1)
[unleashed/tickless.git] / usr / src / lib / libc / port / nsl / rpcb_clnt.c
blob1980c9b1c1400cb2576db7b3e7a0ff52ff7fddfa
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
24 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
28 * Copyright 2014 Gary Mills
29 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
30 * Use is subject to license terms.
33 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
34 /* All Rights Reserved */
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.
42 * interface to rpcbind rpc service.
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 #include <netinet/in.h> /* FOR IPPROTO_TCP/UDP definitions */
56 #include <rpc/pmap_prot.h>
57 #include <errno.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
62 static struct timeval tottimeout = { 60, 0 };
63 static const struct timeval rmttimeout = { 3, 0 };
64 static struct timeval rpcbrmttime = { 15, 0 };
66 extern bool_t xdr_wrapstring(XDR *, char **);
68 static const char nullstring[] = "\000";
70 extern CLIENT *_clnt_tli_create_timed(int, const struct netconfig *,
71 struct netbuf *, rpcprog_t, rpcvers_t, uint_t, uint_t,
72 const struct timeval *);
74 static CLIENT *_getclnthandle_timed(char *, struct netconfig *, char **,
75 struct timeval *);
79 * The life time of a cached entry should not exceed 5 minutes
80 * since automountd attempts an unmount every 5 minutes.
81 * It is arbitrarily set a little lower (3 min = 180 sec)
82 * to reduce the time during which an entry is stale.
84 #define CACHE_TTL 180
85 #define CACHESIZE 6
87 struct address_cache {
88 char *ac_host;
89 char *ac_netid;
90 char *ac_uaddr;
91 struct netbuf *ac_taddr;
92 struct address_cache *ac_next;
93 time_t ac_maxtime;
96 static struct address_cache *front;
97 static int cachesize;
99 extern int lowvers;
100 extern int authdes_cachesz;
102 * This routine adjusts the timeout used for calls to the remote rpcbind.
103 * Also, this routine can be used to set the use of portmapper version 2
104 * only when doing rpc_broadcasts
105 * These are private routines that may not be provided in future releases.
107 bool_t
108 __rpc_control(int request, void *info)
110 switch (request) {
111 case CLCR_GET_RPCB_TIMEOUT:
112 *(struct timeval *)info = tottimeout;
113 break;
114 case CLCR_SET_RPCB_TIMEOUT:
115 tottimeout = *(struct timeval *)info;
116 break;
117 case CLCR_GET_LOWVERS:
118 *(int *)info = lowvers;
119 break;
120 case CLCR_SET_LOWVERS:
121 lowvers = *(int *)info;
122 break;
123 case CLCR_GET_RPCB_RMTTIME:
124 *(struct timeval *)info = rpcbrmttime;
125 break;
126 case CLCR_SET_RPCB_RMTTIME:
127 rpcbrmttime = *(struct timeval *)info;
128 break;
129 case CLCR_GET_CRED_CACHE_SZ:
130 *(int *)info = authdes_cachesz;
131 break;
132 case CLCR_SET_CRED_CACHE_SZ:
133 authdes_cachesz = *(int *)info;
134 break;
135 default:
136 return (FALSE);
138 return (TRUE);
142 * It might seem that a reader/writer lock would be more reasonable here.
143 * However because getclnthandle(), the only user of the cache functions,
144 * may do a delete_cache() operation if a check_cache() fails to return an
145 * address useful to clnt_tli_create(), we may as well use a mutex.
148 * As it turns out, if the cache lock is *not* a reader/writer lock, we will
149 * block all clnt_create's if we are trying to connect to a host that's down,
150 * since the lock will be held all during that time.
152 extern rwlock_t rpcbaddr_cache_lock;
155 * The routines check_cache(), add_cache(), delete_cache() manage the
156 * cache of rpcbind addresses for (host, netid).
159 static struct address_cache *
160 check_cache(char *host, char *netid)
162 struct address_cache *cptr;
164 /* READ LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
166 assert(RW_READ_HELD(&rpcbaddr_cache_lock));
167 for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
168 if ((strcmp(cptr->ac_host, host) == 0) &&
169 (strcmp(cptr->ac_netid, netid) == 0) &&
170 (time(NULL) <= cptr->ac_maxtime)) {
171 return (cptr);
174 return (NULL);
177 static void
178 delete_cache(struct netbuf *addr)
180 struct address_cache *cptr, *prevptr = NULL;
182 /* WRITE LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
183 assert(RW_WRITE_HELD(&rpcbaddr_cache_lock));
184 for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
185 if (!memcmp(cptr->ac_taddr->buf, addr->buf, addr->len)) {
186 free(cptr->ac_host);
187 free(cptr->ac_netid);
188 free(cptr->ac_taddr->buf);
189 free(cptr->ac_taddr);
190 free(cptr->ac_uaddr);
191 if (prevptr)
192 prevptr->ac_next = cptr->ac_next;
193 else
194 front = cptr->ac_next;
195 free(cptr);
196 cachesize--;
197 break;
199 prevptr = cptr;
203 static void
204 add_cache(char *host, char *netid, struct netbuf *taddr, char *uaddr)
206 struct address_cache *ad_cache, *cptr, *prevptr;
208 ad_cache = malloc(sizeof (struct address_cache));
209 if (!ad_cache) {
210 goto memerr;
212 ad_cache->ac_maxtime = time(NULL) + CACHE_TTL;
213 ad_cache->ac_host = strdup(host);
214 ad_cache->ac_netid = strdup(netid);
215 ad_cache->ac_uaddr = uaddr ? strdup(uaddr) : NULL;
216 ad_cache->ac_taddr = malloc(sizeof (struct netbuf));
217 if (!ad_cache->ac_host || !ad_cache->ac_netid || !ad_cache->ac_taddr ||
218 (uaddr && !ad_cache->ac_uaddr)) {
219 goto memerr1;
222 ad_cache->ac_taddr->len = ad_cache->ac_taddr->maxlen = taddr->len;
223 ad_cache->ac_taddr->buf = malloc(taddr->len);
224 if (ad_cache->ac_taddr->buf == NULL) {
225 goto memerr1;
228 (void) memcpy(ad_cache->ac_taddr->buf, taddr->buf, taddr->len);
230 /* VARIABLES PROTECTED BY rpcbaddr_cache_lock: cptr */
232 (void) rw_wrlock(&rpcbaddr_cache_lock);
233 if (cachesize < CACHESIZE) {
234 ad_cache->ac_next = front;
235 front = ad_cache;
236 cachesize++;
237 } else {
238 /* Free the last entry */
239 cptr = front;
240 prevptr = NULL;
241 while (cptr->ac_next) {
242 prevptr = cptr;
243 cptr = cptr->ac_next;
246 free(cptr->ac_host);
247 free(cptr->ac_netid);
248 free(cptr->ac_taddr->buf);
249 free(cptr->ac_taddr);
250 free(cptr->ac_uaddr);
252 if (prevptr) {
253 prevptr->ac_next = NULL;
254 ad_cache->ac_next = front;
255 front = ad_cache;
256 } else {
257 front = ad_cache;
258 ad_cache->ac_next = NULL;
260 free(cptr);
262 (void) rw_unlock(&rpcbaddr_cache_lock);
263 return;
264 memerr1:
265 free(ad_cache->ac_host);
266 free(ad_cache->ac_netid);
267 free(ad_cache->ac_uaddr);
268 free(ad_cache->ac_taddr);
269 free(ad_cache);
270 memerr:
271 syslog(LOG_ERR, "add_cache : out of memory.");
275 * This routine will return a client handle that is connected to the
276 * rpcbind. Returns NULL on error and free's everything.
278 static CLIENT *
279 getclnthandle(char *host, struct netconfig *nconf, char **targaddr)
281 return (_getclnthandle_timed(host, nconf, targaddr, NULL));
285 * Same as getclnthandle() except it takes an extra timeout argument.
286 * This is for bug 4049792: clnt_create_timed does not timeout.
288 * If tp is NULL, use default timeout to get a client handle.
290 static CLIENT *
291 _getclnthandle_timed(char *host, struct netconfig *nconf, char **targaddr,
292 struct timeval *tp)
294 CLIENT *client = NULL;
295 struct netbuf *addr;
296 struct netbuf addr_to_delete;
297 struct nd_addrlist *nas;
298 struct nd_hostserv rpcbind_hs;
299 struct address_cache *ad_cache;
300 char *tmpaddr;
301 int neterr;
302 int j;
304 /* VARIABLES PROTECTED BY rpcbaddr_cache_lock: ad_cache */
306 /* Get the address of the rpcbind. Check cache first */
307 addr_to_delete.len = 0;
308 (void) rw_rdlock(&rpcbaddr_cache_lock);
309 ad_cache = check_cache(host, nconf->nc_netid);
310 if (ad_cache != NULL) {
311 addr = ad_cache->ac_taddr;
312 client = _clnt_tli_create_timed(RPC_ANYFD, nconf, addr,
313 RPCBPROG, RPCBVERS4, 0, 0, tp);
314 if (client != NULL) {
315 if (targaddr) {
317 * case where a client handle is created
318 * without a targaddr and the handle is
319 * requested with a targaddr
321 if (ad_cache->ac_uaddr != NULL) {
322 *targaddr = strdup(ad_cache->ac_uaddr);
323 if (*targaddr == NULL) {
324 syslog(LOG_ERR,
325 "_getclnthandle_timed: strdup "
326 "failed.");
327 rpc_createerr.cf_stat =
328 RPC_SYSTEMERROR;
329 (void) rw_unlock(
330 &rpcbaddr_cache_lock);
331 return (NULL);
333 } else {
334 *targaddr = NULL;
337 (void) rw_unlock(&rpcbaddr_cache_lock);
338 return (client);
340 if (rpc_createerr.cf_stat == RPC_SYSTEMERROR) {
341 (void) rw_unlock(&rpcbaddr_cache_lock);
342 return (NULL);
344 addr_to_delete.len = addr->len;
345 addr_to_delete.buf = malloc(addr->len);
346 if (addr_to_delete.buf == NULL) {
347 addr_to_delete.len = 0;
348 } else {
349 (void) memcpy(addr_to_delete.buf, addr->buf, addr->len);
352 (void) rw_unlock(&rpcbaddr_cache_lock);
353 if (addr_to_delete.len != 0) {
355 * Assume this may be due to cache data being
356 * outdated
358 (void) rw_wrlock(&rpcbaddr_cache_lock);
359 delete_cache(&addr_to_delete);
360 (void) rw_unlock(&rpcbaddr_cache_lock);
361 free(addr_to_delete.buf);
363 rpcbind_hs.h_host = host;
364 rpcbind_hs.h_serv = "rpcbind";
366 if ((neterr = netdir_getbyname(nconf, &rpcbind_hs, &nas)) != 0) {
367 if (neterr == ND_NOHOST)
368 rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
369 else
370 rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
371 return (NULL);
373 /* XXX nas should perhaps be cached for better performance */
375 for (j = 0; j < nas->n_cnt; j++) {
376 addr = &(nas->n_addrs[j]);
377 client = _clnt_tli_create_timed(RPC_ANYFD, nconf, addr, RPCBPROG,
378 RPCBVERS4, 0, 0, tp);
379 if (client)
380 break;
383 if (client) {
384 tmpaddr = targaddr ? taddr2uaddr(nconf, addr) : NULL;
385 add_cache(host, nconf->nc_netid, addr, tmpaddr);
386 if (targaddr) {
387 *targaddr = tmpaddr;
390 netdir_free((char *)nas, ND_ADDRLIST);
391 return (client);
395 * This routine will return a client handle that is connected to the local
396 * rpcbind. Returns NULL on error.
398 static CLIENT *
399 local_rpcb(void)
401 static struct netconfig *loopnconf;
402 extern mutex_t loopnconf_lock;
404 /* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */
405 (void) mutex_lock(&loopnconf_lock);
406 if (loopnconf == NULL) {
407 struct netconfig *nconf, *tmpnconf = NULL;
408 void *nc_handle;
410 nc_handle = setnetconfig();
411 if (nc_handle == NULL) {
412 /* fails to open netconfig file */
413 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
414 (void) mutex_unlock(&loopnconf_lock);
415 return (NULL);
417 while (nconf = getnetconfig(nc_handle)) {
418 if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
419 tmpnconf = nconf;
420 if (nconf->nc_semantics == NC_TPI_CLTS)
421 break;
424 if (tmpnconf == NULL) {
425 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
426 (void) mutex_unlock(&loopnconf_lock);
427 return (NULL);
429 loopnconf = getnetconfigent(tmpnconf->nc_netid);
430 /* loopnconf is never freed */
431 (void) endnetconfig(nc_handle);
433 (void) mutex_unlock(&loopnconf_lock);
434 return (getclnthandle(HOST_SELF_CONNECT, loopnconf, NULL));
438 * Set a mapping between program, version and address.
439 * Calls the rpcbind service to do the mapping.
441 bool_t
442 rpcb_set(const rpcprog_t program, const rpcvers_t version,
443 const struct netconfig *nconf, const struct netbuf *address)
445 CLIENT *client;
446 bool_t rslt = FALSE;
447 RPCB parms;
448 char uidbuf[32];
450 /* parameter checking */
451 if (nconf == NULL) {
452 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
453 return (FALSE);
455 if (address == NULL) {
456 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
457 return (FALSE);
459 client = local_rpcb();
460 if (!client)
461 return (FALSE);
463 parms.r_addr = taddr2uaddr((struct netconfig *)nconf,
464 (struct netbuf *)address); /* convert to universal */
465 if (!parms.r_addr) {
466 rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
467 return (FALSE); /* no universal address */
469 parms.r_prog = program;
470 parms.r_vers = version;
471 parms.r_netid = nconf->nc_netid;
473 * Though uid is not being used directly, we still send it for
474 * completeness. For non-unix platforms, perhaps some other
475 * string or an empty string can be sent.
477 (void) sprintf(uidbuf, "%d", (int)geteuid());
478 parms.r_owner = uidbuf;
480 CLNT_CALL(client, RPCBPROC_SET, (xdrproc_t)xdr_rpcb, (char *)&parms,
481 (xdrproc_t)xdr_bool, (char *)&rslt, tottimeout);
483 CLNT_DESTROY(client);
484 free(parms.r_addr);
485 return (rslt);
489 * Remove the mapping between program, version and netbuf address.
490 * Calls the rpcbind service to do the un-mapping.
491 * If netbuf is NULL, unset for all the transports, otherwise unset
492 * only for the given transport.
494 bool_t
495 rpcb_unset(const rpcprog_t program, const rpcvers_t version,
496 const struct netconfig *nconf)
498 CLIENT *client;
499 bool_t rslt = FALSE;
500 RPCB parms;
501 char uidbuf[32];
503 client = local_rpcb();
504 if (!client)
505 return (FALSE);
507 parms.r_prog = program;
508 parms.r_vers = version;
509 if (nconf)
510 parms.r_netid = nconf->nc_netid;
511 else
512 parms.r_netid = (char *)&nullstring[0]; /* unsets all */
513 parms.r_addr = (char *)&nullstring[0];
514 (void) sprintf(uidbuf, "%d", (int)geteuid());
515 parms.r_owner = uidbuf;
517 CLNT_CALL(client, RPCBPROC_UNSET, (xdrproc_t)xdr_rpcb, (char *)&parms,
518 (xdrproc_t)xdr_bool, (char *)&rslt, tottimeout);
520 CLNT_DESTROY(client);
521 return (rslt);
525 * From the merged list, find the appropriate entry
527 static struct netbuf *
528 got_entry(rpcb_entry_list_ptr relp, struct netconfig *nconf)
530 struct netbuf *na = NULL;
531 rpcb_entry_list_ptr sp;
532 rpcb_entry *rmap;
534 for (sp = relp; sp != NULL; sp = sp->rpcb_entry_next) {
535 rmap = &sp->rpcb_entry_map;
536 if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) &&
537 (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) &&
538 (nconf->nc_semantics == rmap->r_nc_semantics) &&
539 (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != '\0')) {
540 na = uaddr2taddr(nconf, rmap->r_maddr);
541 break;
544 return (na);
548 * Quick check to see if rpcbind is up. Tries to connect over
549 * local transport.
551 bool_t
552 __rpcbind_is_up(void)
554 struct netbuf *addr;
555 int fd;
556 struct t_call *sndcall;
557 struct netconfig *netconf;
558 bool_t res;
560 if ((fd = t_open("/dev/ticotsord", O_RDWR, NULL)) == -1)
561 return (TRUE);
563 if (t_bind(fd, NULL, NULL) == -1) {
564 (void) t_close(fd);
565 return (TRUE);
568 /* LINTED pointer cast */
569 if ((sndcall = (struct t_call *)t_alloc(fd, T_CALL, 0)) == NULL) {
570 (void) t_close(fd);
571 return (TRUE);
574 if ((netconf = getnetconfigent("ticotsord")) == NULL) {
575 (void) t_free((char *)sndcall, T_CALL);
576 (void) t_close(fd);
577 return (FALSE);
579 addr = uaddr2taddr(netconf, "localhost.rpc");
580 freenetconfigent(netconf);
581 if (addr == NULL || addr->buf == NULL) {
582 free(addr);
583 (void) t_free((char *)sndcall, T_CALL);
584 (void) t_close(fd);
585 return (FALSE);
587 sndcall->addr.maxlen = addr->maxlen;
588 sndcall->addr.len = addr->len;
589 sndcall->addr.buf = addr->buf;
591 if (t_connect(fd, sndcall, NULL) == -1)
592 res = FALSE;
593 else
594 res = TRUE;
596 sndcall->addr.maxlen = sndcall->addr.len = 0;
597 sndcall->addr.buf = NULL;
598 (void) t_free((char *)sndcall, T_CALL);
599 free(addr->buf);
600 free(addr);
601 (void) t_close(fd);
603 return (res);
608 * An internal function which optimizes rpcb_getaddr function. It returns
609 * the universal address of the remote service or NULL. It also optionally
610 * returns the client handle that it uses to contact the remote rpcbind.
611 * The caller will re-purpose the client handle to contact the remote service.
613 * The algorithm used: First try version 4. Then try version 3 (svr4).
614 * Finally, if the transport is TCP or UDP, try version 2 (portmap).
615 * Version 4 is now available with all current systems on the network.
616 * With this algorithm, we get performance as well as a plan for
617 * obsoleting version 2.
619 * XXX: Due to some problems with t_connect(), we do not reuse the same client
620 * handle for COTS cases and hence in these cases we do not return the
621 * client handle. This code will change if t_connect() ever
622 * starts working properly. Also look under clnt_vc.c.
624 struct netbuf *
625 __rpcb_findaddr_timed(rpcprog_t program, rpcvers_t version,
626 struct netconfig *nconf, char *host, CLIENT **clpp, struct timeval *tp)
628 static bool_t check_rpcbind = TRUE;
629 CLIENT *client = NULL;
630 RPCB parms;
631 enum clnt_stat clnt_st;
632 char *ua = NULL;
633 uint_t vers;
634 struct netbuf *address = NULL;
635 void *handle;
636 rpcb_entry_list_ptr relp = NULL;
637 bool_t tmp_client = FALSE;
639 /* parameter checking */
640 if (nconf == NULL) {
641 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
643 * Setting rpc_createerr.cf_stat is sufficient.
644 * No details in rpc_createerr.cf_error needed.
646 return (NULL);
649 parms.r_addr = NULL;
652 * Use default total timeout if no timeout is specified.
654 if (tp == NULL)
655 tp = &tottimeout;
658 * Check if rpcbind is up. This prevents needless delays when
659 * accessing applications such as the keyserver while booting
660 * disklessly.
662 if (check_rpcbind && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
663 if (!__rpcbind_is_up()) {
664 rpc_createerr.cf_stat = RPC_PMAPFAILURE;
665 rpc_createerr.cf_error.re_errno = 0;
666 rpc_createerr.cf_error.re_terrno = 0;
667 goto error;
669 check_rpcbind = FALSE;
673 * First try version 4.
675 parms.r_prog = program;
676 parms.r_vers = version;
677 parms.r_owner = (char *)&nullstring[0]; /* not needed; */
678 /* just for xdring */
679 parms.r_netid = nconf->nc_netid; /* not really needed */
682 * If a COTS transport is being used, try getting address via CLTS
683 * transport. This works only with version 4.
685 if (nconf->nc_semantics == NC_TPI_COTS_ORD ||
686 nconf->nc_semantics == NC_TPI_COTS) {
687 tmp_client = TRUE;
688 if ((handle = __rpc_setconf("datagram_v")) != NULL) {
689 struct netconfig *nconf_clts;
691 while ((nconf_clts = __rpc_getconf(handle)) != NULL) {
692 if (strcmp(nconf_clts->nc_protofmly,
693 nconf->nc_protofmly) != 0) {
694 continue;
697 * Sets rpc_createerr.cf_error members
698 * on failure
700 client = _getclnthandle_timed(host, nconf_clts,
701 &parms.r_addr, tp);
702 break;
704 __rpc_endconf(handle);
706 } else {
707 /* Sets rpc_createerr.cf_error members on failure */
708 client = _getclnthandle_timed(host, nconf, &parms.r_addr, tp);
711 if (client != NULL) {
713 /* Set rpcbind version 4 */
714 vers = RPCBVERS4;
715 CLNT_CONTROL(client, CLSET_VERS, (char *)&vers);
718 * We also send the remote system the address we used to
719 * contact it in case it can help it connect back with us
721 if (parms.r_addr == NULL) {
722 parms.r_addr = strdup(""); /* for XDRing */
723 if (parms.r_addr == NULL) {
724 syslog(LOG_ERR, "__rpcb_findaddr_timed: "
725 "strdup failed.");
726 /* Construct a system error */
727 rpc_createerr.cf_error.re_errno = errno;
728 rpc_createerr.cf_error.re_terrno = 0;
729 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
730 goto error;
734 CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT,
735 (char *)&rpcbrmttime);
737 /* Sets error structure members in client handle */
738 clnt_st = CLNT_CALL(client, RPCBPROC_GETADDRLIST,
739 (xdrproc_t)xdr_rpcb, (char *)&parms,
740 (xdrproc_t)xdr_rpcb_entry_list_ptr, (char *)&relp, *tp);
742 switch (clnt_st) {
743 case RPC_SUCCESS: /* Call succeeded */
744 address = got_entry(relp, nconf);
745 xdr_free((xdrproc_t)xdr_rpcb_entry_list_ptr,
746 (char *)&relp);
747 if (address != NULL) {
748 /* Program number and version number matched */
749 goto done;
751 /* Program and version not found for this transport */
753 * XXX: should have returned with RPC_PROGUNAVAIL
754 * or perhaps RPC_PROGNOTREGISTERED error but
755 * since the remote machine might not always be able
756 * to send the address on all transports, we try the
757 * regular way with version 3, then 2
759 /* Try the next version */
760 break;
761 case RPC_PROGVERSMISMATCH: /* RPC protocol mismatch */
762 clnt_geterr(client, &rpc_createerr.cf_error);
763 if (rpc_createerr.cf_error.re_vers.low > vers) {
764 rpc_createerr.cf_stat = clnt_st;
765 goto error; /* a new version, can't handle */
767 /* Try the next version */
768 break;
769 case RPC_PROCUNAVAIL: /* Procedure unavailable */
770 case RPC_PROGUNAVAIL: /* Program not available */
771 case RPC_TIMEDOUT: /* Call timed out */
772 /* Try the next version */
773 break;
774 default:
775 clnt_geterr(client, &rpc_createerr.cf_error);
776 rpc_createerr.cf_stat = RPC_PMAPFAILURE;
777 goto error;
780 } else {
782 /* No client */
783 tmp_client = FALSE;
785 } /* End of version 4 */
787 /* Destroy a temporary client */
788 if (client != NULL && tmp_client) {
789 CLNT_DESTROY(client);
790 client = NULL;
791 free(parms.r_addr);
792 parms.r_addr = NULL;
794 tmp_client = FALSE;
797 * Try version 3
800 /* Now the same transport is to be used to get the address */
801 if (client == NULL) {
802 /* Sets rpc_createerr.cf_error members on failure */
803 client = _getclnthandle_timed(host, nconf, &parms.r_addr, tp);
805 address = NULL;
806 if (client != NULL) {
807 if (parms.r_addr == NULL) {
808 parms.r_addr = strdup(""); /* for XDRing */
809 if (parms.r_addr == NULL) {
810 syslog(LOG_ERR, "__rpcb_findaddr_timed: "
811 "strdup failed.");
812 /* Construct a system error */
813 rpc_createerr.cf_error.re_errno = errno;
814 rpc_createerr.cf_error.re_terrno = 0;
815 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
816 goto error;
820 CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT,
821 (char *)&rpcbrmttime);
822 vers = RPCBVERS; /* Set the version */
823 CLNT_CONTROL(client, CLSET_VERS, (char *)&vers);
825 /* Sets error structure members in client handle */
826 clnt_st = CLNT_CALL(client, RPCBPROC_GETADDR,
827 (xdrproc_t)xdr_rpcb, (char *)&parms,
828 (xdrproc_t)xdr_wrapstring, (char *)&ua, *tp);
830 switch (clnt_st) {
831 case RPC_SUCCESS: /* Call succeeded */
832 if (ua != NULL) {
833 if (ua[0] != '\0') {
834 address = uaddr2taddr(nconf, ua);
836 xdr_free((xdrproc_t)xdr_wrapstring,
837 (char *)&ua);
839 if (address != NULL) {
840 goto done;
842 /* NULL universal address */
843 /* But client call was successful */
844 clnt_geterr(client, &rpc_createerr.cf_error);
845 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
846 goto error;
848 clnt_geterr(client, &rpc_createerr.cf_error);
849 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
850 goto error;
851 /* Try the next version */
852 break;
853 case RPC_PROGVERSMISMATCH: /* RPC protocol mismatch */
854 clnt_geterr(client, &rpc_createerr.cf_error);
855 if (rpc_createerr.cf_error.re_vers.low > vers) {
856 rpc_createerr.cf_stat = clnt_st;
857 goto error; /* a new version, can't handle */
859 /* Try the next version */
860 break;
861 case RPC_PROCUNAVAIL: /* Procedure unavailable */
862 case RPC_PROGUNAVAIL: /* Program not available */
863 case RPC_TIMEDOUT: /* Call timed out */
864 /* Try the next version */
865 break;
866 default:
867 clnt_geterr(client, &rpc_createerr.cf_error);
868 rpc_createerr.cf_stat = RPC_PMAPFAILURE;
869 goto error;
871 } /* End of version 3 */
873 * Try version 2
876 /* Try version 2 for TCP or UDP */
877 if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
878 ushort_t port = 0;
879 struct netbuf remote;
880 uint_t pmapvers = 2;
881 struct pmap pmapparms;
884 * Try UDP only - there are some portmappers out
885 * there that use UDP only.
887 if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
888 struct netconfig *newnconf;
890 if (client != NULL) {
891 CLNT_DESTROY(client);
892 client = NULL;
893 free(parms.r_addr);
894 parms.r_addr = NULL;
896 if ((handle = __rpc_setconf("udp")) == NULL) {
897 /* Construct an unknown protocol error */
898 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
899 goto error;
903 * The following to reinforce that you can
904 * only request for remote address through
905 * the same transport you are requesting.
906 * ie. requesting unversial address
907 * of IPv4 has to be carried through IPv4.
908 * Can't use IPv6 to send out the request.
909 * The mergeaddr in rpcbind can't handle
910 * this.
912 for (;;) {
913 if ((newnconf = __rpc_getconf(handle))
914 == NULL) {
915 __rpc_endconf(handle);
917 * Construct an unknown protocol
918 * error
920 rpc_createerr.cf_stat =
921 RPC_UNKNOWNPROTO;
922 goto error;
925 * here check the protocol family to
926 * be consistent with the request one
928 if (strcmp(newnconf->nc_protofmly,
929 nconf->nc_protofmly) == 0)
930 break;
933 /* Sets rpc_createerr.cf_error members on failure */
934 client = _getclnthandle_timed(host, newnconf,
935 &parms.r_addr, tp);
936 __rpc_endconf(handle);
937 tmp_client = TRUE;
939 if (client == NULL) {
941 * rpc_createerr. cf_error members were set by
942 * creation failure
944 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
945 tmp_client = FALSE;
946 goto error;
950 * Set version and retry timeout.
952 CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
953 CLNT_CONTROL(client, CLSET_VERS, (char *)&pmapvers);
955 pmapparms.pm_prog = program;
956 pmapparms.pm_vers = version;
957 pmapparms.pm_prot = (strcmp(nconf->nc_proto, NC_TCP) != 0) ?
958 IPPROTO_UDP : IPPROTO_TCP;
959 pmapparms.pm_port = 0; /* not needed */
961 /* Sets error structure members in client handle */
962 clnt_st = CLNT_CALL(client, PMAPPROC_GETPORT,
963 (xdrproc_t)xdr_pmap, (caddr_t)&pmapparms,
964 (xdrproc_t)xdr_u_short, (caddr_t)&port, *tp);
966 if (clnt_st != RPC_SUCCESS) {
967 clnt_geterr(client, &rpc_createerr.cf_error);
968 rpc_createerr.cf_stat = RPC_RPCBFAILURE;
969 goto error;
970 } else if (port == 0) {
971 /* Will be NULL universal address */
972 /* But client call was successful */
973 clnt_geterr(client, &rpc_createerr.cf_error);
974 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
975 goto error;
977 port = htons(port);
978 CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)&remote);
979 if (((address = malloc(sizeof (struct netbuf))) == NULL) ||
980 ((address->buf = malloc(remote.len)) == NULL)) {
981 /* Construct a system error */
982 rpc_createerr.cf_error.re_errno = errno;
983 rpc_createerr.cf_error.re_terrno = 0;
984 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
985 free(address);
986 address = NULL;
987 goto error;
989 (void) memcpy(address->buf, remote.buf, remote.len);
990 (void) memcpy(&address->buf[sizeof (short)], &port,
991 sizeof (short));
992 address->len = address->maxlen = remote.len;
993 goto done;
994 } else {
996 * This is not NC_INET.
997 * Always an error for version 2.
999 if (client != NULL && clnt_st != RPC_SUCCESS) {
1000 /* There is a client that failed */
1001 clnt_geterr(client, &rpc_createerr.cf_error);
1002 rpc_createerr.cf_stat = clnt_st;
1003 } else {
1004 /* Something else */
1005 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1007 * Setting rpc_createerr.cf_stat is sufficient.
1008 * No details in rpc_createerr.cf_error needed.
1013 error:
1014 /* Return NULL address and NULL client */
1015 address = NULL;
1016 if (client != NULL) {
1017 CLNT_DESTROY(client);
1018 client = NULL;
1021 done:
1022 /* Return an address and optional client */
1023 if (client != NULL && tmp_client) {
1024 /* This client is the temporary one */
1025 CLNT_DESTROY(client);
1026 client = NULL;
1028 if (clpp != NULL) {
1029 *clpp = client;
1030 } else if (client != NULL) {
1031 CLNT_DESTROY(client);
1033 free(parms.r_addr);
1034 return (address);
1039 * Find the mapped address for program, version.
1040 * Calls the rpcbind service remotely to do the lookup.
1041 * Uses the transport specified in nconf.
1042 * Returns FALSE (0) if no map exists, else returns 1.
1044 * Assuming that the address is all properly allocated
1047 rpcb_getaddr(const rpcprog_t program, const rpcvers_t version,
1048 const struct netconfig *nconf, struct netbuf *address, const char *host)
1050 struct netbuf *na;
1052 if ((na = __rpcb_findaddr_timed(program, version,
1053 (struct netconfig *)nconf, (char *)host, NULL, NULL)) == NULL)
1054 return (FALSE);
1056 if (na->len > address->maxlen) {
1057 /* Too long address */
1058 netdir_free((char *)na, ND_ADDR);
1059 rpc_createerr.cf_stat = RPC_FAILED;
1060 return (FALSE);
1062 (void) memcpy(address->buf, na->buf, (int)na->len);
1063 address->len = na->len;
1064 netdir_free((char *)na, ND_ADDR);
1065 return (TRUE);
1069 * Get a copy of the current maps.
1070 * Calls the rpcbind service remotely to get the maps.
1072 * It returns only a list of the services
1073 * It returns NULL on failure.
1075 rpcblist *
1076 rpcb_getmaps(const struct netconfig *nconf, const char *host)
1078 rpcblist_ptr head = NULL;
1079 CLIENT *client;
1080 enum clnt_stat clnt_st;
1081 int vers = 0;
1083 client = getclnthandle((char *)host,
1084 (struct netconfig *)nconf, NULL);
1085 if (client == NULL)
1086 return (NULL);
1088 clnt_st = CLNT_CALL(client, RPCBPROC_DUMP,
1089 (xdrproc_t)xdr_void, NULL,
1090 (xdrproc_t)xdr_rpcblist_ptr,
1091 (char *)&head, tottimeout);
1092 if (clnt_st == RPC_SUCCESS)
1093 goto done;
1095 if ((clnt_st != RPC_PROGVERSMISMATCH) &&
1096 (clnt_st != RPC_PROGUNAVAIL)) {
1097 rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1098 clnt_geterr(client, &rpc_createerr.cf_error);
1099 goto done;
1102 /* fall back to earlier version */
1103 CLNT_CONTROL(client, CLGET_VERS, (char *)&vers);
1104 if (vers == RPCBVERS4) {
1105 vers = RPCBVERS;
1106 CLNT_CONTROL(client, CLSET_VERS, (char *)&vers);
1107 if (CLNT_CALL(client, RPCBPROC_DUMP,
1108 (xdrproc_t)xdr_void,
1109 NULL, (xdrproc_t)xdr_rpcblist_ptr,
1110 (char *)&head, tottimeout) == RPC_SUCCESS)
1111 goto done;
1113 rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1114 clnt_geterr(client, &rpc_createerr.cf_error);
1116 done:
1117 CLNT_DESTROY(client);
1118 return (head);
1122 * rpcbinder remote-call-service interface.
1123 * This routine is used to call the rpcbind remote call service
1124 * which will look up a service program in the address maps, and then
1125 * remotely call that routine with the given parameters. This allows
1126 * programs to do a lookup and call in one step.
1128 enum clnt_stat
1129 rpcb_rmtcall(const struct netconfig *nconf, const char *host,
1130 const rpcprog_t prog, const rpcvers_t vers, const rpcproc_t proc,
1131 const xdrproc_t xdrargs, const caddr_t argsp, const xdrproc_t xdrres,
1132 const caddr_t resp, const struct timeval tout, struct netbuf *addr_ptr)
1134 CLIENT *client;
1135 enum clnt_stat stat;
1136 struct r_rpcb_rmtcallargs a;
1137 struct r_rpcb_rmtcallres r;
1138 int rpcb_vers;
1140 client = getclnthandle((char *)host, (struct netconfig *)nconf, NULL);
1141 if (client == NULL)
1142 return (RPC_FAILED);
1143 CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rmttimeout);
1144 a.prog = prog;
1145 a.vers = vers;
1146 a.proc = proc;
1147 a.args.args_val = argsp;
1148 a.xdr_args = xdrargs;
1149 r.addr = NULL;
1150 r.results.results_val = resp;
1151 r.xdr_res = xdrres;
1153 for (rpcb_vers = RPCBVERS4; rpcb_vers >= RPCBVERS; rpcb_vers--) {
1154 CLNT_CONTROL(client, CLSET_VERS, (char *)&rpcb_vers);
1155 stat = CLNT_CALL(client, RPCBPROC_CALLIT,
1156 (xdrproc_t)xdr_rpcb_rmtcallargs, (char *)&a,
1157 (xdrproc_t)xdr_rpcb_rmtcallres, (char *)&r, tout);
1158 if ((stat == RPC_SUCCESS) && (addr_ptr != NULL)) {
1159 struct netbuf *na;
1161 na = uaddr2taddr((struct netconfig *)nconf, r.addr);
1162 if (!na) {
1163 stat = RPC_N2AXLATEFAILURE;
1164 ((struct netbuf *)addr_ptr)->len = 0;
1165 goto error;
1167 if (na->len > addr_ptr->maxlen) {
1168 /* Too long address */
1169 stat = RPC_FAILED; /* XXX A better error no */
1170 netdir_free((char *)na, ND_ADDR);
1171 ((struct netbuf *)addr_ptr)->len = 0;
1172 goto error;
1174 (void) memcpy(addr_ptr->buf, na->buf, (int)na->len);
1175 ((struct netbuf *)addr_ptr)->len = na->len;
1176 netdir_free((char *)na, ND_ADDR);
1177 break;
1179 if ((stat != RPC_PROGVERSMISMATCH) &&
1180 (stat != RPC_PROGUNAVAIL))
1181 goto error;
1183 error:
1184 CLNT_DESTROY(client);
1185 if (r.addr)
1186 xdr_free((xdrproc_t)xdr_wrapstring, (char *)&r.addr);
1187 return (stat);
1191 * Gets the time on the remote host.
1192 * Returns 1 if succeeds else 0.
1194 bool_t
1195 rpcb_gettime(const char *host, time_t *timep)
1197 CLIENT *client = NULL;
1198 void *handle;
1199 struct netconfig *nconf;
1200 int vers;
1201 enum clnt_stat st;
1203 if ((host == NULL) || (host[0] == '\0')) {
1204 (void) time(timep);
1205 return (TRUE);
1208 if ((handle = __rpc_setconf("netpath")) == NULL) {
1209 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1210 return (FALSE);
1212 rpc_createerr.cf_stat = RPC_SUCCESS;
1213 while (client == NULL) {
1214 if ((nconf = __rpc_getconf(handle)) == NULL) {
1215 if (rpc_createerr.cf_stat == RPC_SUCCESS)
1216 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1217 break;
1219 client = getclnthandle((char *)host, nconf, NULL);
1220 if (client)
1221 break;
1223 __rpc_endconf(handle);
1224 if (client == NULL)
1225 return (FALSE);
1227 st = CLNT_CALL(client, RPCBPROC_GETTIME,
1228 (xdrproc_t)xdr_void, NULL,
1229 (xdrproc_t)xdr_time_t, (char *)timep, tottimeout);
1231 if ((st == RPC_PROGVERSMISMATCH) || (st == RPC_PROGUNAVAIL)) {
1232 CLNT_CONTROL(client, CLGET_VERS, (char *)&vers);
1233 if (vers == RPCBVERS4) {
1234 /* fall back to earlier version */
1235 vers = RPCBVERS;
1236 CLNT_CONTROL(client, CLSET_VERS, (char *)&vers);
1237 st = CLNT_CALL(client, RPCBPROC_GETTIME,
1238 (xdrproc_t)xdr_void, NULL,
1239 (xdrproc_t)xdr_time_t, (char *)timep,
1240 tottimeout);
1243 CLNT_DESTROY(client);
1244 return (st == RPC_SUCCESS? TRUE : FALSE);
1248 * Converts taddr to universal address. This routine should never
1249 * really be called because local n2a libraries are always provided.
1251 char *
1252 rpcb_taddr2uaddr(struct netconfig *nconf, struct netbuf *taddr)
1254 CLIENT *client;
1255 char *uaddr = NULL;
1257 /* parameter checking */
1258 if (nconf == NULL) {
1259 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1260 return (NULL);
1262 if (taddr == NULL) {
1263 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1264 return (NULL);
1266 client = local_rpcb();
1267 if (!client)
1268 return (NULL);
1270 CLNT_CALL(client, RPCBPROC_TADDR2UADDR, (xdrproc_t)xdr_netbuf,
1271 (char *)taddr, (xdrproc_t)xdr_wrapstring, (char *)&uaddr,
1272 tottimeout);
1273 CLNT_DESTROY(client);
1274 return (uaddr);
1278 * Converts universal address to netbuf. This routine should never
1279 * really be called because local n2a libraries are always provided.
1281 struct netbuf *
1282 rpcb_uaddr2taddr(struct netconfig *nconf, char *uaddr)
1284 CLIENT *client;
1285 struct netbuf *taddr;
1287 /* parameter checking */
1288 if (nconf == NULL) {
1289 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1290 return (NULL);
1292 if (uaddr == NULL) {
1293 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1294 return (NULL);
1296 client = local_rpcb();
1297 if (!client)
1298 return (NULL);
1300 taddr = calloc(1, sizeof (struct netbuf));
1301 if (taddr == NULL) {
1302 CLNT_DESTROY(client);
1303 return (NULL);
1306 if (CLNT_CALL(client, RPCBPROC_UADDR2TADDR, (xdrproc_t)xdr_wrapstring,
1307 (char *)&uaddr, (xdrproc_t)xdr_netbuf, (char *)taddr,
1308 tottimeout) != RPC_SUCCESS) {
1309 free(taddr);
1310 taddr = NULL;
1312 CLNT_DESTROY(client);
1313 return (taddr);