Print this page
4729 __rpcb_findaddr_timed should try rpcbind protocol 4 first
Reviewed by: Marcel Telka <marcel@telka.sk>

*** 19,28 **** --- 19,29 ---- * * CDDL HEADER END */ /* + * Copyright 2014 Gary Mills * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
*** 31,42 **** * Portions of this source code were derived from Berkeley * 4.3 BSD under license from the Regents of the University of * California. */ - #pragma ident "%Z%%M% %I% %E% SMI" - /* * interface to rpcbind rpc service. */ #include "mt.h" --- 32,41 ----
*** 44,62 **** #include <assert.h> #include <rpc/rpc.h> #include <rpc/rpcb_prot.h> #include <netconfig.h> #include <netdir.h> #include <rpc/nettype.h> #include <syslog.h> #ifdef PORTMAP #include <netinet/in.h> /* FOR IPPROTO_TCP/UDP definitions */ #include <rpc/pmap_prot.h> #endif - #ifdef ND_DEBUG - #include <stdio.h> - #endif #include <sys/utsname.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <unistd.h> --- 43,59 ---- #include <assert.h> #include <rpc/rpc.h> #include <rpc/rpcb_prot.h> #include <netconfig.h> #include <netdir.h> + #include <netdb.h> #include <rpc/nettype.h> #include <syslog.h> #ifdef PORTMAP #include <netinet/in.h> /* FOR IPPROTO_TCP/UDP definitions */ #include <rpc/pmap_prot.h> #endif #include <sys/utsname.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <unistd.h>
*** 168,181 **** assert(RW_READ_HELD(&rpcbaddr_cache_lock)); for (cptr = front; cptr != NULL; cptr = cptr->ac_next) { if ((strcmp(cptr->ac_host, host) == 0) && (strcmp(cptr->ac_netid, netid) == 0) && (time(NULL) <= cptr->ac_maxtime)) { - #ifdef ND_DEBUG - fprintf(stderr, "Found cache entry for %s: %s\n", - host, netid); - #endif return (cptr); } } return (NULL); } --- 165,174 ----
*** 231,243 **** if (ad_cache->ac_taddr->buf == NULL) { goto memerr1; } (void) memcpy(ad_cache->ac_taddr->buf, taddr->buf, taddr->len); - #ifdef ND_DEBUG - (void) fprintf(stderr, "Added to cache: %s : %s\n", host, netid); - #endif /* VARIABLES PROTECTED BY rpcbaddr_cache_lock: cptr */ (void) rw_wrlock(&rpcbaddr_cache_lock); if (cachesize < CACHESIZE) { --- 224,233 ----
*** 251,264 **** while (cptr->ac_next) { prevptr = cptr; cptr = cptr->ac_next; } - #ifdef ND_DEBUG - fprintf(stderr, "Deleted from cache: %s : %s\n", - cptr->ac_host, cptr->ac_netid); - #endif free(cptr->ac_host); free(cptr->ac_netid); free(cptr->ac_taddr->buf); free(cptr->ac_taddr); if (cptr->ac_uaddr) --- 241,250 ----
*** 379,393 **** (void) rw_unlock(&rpcbaddr_cache_lock); free(addr_to_delete.buf); } rpcbind_hs.h_host = host; rpcbind_hs.h_serv = "rpcbind"; - #ifdef ND_DEBUG - fprintf(stderr, "rpcbind client routines: diagnostics :\n"); - fprintf(stderr, "\tGetting address for (%s, %s, %s) ... \n", - rpcbind_hs.h_host, rpcbind_hs.h_serv, nconf->nc_netid); - #endif if ((neterr = netdir_getbyname(nconf, &rpcbind_hs, &nas)) != 0) { if (neterr == ND_NOHOST) rpc_createerr.cf_stat = RPC_UNKNOWNHOST; else --- 365,374 ----
*** 396,432 **** } /* XXX nas should perhaps be cached for better performance */ for (j = 0; j < nas->n_cnt; j++) { addr = &(nas->n_addrs[j]); - #ifdef ND_DEBUG - { - int i; - char *ua; - - ua = taddr2uaddr(nconf, &(nas->n_addrs[j])); - fprintf(stderr, "Got it [%s]\n", ua); - free(ua); - - fprintf(stderr, "\tnetbuf len = %d, maxlen = %d\n", - addr->len, addr->maxlen); - fprintf(stderr, "\tAddress is "); - for (i = 0; i < addr->len; i++) - fprintf(stderr, "%u.", addr->buf[i]); - fprintf(stderr, "\n"); - } - #endif client = _clnt_tli_create_timed(RPC_ANYFD, nconf, addr, RPCBPROG, RPCBVERS4, 0, 0, tp); if (client) break; } - #ifdef ND_DEBUG - if (!client) { - clnt_pcreateerror("rpcbind clnt interface"); - } - #endif if (client) { tmpaddr = targaddr ? taddr2uaddr(nconf, addr) : NULL; add_cache(host, nconf->nc_netid, addr, tmpaddr); if (targaddr) { --- 377,391 ----
*** 437,475 **** return (client); } /* * This routine will return a client handle that is connected to the local ! * rpcbind. Returns NULL on error and free's everything. */ static CLIENT * local_rpcb(void) { static struct netconfig *loopnconf; ! static char *hostname; extern mutex_t loopnconf_lock; /* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */ (void) mutex_lock(&loopnconf_lock); if (loopnconf == NULL) { - struct utsname utsname; struct netconfig *nconf, *tmpnconf = NULL; void *nc_handle; ! if (hostname == NULL) { ! #if defined(__i386) && !defined(__amd64) ! if ((_nuname(&utsname) == -1) || ! #else ! if ((uname(&utsname) == -1) || ! #endif ! ((hostname = strdup(utsname.nodename)) == NULL)) { ! syslog(LOG_ERR, "local_rpcb : strdup failed."); rpc_createerr.cf_stat = RPC_UNKNOWNHOST; (void) mutex_unlock(&loopnconf_lock); return (NULL); } - } nc_handle = setnetconfig(); if (nc_handle == NULL) { /* fails to open netconfig file */ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; (void) mutex_unlock(&loopnconf_lock); --- 396,427 ---- return (client); } /* * This routine will return a client handle that is connected to the local ! * rpcbind. Returns NULL on error. */ static CLIENT * local_rpcb(void) { static struct netconfig *loopnconf; ! static char hostname[MAXHOSTNAMELEN + 1]; extern mutex_t loopnconf_lock; /* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */ (void) mutex_lock(&loopnconf_lock); if (loopnconf == NULL) { struct netconfig *nconf, *tmpnconf = NULL; void *nc_handle; ! if ((hostname[0] == '\0') && (gethostname(hostname, ! sizeof (hostname)) < 0)) { ! syslog(LOG_ERR, "local_rpcb: gethostname failed."); rpc_createerr.cf_stat = RPC_UNKNOWNHOST; (void) mutex_unlock(&loopnconf_lock); return (NULL); } nc_handle = setnetconfig(); if (nc_handle == NULL) { /* fails to open netconfig file */ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; (void) mutex_unlock(&loopnconf_lock);
*** 597,613 **** if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) && (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) && (nconf->nc_semantics == rmap->r_nc_semantics) && (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != NULL)) { na = uaddr2taddr(nconf, rmap->r_maddr); - #ifdef ND_DEBUG - fprintf(stderr, "\tRemote address is [%s].\n", - rmap->r_maddr); - if (!na) - fprintf(stderr, - "\tCouldn't resolve remote address!\n"); - #endif break; } } return (na); } --- 549,558 ----
*** 617,639 **** * local transport. */ bool_t __rpcbind_is_up(void) { ! struct utsname name; char uaddr[SYS_NMLN]; struct netbuf *addr; int fd; struct t_call *sndcall; struct netconfig *netconf; bool_t res; ! #if defined(__i386) && !defined(__amd64) ! if (_nuname(&name) == -1) ! #else ! if (uname(&name) == -1) ! #endif return (TRUE); if ((fd = t_open("/dev/ticotsord", O_RDWR, NULL)) == -1) return (TRUE); --- 562,580 ---- * local transport. */ bool_t __rpcbind_is_up(void) { ! char hostname[MAXHOSTNAMELEN + 1]; char uaddr[SYS_NMLN]; struct netbuf *addr; int fd; struct t_call *sndcall; struct netconfig *netconf; bool_t res; ! if (gethostname(hostname, sizeof (hostname)) < 0) return (TRUE); if ((fd = t_open("/dev/ticotsord", O_RDWR, NULL)) == -1) return (TRUE);
*** 647,657 **** (void) t_close(fd); return (TRUE); } uaddr[0] = '\0'; ! (void) strcpy(uaddr, name.nodename); (void) strcat(uaddr, ".rpc"); if ((netconf = getnetconfigent("ticotsord")) == NULL) { (void) t_free((char *)sndcall, T_CALL); (void) t_close(fd); return (FALSE); --- 588,598 ---- (void) t_close(fd); return (TRUE); } uaddr[0] = '\0'; ! (void) strlcpy(uaddr, hostname, sizeof (uaddr) - 5); (void) strcat(uaddr, ".rpc"); if ((netconf = getnetconfigent("ticotsord")) == NULL) { (void) t_free((char *)sndcall, T_CALL); (void) t_close(fd); return (FALSE);
*** 684,705 **** return (res); } /* ! * An internal function which optimizes rpcb_getaddr function. It also * returns the client handle that it uses to contact the remote rpcbind. * ! * The algorithm used: If the transports is TCP or UDP, it first tries ! * version 2 (portmap), 4 and then 3 (svr4). This order should be ! * changed in the next OS release to 4, 2 and 3. We are assuming that by ! * that time, version 4 would be available on many machines on the network. * With this algorithm, we get performance as well as a plan for * obsoleting version 2. * - * For all other transports, the algorithm remains as 4 and then 3. - * * XXX: Due to some problems with t_connect(), we do not reuse the same client * handle for COTS cases and hence in these cases we do not return the * client handle. This code will change if t_connect() ever * starts working properly. Also look under clnt_vc.c. */ --- 625,645 ---- return (res); } /* ! * An internal function which optimizes rpcb_getaddr function. It returns ! * the universal address of the remote service or NULL. It also optionally * returns the client handle that it uses to contact the remote rpcbind. + * The caller will re-purpose the client handle to contact the remote service. * ! * The algorithm used: First try version 4. Then try version 3 (svr4). ! * Finally, if the transport is TCP or UDP, try version 2 (portmap). ! * Version 4 is now available with all current systems on the network. * With this algorithm, we get performance as well as a plan for * obsoleting version 2. * * XXX: Due to some problems with t_connect(), we do not reuse the same client * handle for COTS cases and hence in these cases we do not return the * client handle. This code will change if t_connect() ever * starts working properly. Also look under clnt_vc.c. */
*** 712,726 **** RPCB parms; enum clnt_stat clnt_st; char *ua = NULL; uint_t vers; struct netbuf *address = NULL; ! uint_t start_vers = RPCBVERS4; /* parameter checking */ if (nconf == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; return (NULL); } parms.r_addr = NULL; --- 652,672 ---- RPCB parms; enum clnt_stat clnt_st; char *ua = NULL; uint_t vers; struct netbuf *address = NULL; ! void *handle; ! rpcb_entry_list_ptr relp = NULL; ! bool_t tmp_client = FALSE; /* parameter checking */ if (nconf == NULL) { rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; + /* + * Setting rpc_createerr.cf_stat is sufficient. + * No details in rpc_createerr.cf_error needed. + */ return (NULL); } parms.r_addr = NULL;
*** 728,844 **** * Use default total timeout if no timeout is specified. */ if (tp == NULL) tp = &tottimeout; - #ifdef PORTMAP - /* Try version 2 for TCP or UDP */ - if (strcmp(nconf->nc_protofmly, NC_INET) == 0) { - ushort_t port = 0; - struct netbuf remote; - uint_t pmapvers = 2; - struct pmap pmapparms; - /* - * Try UDP only - there are some portmappers out - * there that use UDP only. - */ - if (strcmp(nconf->nc_proto, NC_TCP) == 0) { - struct netconfig *newnconf; - void *handle; - - if ((handle = __rpc_setconf("udp")) == NULL) { - rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; - return (NULL); - } - - /* - * The following to reinforce that you can - * only request for remote address through - * the same transport you are requesting. - * ie. requesting unversial address - * of IPv4 has to be carried through IPv4. - * Can't use IPv6 to send out the request. - * The mergeaddr in rpcbind can't handle - * this. - */ - for (;;) { - if ((newnconf = __rpc_getconf(handle)) - == NULL) { - __rpc_endconf(handle); - rpc_createerr.cf_stat = - RPC_UNKNOWNPROTO; - return (NULL); - } - /* - * here check the protocol family to - * be consistent with the request one - */ - if (strcmp(newnconf->nc_protofmly, - nconf->nc_protofmly) == NULL) - break; - } - - client = _getclnthandle_timed(host, newnconf, - &parms.r_addr, tp); - __rpc_endconf(handle); - } else { - client = _getclnthandle_timed(host, nconf, - &parms.r_addr, tp); - } - if (client == NULL) - return (NULL); - - /* - * Set version and retry timeout. - */ - CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime); - CLNT_CONTROL(client, CLSET_VERS, (char *)&pmapvers); - - pmapparms.pm_prog = program; - pmapparms.pm_vers = version; - pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ? - IPPROTO_UDP : IPPROTO_TCP; - pmapparms.pm_port = 0; /* not needed */ - clnt_st = CLNT_CALL(client, PMAPPROC_GETPORT, - (xdrproc_t)xdr_pmap, (caddr_t)&pmapparms, - (xdrproc_t)xdr_u_short, (caddr_t)&port, - *tp); - if (clnt_st != RPC_SUCCESS) { - if ((clnt_st == RPC_PROGVERSMISMATCH) || - (clnt_st == RPC_PROGUNAVAIL)) - goto try_rpcbind; /* Try different versions */ - rpc_createerr.cf_stat = RPC_PMAPFAILURE; - clnt_geterr(client, &rpc_createerr.cf_error); - goto error; - } else if (port == 0) { - address = NULL; - rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; - goto error; - } - port = htons(port); - CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)&remote); - if (((address = malloc(sizeof (struct netbuf))) == NULL) || - ((address->buf = malloc(remote.len)) == NULL)) { - rpc_createerr.cf_stat = RPC_SYSTEMERROR; - clnt_geterr(client, &rpc_createerr.cf_error); - if (address) { - free(address); - address = NULL; - } - goto error; - } - (void) memcpy(address->buf, remote.buf, remote.len); - (void) memcpy(&address->buf[sizeof (short)], &port, - sizeof (short)); - address->len = address->maxlen = remote.len; - goto done; - } - #endif - - try_rpcbind: - /* * Check if rpcbind is up. This prevents needless delays when * accessing applications such as the keyserver while booting * disklessly. */ if (check_rpcbind && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) { --- 674,684 ----
*** 850,862 **** } check_rpcbind = FALSE; } /* ! * Now we try version 4 and then 3. ! * We also send the remote system the address we used to ! * contact it in case it can help to connect back with us */ parms.r_prog = program; parms.r_vers = version; parms.r_owner = (char *)&nullstring[0]; /* not needed; */ /* just for xdring */ --- 690,700 ---- } check_rpcbind = FALSE; } /* ! * First try version 4. */ parms.r_prog = program; parms.r_vers = version; parms.r_owner = (char *)&nullstring[0]; /* not needed; */ /* just for xdring */
*** 866,1051 **** * If a COTS transport is being used, try getting address via CLTS * transport. This works only with version 4. */ if (nconf->nc_semantics == NC_TPI_COTS_ORD || nconf->nc_semantics == NC_TPI_COTS) { ! void *handle; struct netconfig *nconf_clts; - rpcb_entry_list_ptr relp = NULL; ! if (client == NULL) { ! /* This did not go through the above PORTMAP/TCP code */ ! if ((handle = __rpc_setconf("datagram_v")) != NULL) { ! while ((nconf_clts = __rpc_getconf(handle)) ! != NULL) { if (strcmp(nconf_clts->nc_protofmly, nconf->nc_protofmly) != 0) { continue; } ! client = _getclnthandle_timed(host, ! nconf_clts, &parms.r_addr, ! tp); break; } __rpc_endconf(handle); } - if (client == NULL) - goto regular_rpcbind; /* Go the regular way */ } else { ! /* This is a UDP PORTMAP handle. Change to version 4 */ vers = RPCBVERS4; CLNT_CONTROL(client, CLSET_VERS, (char *)&vers); ! } /* * We also send the remote system the address we used to * contact it in case it can help it connect back with us */ if (parms.r_addr == NULL) { parms.r_addr = strdup(""); /* for XDRing */ if (parms.r_addr == NULL) { syslog(LOG_ERR, "__rpcb_findaddr_timed: " "strdup failed."); rpc_createerr.cf_stat = RPC_SYSTEMERROR; - address = NULL; goto error; } } ! CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime); clnt_st = CLNT_CALL(client, RPCBPROC_GETADDRLIST, (xdrproc_t)xdr_rpcb, (char *)&parms, ! (xdrproc_t)xdr_rpcb_entry_list_ptr, ! (char *)&relp, *tp); ! if (clnt_st == RPC_SUCCESS) { ! if (address = got_entry(relp, nconf)) { xdr_free((xdrproc_t)xdr_rpcb_entry_list_ptr, (char *)&relp); goto done; } ! /* Entry not found for this transport */ ! xdr_free((xdrproc_t)xdr_rpcb_entry_list_ptr, ! (char *)&relp); /* ! * XXX: should have perhaps returned with error but * since the remote machine might not always be able * to send the address on all transports, we try the ! * regular way with regular_rpcbind */ ! goto regular_rpcbind; ! } else if ((clnt_st == RPC_PROGVERSMISMATCH) || ! (clnt_st == RPC_PROGUNAVAIL)) { ! start_vers = RPCBVERS; /* Try version 3 now */ ! goto regular_rpcbind; /* Try different versions */ ! } else { ! rpc_createerr.cf_stat = RPC_PMAPFAILURE; clnt_geterr(client, &rpc_createerr.cf_error); goto error; } - } ! regular_rpcbind: ! /* Now the same transport is to be used to get the address */ ! if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) || ! (nconf->nc_semantics == NC_TPI_COTS))) { ! /* A CLTS type of client - destroy it */ CLNT_DESTROY(client); client = NULL; free(parms.r_addr); parms.r_addr = NULL; } if (client == NULL) { client = _getclnthandle_timed(host, nconf, &parms.r_addr, tp); - if (client == NULL) { - address = NULL; - goto error; } ! } if (parms.r_addr == NULL) { parms.r_addr = strdup(""); /* for XDRing */ if (parms.r_addr == NULL) { syslog(LOG_ERR, "__rpcb_findaddr_timed: " "strdup failed."); ! address = NULL; rpc_createerr.cf_stat = RPC_SYSTEMERROR; goto error; } } ! /* First try from start_vers and then version 3 (RPCBVERS) */ ! ! CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime); ! for (vers = start_vers; vers >= RPCBVERS; vers--) { ! /* Set the version */ CLNT_CONTROL(client, CLSET_VERS, (char *)&vers); clnt_st = CLNT_CALL(client, RPCBPROC_GETADDR, (xdrproc_t)xdr_rpcb, (char *)&parms, ! (xdrproc_t)xdr_wrapstring, ! (char *)&ua, *tp); ! if (clnt_st == RPC_SUCCESS) { ! if ((ua == NULL) || (ua[0] == NULL)) { ! if (ua != NULL) ! xdr_free(xdr_wrapstring, (char *)&ua); ! /* address unknown */ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; goto error; } ! address = uaddr2taddr(nconf, ua); ! #ifdef ND_DEBUG ! fprintf(stderr, "\tRemote address is [%s]\n", ua); ! if (!address) ! fprintf(stderr, ! "\tCouldn't resolve remote address!\n"); #endif ! xdr_free((xdrproc_t)xdr_wrapstring, (char *)&ua); ! ! if (!address) { ! /* We don't know about your universal address */ ! rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; goto error; } ! goto done; } ! if (clnt_st == RPC_PROGVERSMISMATCH) { ! struct rpc_err rpcerr; ! clnt_geterr(client, &rpcerr); ! if (rpcerr.re_vers.low > RPCBVERS4) ! goto error; /* a new version, can't handle */ ! } else if (clnt_st != RPC_PROGUNAVAIL) { ! /* Cant handle this error */ goto error; } } ! if ((address == NULL) || (address->len == 0)) { rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; clnt_geterr(client, &rpc_createerr.cf_error); } error: ! if (client) { CLNT_DESTROY(client); client = NULL; } done: ! if (nconf->nc_semantics != NC_TPI_CLTS) { ! /* This client is the connectionless one */ ! if (client) { CLNT_DESTROY(client); client = NULL; } ! } ! if (clpp) { *clpp = client; ! } else if (client) { CLNT_DESTROY(client); } - if (parms.r_addr) free(parms.r_addr); return (address); } --- 704,1076 ---- * If a COTS transport is being used, try getting address via CLTS * transport. This works only with version 4. */ if (nconf->nc_semantics == NC_TPI_COTS_ORD || nconf->nc_semantics == NC_TPI_COTS) { ! tmp_client = TRUE; ! if ((handle = __rpc_setconf("datagram_v")) != NULL) { struct netconfig *nconf_clts; ! while ((nconf_clts = __rpc_getconf(handle)) != NULL) { if (strcmp(nconf_clts->nc_protofmly, nconf->nc_protofmly) != 0) { continue; } ! /* ! * Sets rpc_createerr.cf_error members ! * on failure ! */ ! client = _getclnthandle_timed(host, nconf_clts, ! &parms.r_addr, tp); break; } __rpc_endconf(handle); } } else { ! /* Sets rpc_createerr.cf_error members on failure */ ! client = _getclnthandle_timed(host, nconf, &parms.r_addr, tp); ! } ! ! if (client != NULL) { ! ! /* Set rpcbind version 4 */ vers = RPCBVERS4; CLNT_CONTROL(client, CLSET_VERS, (char *)&vers); ! /* * We also send the remote system the address we used to * contact it in case it can help it connect back with us */ if (parms.r_addr == NULL) { parms.r_addr = strdup(""); /* for XDRing */ if (parms.r_addr == NULL) { syslog(LOG_ERR, "__rpcb_findaddr_timed: " "strdup failed."); + /* Construct a system error */ + rpc_createerr.cf_error.re_errno = errno; + rpc_createerr.cf_error.re_terrno = 0; rpc_createerr.cf_stat = RPC_SYSTEMERROR; goto error; } } ! CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, ! (char *)&rpcbrmttime); + /* Sets error structure members in client handle */ clnt_st = CLNT_CALL(client, RPCBPROC_GETADDRLIST, (xdrproc_t)xdr_rpcb, (char *)&parms, ! (xdrproc_t)xdr_rpcb_entry_list_ptr, (char *)&relp, *tp); ! ! switch (clnt_st) { ! case RPC_SUCCESS: /* Call succeeded */ ! address = got_entry(relp, nconf); xdr_free((xdrproc_t)xdr_rpcb_entry_list_ptr, (char *)&relp); + if (address != NULL) { + /* Program number and version number matched */ goto done; } ! /* Program and version not found for this transport */ /* ! * XXX: should have returned with RPC_PROGUNAVAIL ! * or perhaps RPC_PROGNOTREGISTERED error but * since the remote machine might not always be able * to send the address on all transports, we try the ! * regular way with version 3, then 2 */ ! /* Try the next version */ ! break; ! case RPC_PROGVERSMISMATCH: /* RPC protocol mismatch */ clnt_geterr(client, &rpc_createerr.cf_error); + if (rpc_createerr.cf_error.re_vers.low > vers) { + rpc_createerr.cf_stat = clnt_st; + goto error; /* a new version, can't handle */ + } + /* Try the next version */ + break; + case RPC_PROCUNAVAIL: /* Procedure unavailable */ + case RPC_PROGUNAVAIL: /* Program not available */ + case RPC_TIMEDOUT: /* Call timed out */ + /* Try the next version */ + break; + default: + clnt_geterr(client, &rpc_createerr.cf_error); + rpc_createerr.cf_stat = RPC_PMAPFAILURE; goto error; + break; } ! } else { ! /* No client */ ! tmp_client = FALSE; ! ! } /* End of version 4 */ ! ! /* Destroy a temporary client */ ! if (client != NULL && tmp_client) { CLNT_DESTROY(client); client = NULL; free(parms.r_addr); parms.r_addr = NULL; } + tmp_client = FALSE; + /* + * Try version 3 + */ + + /* Now the same transport is to be used to get the address */ if (client == NULL) { + /* Sets rpc_createerr.cf_error members on failure */ client = _getclnthandle_timed(host, nconf, &parms.r_addr, tp); } ! address = NULL; ! if (client != NULL) { if (parms.r_addr == NULL) { parms.r_addr = strdup(""); /* for XDRing */ if (parms.r_addr == NULL) { syslog(LOG_ERR, "__rpcb_findaddr_timed: " "strdup failed."); ! /* Construct a system error */ ! rpc_createerr.cf_error.re_errno = errno; ! rpc_createerr.cf_error.re_terrno = 0; rpc_createerr.cf_stat = RPC_SYSTEMERROR; goto error; } } ! CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, ! (char *)&rpcbrmttime); ! vers = RPCBVERS; /* Set the version */ CLNT_CONTROL(client, CLSET_VERS, (char *)&vers); + + /* Sets error structure members in client handle */ clnt_st = CLNT_CALL(client, RPCBPROC_GETADDR, (xdrproc_t)xdr_rpcb, (char *)&parms, ! (xdrproc_t)xdr_wrapstring, (char *)&ua, *tp); ! switch (clnt_st) { ! case RPC_SUCCESS: /* Call succeeded */ ! if (ua != NULL) { ! if (ua[0] != '\0') { ! address = uaddr2taddr(nconf, ua); ! } ! xdr_free((xdrproc_t)xdr_wrapstring, ! (char *)&ua); ! ! if (address != NULL) { ! goto done; ! } ! /* NULL universal address */ ! /* But client call was successful */ ! clnt_geterr(client, &rpc_createerr.cf_error); rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; goto error; } ! #ifndef PORTMAP ! clnt_geterr(client, &rpc_createerr.cf_error); ! rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; ! goto error; #endif ! /* Try the next version */ ! break; ! case RPC_PROGVERSMISMATCH: /* RPC protocol mismatch */ ! clnt_geterr(client, &rpc_createerr.cf_error); ! #ifdef PORTMAP ! if (rpc_createerr.cf_error.re_vers.low > vers) { ! rpc_createerr.cf_stat = clnt_st; ! goto error; /* a new version, can't handle */ ! } ! #else ! rpc_createerr.cf_stat = clnt_st; goto error; + #endif + /* Try the next version */ + break; + #ifdef PORTMAP + case RPC_PROCUNAVAIL: /* Procedure unavailable */ + case RPC_PROGUNAVAIL: /* Program not available */ + case RPC_TIMEDOUT: /* Call timed out */ + /* Try the next version */ + break; + #endif + default: + clnt_geterr(client, &rpc_createerr.cf_error); + rpc_createerr.cf_stat = RPC_PMAPFAILURE; + goto error; + break; } ! } /* End of version 3 */ ! #ifndef PORTMAP ! /* cf_error members set by creation failure */ ! rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; ! #endif ! /* ! * Try version 2 ! */ ! ! #ifdef PORTMAP ! /* Try version 2 for TCP or UDP */ ! if (strcmp(nconf->nc_protofmly, NC_INET) == 0) { ! ushort_t port = 0; ! struct netbuf remote; ! uint_t pmapvers = 2; ! struct pmap pmapparms; ! ! /* ! * Try UDP only - there are some portmappers out ! * there that use UDP only. ! */ ! if (strcmp(nconf->nc_proto, NC_TCP) == 0) { ! struct netconfig *newnconf; ! ! if (client != NULL) { ! CLNT_DESTROY(client); ! client = NULL; ! free(parms.r_addr); ! parms.r_addr = NULL; } ! if ((handle = __rpc_setconf("udp")) == NULL) { ! /* Construct an unknown protocol error */ ! rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; ! goto error; ! } ! /* ! * The following to reinforce that you can ! * only request for remote address through ! * the same transport you are requesting. ! * ie. requesting unversial address ! * of IPv4 has to be carried through IPv4. ! * Can't use IPv6 to send out the request. ! * The mergeaddr in rpcbind can't handle ! * this. ! */ ! for (;;) { ! if ((newnconf = __rpc_getconf(handle)) ! == NULL) { ! __rpc_endconf(handle); ! /* ! * Construct an unknown protocol ! * error ! */ ! rpc_createerr.cf_stat = ! RPC_UNKNOWNPROTO; goto error; } + /* + * here check the protocol family to + * be consistent with the request one + */ + if (strcmp(newnconf->nc_protofmly, + nconf->nc_protofmly) == 0) + break; } ! /* Sets rpc_createerr.cf_error members on failure */ ! client = _getclnthandle_timed(host, newnconf, ! &parms.r_addr, tp); ! __rpc_endconf(handle); ! tmp_client = TRUE; ! } ! if (client == NULL) { ! /* ! * rpc_createerr. cf_error members were set by ! * creation failure ! */ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; + tmp_client = FALSE; + goto error; + } + + /* + * Set version and retry timeout. + */ + CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime); + CLNT_CONTROL(client, CLSET_VERS, (char *)&pmapvers); + + pmapparms.pm_prog = program; + pmapparms.pm_vers = version; + pmapparms.pm_prot = (strcmp(nconf->nc_proto, NC_TCP) != 0) ? + IPPROTO_UDP : IPPROTO_TCP; + pmapparms.pm_port = 0; /* not needed */ + + /* Sets error structure members in client handle */ + clnt_st = CLNT_CALL(client, PMAPPROC_GETPORT, + (xdrproc_t)xdr_pmap, (caddr_t)&pmapparms, + (xdrproc_t)xdr_u_short, (caddr_t)&port, *tp); + + if (clnt_st != RPC_SUCCESS) { clnt_geterr(client, &rpc_createerr.cf_error); + rpc_createerr.cf_stat = RPC_RPCBFAILURE; + goto error; + } else if (port == 0) { + /* Will be NULL universal address */ + /* But client call was successful */ + clnt_geterr(client, &rpc_createerr.cf_error); + rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; + goto error; } + port = htons(port); + CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)&remote); + if (((address = malloc(sizeof (struct netbuf))) == NULL) || + ((address->buf = malloc(remote.len)) == NULL)) { + /* Construct a system error */ + rpc_createerr.cf_error.re_errno = errno; + rpc_createerr.cf_error.re_terrno = 0; + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + free(address); + address = NULL; + goto error; + } + (void) memcpy(address->buf, remote.buf, remote.len); + (void) memcpy(&address->buf[sizeof (short)], &port, + sizeof (short)); + address->len = address->maxlen = remote.len; + goto done; + } else { + /* + * This is not NC_INET. + * Always an error for version 2. + */ + if (client != NULL && clnt_st != RPC_SUCCESS) { + /* There is a client that failed */ + clnt_geterr(client, &rpc_createerr.cf_error); + rpc_createerr.cf_stat = clnt_st; + } else { + /* Something else */ + rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; + /* + * Setting rpc_createerr.cf_stat is sufficient. + * No details in rpc_createerr.cf_error needed. + */ + } + } + #endif error: ! /* Return NULL address and NULL client */ ! address = NULL; ! if (client != NULL) { CLNT_DESTROY(client); client = NULL; } + done: ! /* Return an address and optional client */ ! if (client != NULL && tmp_client) { ! /* This client is the temporary one */ CLNT_DESTROY(client); client = NULL; } ! if (clpp != NULL) { *clpp = client; ! } else if (client != NULL) { CLNT_DESTROY(client); } free(parms.r_addr); return (address); }