/* * 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 nfs_probe_port(const struct sockaddr *sap, const socklen_t salen, struct pmap *pmap, const unsigned long *versions, const unsigned int *protos) { 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 = nfs_getport(saddr, salen, prog, *p_vers, *p_prot); if (p_port) { if (!port || port == p_port) { 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; } } if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED && rpc_createerr.cf_stat != RPC_TIMEDOUT && rpc_createerr.cf_stat != RPC_CANTRECV && rpc_createerr.cf_stat != RPC_PROGVERSMISMATCH) goto out_bad; if (!prot) { if (*++p_prot) continue; p_prot = protos; } if (rpc_createerr.cf_stat == RPC_TIMEDOUT || rpc_createerr.cf_stat == RPC_CANTRECV) goto out_bad; if (vers || !*++p_vers) break; } out_bad: return 0; out_ok: if (!vers) pmap->pm_vers = *p_vers; if (!prot) pmap->pm_prot = *p_prot; if (!port) pmap->pm_port = p_port; rpc_createerr.cf_stat = 0; return 1; }
/* * If the port isn't already set, do an rpcbind query to the remote server * using the program and version and get the port. * * Newer kernels send the value of the port= mount option in the "info" * file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value * of the port= option or '2049'. The port field in a new sockaddr should * reflect the value that was sent by the kernel. */ static int populate_port(struct sockaddr *sa, const socklen_t salen, const rpcprog_t program, const rpcvers_t version, const unsigned short protocol) { struct sockaddr_in *s4 = (struct sockaddr_in *) sa; #ifdef IPV6_SUPPORTED struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa; #endif /* IPV6_SUPPORTED */ unsigned short port; /* * Newer kernels send the port in the upcall. If we already have * the port, there's no need to look it up. */ switch (sa->sa_family) { case AF_INET: if (s4->sin_port != 0) { printerr(4, "DEBUG: port already set to %d\n", ntohs(s4->sin_port)); return 1; } break; #ifdef IPV6_SUPPORTED case AF_INET6: if (s6->sin6_port != 0) { printerr(4, "DEBUG: port already set to %d\n", ntohs(s6->sin6_port)); return 1; } break; #endif /* IPV6_SUPPORTED */ default: printerr(0, "ERROR: unsupported address family %d\n", sa->sa_family); return 0; } /* * Newer kernels that send the port in the upcall set the value to * 2049 for NFSv4 mounts when one isn't specified. The check below is * only for kernels that don't send the port in the upcall. For those * we either have to do an rpcbind query or set it to the standard * port. Doing a query could be problematic (firewalls, etc), so take * the latter approach. */ if (program == 100003 && version == 4) { port = 2049; goto set_port; } port = nfs_getport(sa, salen, program, version, protocol); if (!port) { printerr(0, "ERROR: unable to obtain port for prog %ld " "vers %ld\n", program, version); return 0; } set_port: printerr(2, "DEBUG: setting port to %hu for prog %lu vers %lu\n", port, program, version); switch (sa->sa_family) { case AF_INET: s4->sin_port = htons(port); break; #ifdef IPV6_SUPPORTED case AF_INET6: s6->sin6_port = htons(port); break; #endif /* IPV6_SUPPORTED */ } return 1; }