+/*
+ * 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(2, "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(2, "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;
+}
+