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(-)
--- autofs-5.0.7.orig/CHANGELOG
+++ autofs-5.0.7/CHANGELOG
@@ -64,6 +64,7 @@
- fix get_nfs_info() probe.
- fix portmap lookup.
- only probe specific nfs version if requested.
+- fix ipv6 libtirpc getport.
25/07/2012 autofs-5.0.7
=======================
--- autofs-5.0.7.orig/lib/rpc_subs.c
+++ autofs-5.0.7/lib/rpc_subs.c
@@ -234,6 +234,28 @@ static int rpc_do_create_client(struct s
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 s
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 s
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
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
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) {