/* * network.c -- Provide common network functions for NFS mount/umount * * Copyright (C) 2007 Oracle. All rights reserved. * Copyright (C) 2007 Chuck Lever * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "conn.h" #include "xcommon.h" #include "mount.h" #include "nls.h" #include "nfsumount.h" #include "nfs_mount.h" #include "mount_constants.h" #include "network.h" #ifdef HAVE_RPCSVC_NFS_PROT_H #include #else #include #define nfsstat nfs_stat #endif #ifndef NFS_PORT #define NFS_PORT 2049 #endif extern int nfs_mount_data_version; extern char *progname; extern int verbose; static const unsigned int probe_udp_only[] = { IPPROTO_UDP, 0, }; static const unsigned int probe_udp_first[] = { IPPROTO_UDP, IPPROTO_TCP, 0, }; static const unsigned int probe_tcp_first[] = { IPPROTO_TCP, IPPROTO_UDP, 0, }; static const unsigned long probe_nfs2_only[] = { 2, 0, }; static const unsigned long probe_nfs3_first[] = { 3, 2, 0, }; static const unsigned long probe_mnt1_first[] = { 1, 2, 0, }; static const unsigned long probe_mnt3_first[] = { 3, 1, 2, 0, }; int nfs_gethostbyname(const char *hostname, struct sockaddr_in *saddr) { struct hostent *hp; saddr->sin_family = AF_INET; if (!inet_aton(hostname, &saddr->sin_addr)) { if ((hp = gethostbyname(hostname)) == NULL) { nfs_error(_("mount: can't get address for %s\n"), hostname); return 0; } else { if (hp->h_length > sizeof(*saddr)) { nfs_error(_("mount: got bad hp->h_length\n")); hp->h_length = sizeof(*saddr); } memcpy(&saddr->sin_addr, hp->h_addr, hp->h_length); } } return 1; } /* * getport() is very similar to pmap_getport() with * the exception this version uses a non-reserve ports * instead of reserve ports since reserve ports * are not needed for pmap requests. */ unsigned short getport(struct sockaddr_in *saddr, unsigned long prog, unsigned long vers, unsigned int prot) { unsigned short port = 0; int socket; CLIENT *clnt = NULL; struct pmap parms; enum clnt_stat stat; saddr->sin_port = htons (PMAPPORT); socket = get_socket(saddr, prot, FALSE, FALSE); switch (prot) { case IPPROTO_UDP: clnt = clntudp_bufcreate(saddr, PMAPPROG, PMAPVERS, TIMEOUT, &socket, UDPMSGSIZE, UDPMSGSIZE); break; case IPPROTO_TCP: clnt = clnttcp_create(saddr, PMAPPROG, PMAPVERS, &socket, 50, 500); break; } if (clnt != NULL) { parms.pm_prog = prog; parms.pm_vers = vers; parms.pm_prot = prot; parms.pm_port = 0; /* not needed or used */ 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 (socket != 1) close(socket); return 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. */ static int probe_port(clnt_addr_t *server, const unsigned long *versions, const unsigned int *protos) { struct sockaddr_in *saddr = &server->saddr; struct pmap *pmap = &server->pmap; 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; p_prot = prot ? &prot : protos; p_vers = vers ? &vers : versions; rpc_createerr.cf_stat = 0; for (;;) { saddr->sin_port = htons(PMAPPORT); p_port = getport(saddr, 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)) goto out_ok; if (rpc_createerr.cf_stat == RPC_TIMEDOUT) goto out_bad; } } if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED) goto out_bad; if (!prot) { if (*++p_prot) continue; p_prot = protos; } if (vers == pmap->pm_vers) { p_vers = versions; vers = 0; } 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; } int probe_nfsport(clnt_addr_t *nfs_server) { 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); else return probe_port(nfs_server, probe_nfs2_only, probe_udp_only); } int probe_mntport(clnt_addr_t *mnt_server) { 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); else return probe_port(mnt_server, probe_mnt1_first, probe_udp_only); } int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server) { struct pmap *nfs_pmap = &nfs_server->pmap; struct pmap *mnt_pmap = &mnt_server->pmap; struct pmap save_nfs, save_mnt; int res; const unsigned long *probe_vers; if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers) nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers); else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers) mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers); if (nfs_pmap->pm_vers) goto version_fixed; memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs)); memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt)); probe_vers = (nfs_mount_data_version >= 4) ? probe_mnt3_first : probe_mnt1_first; for (; *probe_vers; probe_vers++) { nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers); if ((res = probe_nfsport(nfs_server) != 0)) { mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers); if ((res = probe_mntport(mnt_server)) != 0) return 1; memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap)); } switch (rpc_createerr.cf_stat) { case RPC_PROGVERSMISMATCH: case RPC_PROGNOTREGISTERED: break; default: goto out_bad; } memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap)); } out_bad: return 0; version_fixed: if (!probe_nfsport(nfs_server)) goto out_bad; return probe_mntport(mnt_server); } static int probe_statd(void) { struct sockaddr_in addr; unsigned short port; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); port = getport(&addr, 100024, 1, IPPROTO_UDP); if (port == 0) return 0; addr.sin_port = htons(port); if (clnt_ping(&addr, 100024, 1, IPPROTO_UDP, NULL) <= 0) return 0; return 1; } /* * Attempt to start rpc.statd */ int start_statd(void) { #ifdef START_STATD struct stat stb; #endif if (probe_statd()) return 1; #ifdef START_STATD if (stat(START_STATD, &stb) == 0) { if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) { system(START_STATD); if (probe_statd()) return 1; } } #endif return 0; }