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