Print this page
4729 __rpcb_findaddr_timed should try rpcbind protocol 4 first
@@ -19,10 +19,11 @@
*
* CDDL HEADER END
*/
/*
+ * Copyright (c) 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,12 +32,10 @@
* 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"
@@ -456,14 +455,15 @@
void *nc_handle;
if (hostname == NULL) {
#if defined(__i386) && !defined(__amd64)
if ((_nuname(&utsname) == -1) ||
+ ((hostname = strdup(utsname.nodename)) == NULL)) {
#else
if ((uname(&utsname) == -1) ||
-#endif
((hostname = strdup(utsname.nodename)) == NULL)) {
+#endif
syslog(LOG_ERR, "local_rpcb : strdup failed.");
rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
(void) mutex_unlock(&loopnconf_lock);
return (NULL);
}
@@ -684,22 +684,20 @@
return (res);
}
/*
- * An internal function which optimizes rpcb_getaddr function. It also
+ * 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 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.
+ * The algorithm used: First try version 4. Then try version 3 (svr4).
+ * Finally, if the transport is TCP or UDP, try version 2 (portmap).
+ * We assume that version 4 is now 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.
*/
@@ -712,11 +710,13 @@
RPCB parms;
enum clnt_stat clnt_st;
char *ua = NULL;
uint_t vers;
struct netbuf *address = NULL;
- uint_t start_vers = RPCBVERS4;
+ void *handle;
+ rpcb_entry_list_ptr relp = NULL;
+ bool_t tmp_client = FALSE;
/* parameter checking */
if (nconf == NULL) {
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
return (NULL);
@@ -728,117 +728,11 @@
* 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) {
@@ -850,13 +744,11 @@
}
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
+ * First try version 4.
*/
parms.r_prog = program;
parms.r_vers = version;
parms.r_owner = (char *)&nullstring[0]; /* not needed; */
/* just for xdring */
@@ -866,17 +758,18 @@
* 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;
+ handle = __rpc_setconf("datagram_v");
+ } else {
+ handle = __rpc_setconf(nconf->nc_proto);
+ }
+
+ if (handle != NULL) {
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;
@@ -886,17 +779,20 @@
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 */
+ if (client != NULL) {
+
+ if (nconf->nc_semantics == NC_TPI_COTS_ORD ||
+ nconf->nc_semantics == NC_TPI_COTS)
+ tmp_client = TRUE;
+
+ /* 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) {
@@ -908,17 +804,19 @@
address = NULL;
goto error;
}
}
- CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)&rpcbrmttime);
+ 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) {
+ switch (clnt_st) {
+ case RPC_SUCCESS:
if (address = got_entry(relp, nconf)) {
xdr_free((xdrproc_t)xdr_rpcb_entry_list_ptr,
(char *)&relp);
goto done;
}
@@ -927,25 +825,29 @@
(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
+ * regular way with version 3, then 2
*/
- 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 {
+ /* Try the next version */
+ break;
+ case RPC_PROGVERSMISMATCH:
+ case RPC_PROGUNAVAIL:
+ /* Try the next version */
+ break;
+ default:
rpc_createerr.cf_stat = RPC_PMAPFAILURE;
clnt_geterr(client, &rpc_createerr.cf_error);
goto error;
+ break;
}
- }
+ } /* End of version 4 */
-regular_rpcbind:
+ /*
+ * Try version 3
+ */
/* 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 */
@@ -955,15 +857,13 @@
parms.r_addr = NULL;
}
if (client == NULL) {
client = _getclnthandle_timed(host, nconf, &parms.r_addr, tp);
- if (client == NULL) {
- address = NULL;
- goto error;
}
- }
+ if (client != NULL) {
+ tmp_client = FALSE;
if (parms.r_addr == NULL) {
parms.r_addr = strdup(""); /* for XDRing */
if (parms.r_addr == NULL) {
syslog(LOG_ERR, "__rpcb_findaddr_timed: "
"strdup failed.");
@@ -971,70 +871,186 @@
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_RETRY_TIMEOUT,
+ (char *)&rpcbrmttime);
+ vers = RPCBVERS; /* 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)
+ switch (clnt_st) {
+ case RPC_SUCCESS:
+ if ((ua != NULL) && (ua[0] != '\0')) {
+ address = uaddr2taddr(nconf, ua);
+#ifdef ND_DEBUG
+ fprintf(stderr, "\tRemote address is [%s]\n",
+ ua);
+#endif
+ xdr_free((xdrproc_t)xdr_wrapstring,
+ (char *)&ua);
+ } else if (ua != NULL) {
xdr_free(xdr_wrapstring, (char *)&ua);
-
- /* address unknown */
- rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
- goto error;
}
- address = uaddr2taddr(nconf, ua);
+
+ if (ua != NULL && address != NULL) {
+ goto done;
+ } else if (address == NULL) {
+ /* We don't know about your universal addr */
#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;
+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
goto error;
}
- goto done;
- }
- if (clnt_st == RPC_PROGVERSMISMATCH) {
- struct rpc_err rpcerr;
-
- clnt_geterr(client, &rpcerr);
- if (rpcerr.re_vers.low > RPCBVERS4)
+ /* Try the next version */
+ break;
+ case RPC_PROGVERSMISMATCH:
+ clnt_geterr(client, &rpc_createerr.cf_error);
+ if (rpc_createerr.cf_error.re_vers.low > RPCBVERS4)
goto error; /* a new version, can't handle */
- } else if (clnt_st != RPC_PROGUNAVAIL) {
- /* Cant handle this error */
+ /* Try the next version */
+ break;
+ case RPC_PROGUNAVAIL:
+ /* Try the next version */
+ break;
+ default:
+ clnt_geterr(client, &rpc_createerr.cf_error);
+ rpc_createerr.cf_stat = RPC_PMAPFAILURE;
goto error;
+ break;
}
+ } else {
+ address = NULL;
+ } /* End of version 3 */
+
+ /*
+ * 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) {
+ CLNT_DESTROY(client);
+ client = NULL;
+ free(parms.r_addr);
+ parms.r_addr = NULL;
}
+ if ((handle = __rpc_setconf("udp")) == NULL) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+ return (NULL);
+ }
- if ((address == NULL) || (address->len == 0)) {
+ /*
+ * 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);
+ }
+ if (client == NULL)
+ return (NULL);
+
+ if (strcmp(nconf->nc_proto, NC_TCP) == 0)
+ tmp_client = TRUE;
+
+ /*
+ * 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) {
+ 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
error:
+ /* Return NULL address and NULL client */
if (client) {
CLNT_DESTROY(client);
client = NULL;
}
+
done:
- if (nconf->nc_semantics != NC_TPI_CLTS) {
- /* This client is the connectionless one */
+ /* Return an address and optional client */
+ if (tmp_client) {
+ /* This client is the temporary one */
if (client) {
CLNT_DESTROY(client);
client = NULL;
}
}