0,
};
+static void nfs_set_port(struct sockaddr *sap, const unsigned short port)
+{
+ switch (sap->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)sap)->sin_port = htons(port);
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)sap)->sin6_port = htons(port);
+ break;
+ default:
+ nfs_error(_("%s: unrecognized address family in %s"),
+ progname, __func__);
+ }
+}
+
/**
* nfs_name_to_address - resolve hostname to an IPv4 or IPv6 socket address
* @hostname: pointer to C string containing DNS hostname to resolve
return RPC_ANYSOCK;
}
-/*
- * getport() is very similar to pmap_getport() with the exception that
- * this version tries to use an ephemeral port, since reserved ports are
- * not needed for GETPORT queries. This conserves the very limited
- * reserved port space, which helps reduce failed socket binds
- * during mount storms.
- *
- * A side effect of calling this function is that rpccreateerr is set.
- */
-static unsigned short getport(struct sockaddr_in *saddr,
- unsigned long program,
- unsigned long version,
- unsigned int proto)
+static void nfs_pp_debug(const struct sockaddr *sap, const socklen_t salen,
+ const rpcprog_t program, const rpcvers_t version,
+ const unsigned short protocol,
+ const unsigned short port)
{
- struct sockaddr_in bind_saddr;
- unsigned short port = 0;
- int socket;
- CLIENT *clnt = NULL;
- enum clnt_stat stat;
-
- bind_saddr = *saddr;
- bind_saddr.sin_port = htons(PMAPPORT);
-
- socket = get_socket(&bind_saddr, proto, PMAP_TIMEOUT, FALSE, TRUE);
- if (socket == RPC_ANYSOCK) {
- if (proto == IPPROTO_TCP &&
- rpc_createerr.cf_error.re_errno == ETIMEDOUT)
- rpc_createerr.cf_stat = RPC_TIMEDOUT;
- return 0;
- }
+ char buf[NI_MAXHOST];
- switch (proto) {
- case IPPROTO_UDP:
- clnt = clntudp_bufcreate(&bind_saddr,
- PMAPPROG, PMAPVERS,
- RETRY_TIMEOUT, &socket,
- RPCSMALLMSGSIZE,
- RPCSMALLMSGSIZE);
- break;
- case IPPROTO_TCP:
- clnt = clnttcp_create(&bind_saddr,
- PMAPPROG, PMAPVERS,
- &socket,
- RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
- break;
- }
- if (clnt != NULL) {
- struct pmap parms = {
- .pm_prog = program,
- .pm_vers = version,
- .pm_prot = proto,
- };
-
- stat = clnt_call(clnt, PMAPPROC_GETPORT,
- (xdrproc_t)xdr_pmap, (caddr_t)&parms,
- (xdrproc_t)xdr_u_short, (caddr_t)&port,
- TIMEOUT);
- if (stat) {
- clnt_geterr(clnt, &rpc_createerr.cf_error);
- rpc_createerr.cf_stat = stat;
- }
- clnt_destroy(clnt);
- if (stat != RPC_SUCCESS)
- port = 0;
- else if (port == 0)
- rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+ if (!verbose)
+ return;
+
+ if (nfs_present_sockaddr(sap, salen, buf, sizeof(buf)) == 0) {
+ buf[0] = '\0';
+ strcat(buf, "unknown host");
}
- close(socket);
- return port;
+ fprintf(stderr, _("%s: trying %s prog %ld vers %ld prot %s port %d\n"),
+ progname, buf, program, version,
+ (protocol == IPPROTO_UDP ? _("UDP") : _("TCP")),
+ port);
}
/*
* Use the portmapper to discover whether or not the service we want is
* available. The lists 'versions' and 'protos' define ordered sequences
* of service versions and udp/tcp protocols to probe for.
+ *
+ * Returns 1 if the requested service port is unambiguous and pingable;
+ * @pmap is filled in with the version, port, and transport protocol used
+ * during the successful ping. Note that if a port is already specified
+ * in @pmap and it matches the rpcbind query result, nfs_probe_port() does
+ * not perform an RPC ping.
+ *
+ * If an error occurs or the requested service isn't available, zero is
+ * returned; rpccreateerr.cf_stat is set to reflect the nature of the error.
*/
-static int probe_port(clnt_addr_t *server, const unsigned long *versions,
- const unsigned int *protos)
+static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
+ struct pmap *pmap, const unsigned long *versions,
+ const unsigned int *protos)
{
- struct sockaddr_in *saddr = &server->saddr;
- struct pmap *pmap = &server->pmap;
+ struct sockaddr_storage address;
+ struct sockaddr *saddr = (struct sockaddr *)&address;
const unsigned long prog = pmap->pm_prog, *p_vers;
const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
const u_short port = (u_short) pmap->pm_port;
unsigned long vers = pmap->pm_vers;
unsigned short p_port;
+ memcpy(saddr, sap, salen);
p_prot = prot ? &prot : protos;
p_vers = vers ? &vers : versions;
rpc_createerr.cf_stat = 0;
+
for (;;) {
- p_port = getport(saddr, prog, *p_vers, *p_prot);
+ p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot);
if (p_port) {
if (!port || port == p_port) {
- saddr->sin_port = htons(p_port);
- if (verbose) {
- printf(_("%s: trying %s prog %ld vers "
- "%ld prot %s port %d\n"),
- progname,
- inet_ntoa(saddr->sin_addr),
- prog, *p_vers,
- *p_prot == IPPROTO_UDP ?
- _("UDP") : _("TCP"),
- p_port);
- }
- if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL))
+ nfs_set_port(saddr, p_port);
+ nfs_pp_debug(saddr, salen, prog, *p_vers,
+ *p_prot, p_port);
+ if (nfs_rpc_ping(saddr, salen, prog,
+ *p_vers, *p_prot, NULL))
goto out_ok;
}
}
static int probe_nfsport(clnt_addr_t *nfs_server)
{
+ struct sockaddr *sap = (struct sockaddr *)&nfs_server->saddr;
+ socklen_t salen = sizeof(nfs_server->saddr);
struct pmap *pmap = &nfs_server->pmap;
if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
return 1;
if (nfs_mount_data_version >= 4)
- return probe_port(nfs_server, probe_nfs3_first, probe_tcp_first);
+ return nfs_probe_port(sap, salen, pmap,
+ probe_nfs3_first, probe_tcp_first);
else
- return probe_port(nfs_server, probe_nfs2_only, probe_udp_only);
+ return nfs_probe_port(sap, salen, pmap,
+ probe_nfs2_only, probe_udp_only);
}
static int probe_mntport(clnt_addr_t *mnt_server)
{
+ struct sockaddr *sap = (struct sockaddr *)&mnt_server->saddr;
+ socklen_t salen = sizeof(mnt_server->saddr);
struct pmap *pmap = &mnt_server->pmap;
if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
return 1;
if (nfs_mount_data_version >= 4)
- return probe_port(mnt_server, probe_mnt3_first, probe_udp_first);
+ return nfs_probe_port(sap, salen, pmap,
+ probe_mnt3_first, probe_udp_first);
else
- return probe_port(mnt_server, probe_mnt1_first, probe_udp_only);
+ return nfs_probe_port(sap, salen, pmap,
+ probe_mnt1_first, probe_udp_only);
}
/**
* @prot: target RPC protocol
* @caddr: filled in with our network address
*
- * Sigh... getport() doesn't actually check the version number.
+ * Sigh... GETPORT queries don't actually check the version number.
* In order to make sure that the server actually supports the service
- * we're requesting, we open and RPC client, and fire off a NULL
+ * we're requesting, we open an RPC client, and fire off a NULL
* RPC call.
*
* caddr is the network address that the server will use to call us back.