commit 17619288497d76ade1671f0adbda682548d026d5 Author: Olaf Kirch Date: Thu Nov 13 10:24:39 2008 +0100 rpc_broadcast: handle misformed rpcbind replies Some rpcbind implementations seem to return IPv6 uaddrs in response to an IPv4 broadcast (which is probably due to their using a single v6 socket to handle both v6 and v4 requests). We can either discard these replies, or fix them up silently. Here's a patch that implements the latter. Signed-off-by: Olaf Kirch diff --git a/src/clnt_bcast.c b/src/clnt_bcast.c index 899eb76..55efc9d 100644 --- a/src/clnt_bcast.c +++ b/src/clnt_bcast.c @@ -227,6 +227,39 @@ __rpc_broadenable(int af, int s, struct broadif *bip) return 0; } +/* + * Some rpcbind implementations use an IPv6 socket to serve both + * IPv4 and IPv6 messages, but neglect to check for the caller's + * address family when sending broadcast replies. These rpcbind + * implementations return an IPv6 address in reply to an IPv4 + * broadcast. We can either ignore them, or try to patch them up. + */ +static struct netbuf * +__ipv6v4_fixup(struct sockaddr_storage *ss, const char *uaddr) +{ + struct sockaddr_in sin; + struct netbuf *np; + + /* ss is the remote rpcbind server's address */ + if (ss->ss_family != AF_INET) + return NULL; + memcpy(&sin, ss, sizeof(sin)); + + np = __rpc_uaddr2taddr_af(AF_INET6, uaddr); + if (np == NULL) + return NULL; + + /* Overwrite the port with that of the service we + * wanted to talk to. */ + sin.sin_port = ((struct sockaddr_in6 *) np)->sin6_port; + + /* We know netbuf holds a sockaddr_in6, so it can easily + * hold a sockaddr_in as well. */ + memcpy(np->buf, &sin, sizeof(sin)); + np->len = sizeof(sin); + + return np; +} enum clnt_stat rpc_broadcast_exp(prog, vers, proc, xargs, argsp, xresults, resultsp, @@ -607,6 +640,13 @@ rpc_broadcast_exp(prog, vers, proc, xargs, argsp, xresults, resultsp, #endif np = uaddr2taddr( fdlist[i].nconf, uaddrp); + /* Some misguided rpcbind implemenations + * seem to return an IPv6 uaddr in IPv4 + * responses. */ + if (np == NULL) + np = __ipv6v4_fixup( + &fdlist[i].raddr, + uaddrp); if (np != NULL) { done = (*eachresult)(resultsp, np, fdlist[i].nconf);