/* * conn.c -- NFS client mount / umount connection code support functions * * 2006-06-06 Amit Gud * - Moved code snippets to nfs-utils/support/nfs from util-linux/mount/nfsmount.c * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "conn.h" #if SIZEOF_SOCKLEN_T - 0 == 0 #define socklen_t int #endif extern int verbose; /* Map an NFS version into the corresponding Mountd version */ u_long nfsvers_to_mnt(const u_long vers) { static const u_long nfs_to_mnt[] = { 0, 0, 1, 3 }; if (vers <= 3) return nfs_to_mnt[vers]; return 0; } /* Map a Mountd version into the corresponding NFS version */ u_long mntvers_to_nfs(const u_long vers) { static const u_long mnt_to_nfs[] = { 0, 2, 2, 3 }; if (vers <= 3) return mnt_to_nfs[vers]; return 0; } /* * Create a socket that is locally bound to a * reserve or non-reserve port. For any failures, * RPC_ANYSOCK is returned which will cause * the RPC code to create the socket instead. */ int get_socket(struct sockaddr_in *saddr, u_int p_prot, int resvp, int conn) { int so, cc, type; struct sockaddr_in laddr; socklen_t namelen = sizeof(laddr); type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM); if ((so = socket (AF_INET, type, p_prot)) < 0) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; if (verbose) { fprintf(stderr, "mount: Unable to create %s socket: errno %d (%s)\n", p_prot == IPPROTO_UDP ? "UDP" : "TCP", errno, strerror(errno)); } return RPC_ANYSOCK; } laddr.sin_family = AF_INET; laddr.sin_port = 0; laddr.sin_addr.s_addr = htonl(INADDR_ANY); if (resvp) { if (bindresvport(so, &laddr) < 0) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; if (verbose) { fprintf(stderr, "mount: Unable to bindresvport %s socket: errno %d (%s)\n", p_prot == IPPROTO_UDP ? "UDP" : "TCP", errno, strerror(errno)); } close(so); return RPC_ANYSOCK; } } else { cc = bind(so, (struct sockaddr *)&laddr, namelen); if (cc < 0) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; if (verbose) { fprintf(stderr, "mount: Unable to bind to %s socket: errno %d (%s)\n", p_prot == IPPROTO_UDP ? "UDP" : "TCP", errno, strerror(errno)); } close(so); return RPC_ANYSOCK; } } if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) { cc = connect(so, (struct sockaddr *)saddr, namelen); if (cc < 0) { rpc_createerr.cf_stat = RPC_SYSTEMERROR; rpc_createerr.cf_error.re_errno = errno; if (verbose) { fprintf(stderr, "mount: Unable to connect to %s:%d, errno %d (%s)\n", inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port), errno, strerror(errno)); } close(so); return RPC_ANYSOCK; } } return so; } /* * Sigh... getport() doesn'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 * RPC call. */ int clnt_ping(struct sockaddr_in *saddr, const u_long prog, const u_long vers, const u_int prot, struct sockaddr_in *caddr) { CLIENT *clnt=NULL; int sock, stat; static char clnt_res; struct sockaddr dissolve; rpc_createerr.cf_stat = stat = errno = 0; sock = get_socket(saddr, prot, FALSE, TRUE); if (sock == RPC_ANYSOCK) { if (errno == ETIMEDOUT) { /* * TCP timeout. Bubble up the error to see * how it should be handled. */ rpc_createerr.cf_stat = RPC_TIMEDOUT; } return 0; } if (caddr) { /* Get the address of our end of this connection */ socklen_t len = sizeof(*caddr); if (getsockname(sock, caddr, &len) != 0) caddr->sin_family = 0; } switch(prot) { case IPPROTO_UDP: /* The socket is connected (so we could getsockname successfully), * but some servers on multi-homed hosts reply from * the wrong address, so if we stay connected, we lose the reply. */ dissolve.sa_family = AF_UNSPEC; connect(sock, &dissolve, sizeof(dissolve)); clnt = clntudp_bufcreate(saddr, prog, vers, RETRY_TIMEOUT, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); break; case IPPROTO_TCP: clnt = clnttcp_create(saddr, prog, vers, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); break; } if (!clnt) { close(sock); return 0; } memset(&clnt_res, 0, sizeof(clnt_res)); stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void, (caddr_t)NULL, (xdrproc_t)xdr_void, (caddr_t)&clnt_res, TIMEOUT); if (stat) { clnt_geterr(clnt, &rpc_createerr.cf_error); rpc_createerr.cf_stat = stat; } clnt_destroy(clnt); close(sock); if (stat == RPC_SUCCESS) return 1; else return 0; } CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock) { struct sockaddr_in *mnt_saddr = &mnt_server->saddr; struct pmap *mnt_pmap = &mnt_server->pmap; CLIENT *clnt = NULL; /* contact the mount daemon via TCP */ mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port); *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, TRUE, FALSE); switch (mnt_pmap->pm_prot) { case IPPROTO_UDP: clnt = clntudp_bufcreate(mnt_saddr, mnt_pmap->pm_prog, mnt_pmap->pm_vers, RETRY_TIMEOUT, msock, MNT_SENDBUFSIZE, MNT_RECVBUFSIZE); break; case IPPROTO_TCP: clnt = clnttcp_create(mnt_saddr, mnt_pmap->pm_prog, mnt_pmap->pm_vers, msock, MNT_SENDBUFSIZE, MNT_RECVBUFSIZE); break; } if (clnt) { /* try to mount hostname:dirname */ clnt->cl_auth = authunix_create_default(); return clnt; } return NULL; } void mnt_closeclnt(CLIENT *clnt, int msock) { auth_destroy(clnt->cl_auth); clnt_destroy(clnt); close(msock); }