-autofs-5.0.8 - fix ipv6 libtirpc getport
-
-From: Ian Kent <ikent@redhat.com>
-
-The method that was being used to obtain a service port number
-when using libtirpc was wrong.
----
- CHANGELOG | 1
- lib/rpc_subs.c | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
- 2 files changed, 267 insertions(+), 17 deletions(-)
-
-diff --git a/CHANGELOG b/CHANGELOG
-index 68db340..9c87373 100644
---- a/CHANGELOG
-+++ b/CHANGELOG
-@@ -5,6 +5,7 @@
- - fix task manager not getting signaled.
- - allow --with-systemd to take a path arg.
- - fix WITH_LIBTIRPC function name.
-+- fix ipv6 libtirpc getport.
-
- 17/10/2013 autofs-5.0.8
- =======================
-diff --git a/lib/rpc_subs.c b/lib/rpc_subs.c
-index 46b3e8d..2365b6e 100644
---- a/lib/rpc_subs.c
-+++ b/lib/rpc_subs.c
-@@ -234,6 +234,28 @@ static int rpc_do_create_client(struct sockaddr *addr, struct conn_info *info, i
-
- return 0;
- }
-+static int rpc_getport(struct conn_info *info,
-+ struct pmap *parms, CLIENT *client)
-+{
-+ enum clnt_stat status;
-+
-+ /*
-+ * Check to see if server is up otherwise a getport will take
-+ * forever to timeout.
-+ */
-+ status = clnt_call(client, PMAPPROC_NULL,
-+ (xdrproc_t) xdr_void, 0, (xdrproc_t) xdr_void, 0,
-+ info->timeout);
-+
-+ if (status == RPC_SUCCESS) {
-+ status = clnt_call(client, PMAPPROC_GETPORT,
-+ (xdrproc_t) xdr_pmap, (caddr_t) parms,
-+ (xdrproc_t) xdr_u_short, (caddr_t) port,
-+ info->timeout);
-+ }
-+
-+ return status;
-+}
- #else
- static int rpc_do_create_client(struct sockaddr *addr, struct conn_info *info, int *fd, CLIENT **client)
- {
-@@ -267,9 +289,6 @@ static int rpc_do_create_client(struct sockaddr *addr, struct conn_info *info, i
- laddr = (struct sockaddr *) &in4_laddr;
- in4_raddr->sin_port = htons(info->port);
- slen = sizeof(struct sockaddr_in);
-- /* Use rpcbind v2 for AF_INET */
-- if (info->program == rpcb_prog)
-- info->version = PMAPVERS;
- } else if (addr->sa_family == AF_INET6) {
- struct sockaddr_in6 *in6_raddr = (struct sockaddr_in6 *) addr;
- in6_laddr.sin6_family = AF_INET6;
-@@ -324,6 +343,244 @@ static int rpc_do_create_client(struct sockaddr *addr, struct conn_info *info, i
-
- return 0;
- }
-+
-+/*
-+ * Thankfully nfs-utils had already dealt with this.
-+ * Thanks to Chuck Lever for his nfs-utils patch series, much of
-+ * which is used here.
-+ */
-+static pthread_mutex_t proto_mutex = PTHREAD_MUTEX_INITIALIZER;
-+
-+static enum clnt_stat rpc_get_netid(const sa_family_t family,
-+ const int protocol, char **netid)
-+{
-+ char *nc_protofmly, *nc_proto, *nc_netid;
-+ struct netconfig *nconf;
-+ struct protoent *proto;
-+ void *handle;
-+
-+ switch (family) {
-+ case AF_LOCAL:
-+ case AF_INET:
-+ nc_protofmly = NC_INET;
-+ break;
-+ case AF_INET6:
-+ nc_protofmly = NC_INET6;
-+ break;
-+ default:
-+ return RPC_UNKNOWNPROTO;
-+ }
-+
-+ pthread_mutex_lock(&proto_mutex);
-+ proto = getprotobynumber(protocol);
-+ if (!proto) {
-+ pthread_mutex_unlock(&proto_mutex);
-+ return RPC_UNKNOWNPROTO;
-+ }
-+ nc_proto = strdup(proto->p_name);
-+ pthread_mutex_unlock(&proto_mutex);
-+ if (!nc_proto)
-+ return RPC_SYSTEMERROR;
-+
-+ handle = setnetconfig();
-+ while ((nconf = getnetconfig(handle)) != NULL) {
-+ if (nconf->nc_protofmly != NULL &&
-+ strcmp(nconf->nc_protofmly, nc_protofmly) != 0)
-+ continue;
-+ if (nconf->nc_proto != NULL &&
-+ strcmp(nconf->nc_proto, nc_proto) != 0)
-+ continue;
-+
-+ nc_netid = strdup(nconf->nc_netid);
-+ if (!nc_netid) {
-+ free(nc_proto);
-+ return RPC_SYSTEMERROR;
-+ }
-+
-+ *netid = nc_netid;
-+ }
-+ endnetconfig(handle);
-+ free(nc_proto);
-+
-+ return RPC_SUCCESS;
-+}
-+
-+static char *rpc_sockaddr2universal(const struct sockaddr *addr)
-+{
-+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *) addr;
-+ const struct sockaddr_un *sun = (const struct sockaddr_un *) addr;
-+ const struct sockaddr_in *sin = (const struct sockaddr_in *) addr;
-+ char buf[INET6_ADDRSTRLEN + 8 /* for port information */];
-+ uint16_t port;
-+ size_t count;
-+ char *result;
-+ int len;
-+
-+ switch (addr->sa_family) {
-+ case AF_LOCAL:
-+ return strndup(sun->sun_path, sizeof(sun->sun_path));
-+ case AF_INET:
-+ if (inet_ntop(AF_INET, (const void *)&sin->sin_addr.s_addr,
-+ buf, (socklen_t)sizeof(buf)) == NULL)
-+ goto out_err;
-+ port = ntohs(sin->sin_port);
-+ break;
-+ case AF_INET6:
-+ if (inet_ntop(AF_INET6, (const void *)&sin6->sin6_addr,
-+ buf, (socklen_t)sizeof(buf)) == NULL)
-+ goto out_err;
-+ port = ntohs(sin6->sin6_port);
-+ break;
-+ default:
-+ goto out_err;
-+ }
-+
-+ count = sizeof(buf) - strlen(buf);
-+ len = snprintf(buf + strlen(buf), count, ".%u.%u",
-+ (unsigned)(port >> 8), (unsigned)(port & 0xff));
-+ /* before glibc 2.0.6, snprintf(3) could return -1 */
-+ if (len < 0 || (size_t)len > count)
-+ goto out_err;
-+
-+ result = strdup(buf);
-+ return result;
-+
-+out_err:
-+ return NULL;
-+}
-+
-+static int rpc_universal2port(const char *uaddr)
-+{
-+ char *addrstr;
-+ char *p, *endptr;
-+ unsigned long portlo, porthi;
-+ int port = -1;
-+
-+ addrstr = strdup(uaddr);
-+ if (!addrstr)
-+ return -1;
-+
-+ p = strrchr(addrstr, '.');
-+ if (!p)
-+ goto out;
-+
-+ portlo = strtoul(p + 1, &endptr, 10);
-+ if (*endptr != '\0' || portlo > 255)
-+ goto out;
-+ *p = '\0';
-+
-+ p = strrchr(addrstr, '.');
-+ if (!p)
-+ goto out;
-+
-+ porthi = strtoul(p + 1, &endptr, 10);
-+ if (*endptr != '\0' || porthi > 255)
-+ goto out;
-+ *p = '\0';
-+
-+ port = (porthi << 8) | portlo;
-+
-+out:
-+ free(addrstr);
-+ return port;
-+}
-+
-+static enum clnt_stat rpc_rpcb_getport(CLIENT *client,
-+ struct rpcb *parms,
-+ struct timeval timeout,
-+ unsigned short *port)
-+{
-+ rpcvers_t rpcb_version;
-+ struct rpc_err rpcerr;
-+ int s_port = 0;
-+
-+ for (rpcb_version = RPCBVERS_4;
-+ rpcb_version >= RPCBVERS_3;
-+ rpcb_version--) {
-+ enum clnt_stat status;
-+ char *uaddr = NULL;
-+
-+ CLNT_CONTROL(client, CLSET_VERS, (void *) &rpcb_version);
-+ status = CLNT_CALL(client, (rpcproc_t) RPCBPROC_GETADDR,
-+ (xdrproc_t) xdr_rpcb, (void *) parms,
-+ (xdrproc_t) xdr_wrapstring, (void *) &uaddr,
-+ timeout);
-+
-+ switch (status) {
-+ case RPC_SUCCESS:
-+ if ((uaddr == NULL) || (uaddr[0] == '\0'))
-+ return RPC_PROGNOTREGISTERED;
-+
-+ s_port = rpc_universal2port(uaddr);
-+ xdr_free((xdrproc_t) xdr_wrapstring, (char *) &uaddr);
-+ if (s_port == -1) {
-+ return RPC_N2AXLATEFAILURE;
-+ }
-+ *port = s_port;
-+ return RPC_SUCCESS;
-+
-+ case RPC_PROGVERSMISMATCH:
-+ clnt_geterr(client, &rpcerr);
-+ if (rpcerr.re_vers.low > RPCBVERS4)
-+ return status;
-+ continue;
-+ case RPC_PROCUNAVAIL:
-+ case RPC_PROGUNAVAIL:
-+ continue;
-+ default:
-+ /* Most likely RPC_TIMEDOUT or RPC_CANTRECV */
-+ return status;
-+ }
-+ }
-+
-+ if (s_port == 0)
-+ return RPC_PROGNOTREGISTERED;
-+
-+ return RPC_PROCUNAVAIL;
-+}
-+
-+static enum clnt_stat rpc_getport(struct conn_info *info,
-+ struct pmap *parms, CLIENT *client,
-+ unsigned short *port)
-+{
-+ enum clnt_stat status;
-+ struct sockaddr *paddr, addr;
-+ struct rpcb rpcb_parms;
-+ char *netid, *raddr;
-+
-+ if (info->addr)
-+ paddr = info->addr;
-+ else {
-+ if (!clnt_control(client, CLGET_SERVER_ADDR, (char *) &addr))
-+ return RPC_UNKNOWNADDR;
-+ paddr = &addr;
-+ }
-+
-+ netid = NULL;
-+ status = rpc_get_netid(paddr->sa_family, info->proto, &netid);
-+ if (status != RPC_SUCCESS)
-+ return status;
-+
-+ raddr = rpc_sockaddr2universal(paddr);
-+ if (!raddr) {
-+ free(netid);
-+ return RPC_UNKNOWNADDR;
-+ }
-+
-+ memset(&rpcb_parms, 0, sizeof(rpcb_parms));
-+ rpcb_parms.r_prog = parms->pm_prog;
-+ rpcb_parms.r_vers = parms->pm_vers;
-+ rpcb_parms.r_netid = netid;
-+ rpcb_parms.r_addr = raddr;
-+ rpcb_parms.r_owner = "";
-+
-+ status = rpc_rpcb_getport(client, &rpcb_parms, info->timeout, port);
-+
-+ free(netid);
-+ free(raddr);
-+
-+ return status;
-+}
- #endif
-
- #if defined(HAVE_GETRPCBYNAME) || defined(HAVE_GETSERVBYNAME)
-@@ -647,20 +904,7 @@ int rpc_portmap_getport(struct conn_info *info,
- return ret;
- }
-
-- /*
-- * Check to see if server is up otherwise a getport will take
-- * forever to timeout.
-- */
-- status = clnt_call(client, PMAPPROC_NULL,
-- (xdrproc_t) xdr_void, 0, (xdrproc_t) xdr_void, 0,
-- pmap_info.timeout);
--
-- if (status == RPC_SUCCESS) {
-- status = clnt_call(client, PMAPPROC_GETPORT,
-- (xdrproc_t) xdr_pmap, (caddr_t) parms,
-- (xdrproc_t) xdr_u_short, (caddr_t) port,
-- pmap_info.timeout);
-- }
-+ status = rpc_getport(&pmap_info, parms, client, port);
-
- if (!info->client) {
- /*
-@@ -867,6 +1111,11 @@ static int rpc_get_exports_proto(struct conn_info *info, exports *exp)
- clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) &info->timeout);
-
- client->cl_auth = authunix_create_default();
-+ if (client->cl_auth == NULL) {
-+ error(LOGOPT_ANY, "auth create failed");
-+ clnt_destroy(client);
-+ return 0;
-+ }
-
- vers_entry = 0;
- while (1) {