X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fmount%2Fnetwork.c;h=5a56c23ce20f9b9ca6333d9f1e08f837a8d18925;hp=4831990c2b6c305246c61937e93c2181c3735bc2;hb=7f8fae8cd60025f7fa1b446da363dcc2e1fb5b6a;hpb=12e990f4b07172c693203ffd1e12d0518cacb9e6 diff --git a/utils/mount/network.c b/utils/mount/network.c index 4831990..5a56c23 100644 --- a/utils/mount/network.c +++ b/utils/mount/network.c @@ -34,7 +34,6 @@ #include #include -#include "conn.h" #include "xcommon.h" #include "mount.h" #include "nls.h" @@ -53,6 +52,10 @@ #define NFS_PORT 2049 #endif +#define PMAP_TIMEOUT (10) +#define CONNECT_TIMEOUT (20) +#define MOUNT_TIMEOUT (30) + #if SIZEOF_SOCKLEN_T - 0 == 0 #define socklen_t unsigned int #endif @@ -159,76 +162,131 @@ int nfs_gethostbyname(const char *hostname, struct sockaddr_in *saddr) } /* - * Create a socket that is locally bound to a reserved or non-reserved - * port. For any failures, RPC_ANYSOCK is returned which will cause - * the RPC code to create the socket instead. + * Attempt to connect a socket, but time out after "timeout" seconds. + * + * On error return, caller closes the socket. + */ +static int connect_to(int fd, struct sockaddr *addr, + socklen_t addrlen, int timeout) +{ + int ret, saved; + fd_set rset, wset; + struct timeval tv = { + .tv_sec = timeout, + }; + + saved = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, saved | O_NONBLOCK); + + ret = connect(fd, addr, addrlen); + if (ret < 0 && errno != EINPROGRESS) + return -1; + if (ret == 0) + goto out; + + FD_ZERO(&rset); + FD_SET(fd, &rset); + wset = rset; + ret = select(fd + 1, &rset, &wset, NULL, &tv); + if (ret == 0) { + errno = ETIMEDOUT; + return -1; + } + if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) { + int error; + socklen_t len = sizeof(error); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) + return -1; + if (error) { + errno = error; + return -1; + } + } else + return -1; + +out: + fcntl(fd, F_SETFL, saved); + return 0; +} + +/* + * Create a socket that is locally bound to a reserved or non-reserved port. + * + * The caller should check rpc_createerr to determine the cause of any error. */ static int get_socket(struct sockaddr_in *saddr, unsigned int p_prot, - int resvp, int conn) + unsigned int timeout, 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; - } + if ((so = socket (AF_INET, type, p_prot)) < 0) + goto err_socket; + 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; - } + if (bindresvport(so, &laddr) < 0) + goto err_bindresvport; } 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 (cc < 0) + goto err_bind; } 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; - } + cc = connect_to(so, (struct sockaddr *)saddr, namelen, + timeout); + if (cc < 0) + goto err_connect; } return so; + +err_socket: + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + if (verbose) { + nfs_error(_("%s: Unable to create %s socket: errno %d (%s)\n"), + progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"), + errno, strerror(errno)); + } + return RPC_ANYSOCK; + +err_bindresvport: + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + if (verbose) { + nfs_error(_("%s: Unable to bindresvport %s socket: errno %d" + " (%s)\n"), + progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"), + errno, strerror(errno)); + } + close(so); + return RPC_ANYSOCK; + +err_bind: + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + if (verbose) { + nfs_error(_("%s: Unable to bind to %s socket: errno %d (%s)\n"), + progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"), + errno, strerror(errno)); + } + close(so); + return RPC_ANYSOCK; + +err_connect: + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + if (verbose) { + nfs_error(_("%s: Unable to connect to %s:%d, errno %d (%s)\n"), + progname, inet_ntoa(saddr->sin_addr), + ntohs(saddr->sin_port), errno, strerror(errno)); + } + close(so); + return RPC_ANYSOCK; } /* @@ -252,19 +310,11 @@ static unsigned short getport(struct sockaddr_in *saddr, saddr->sin_port = htons(PMAPPORT); - /* - * Try to get a socket with a non-privileged port. - * clnt*create() will create one anyway if this - * fails. - */ - socket = get_socket(saddr, proto, FALSE, FALSE); + socket = get_socket(saddr, proto, PMAP_TIMEOUT, FALSE, FALSE); if (socket == RPC_ANYSOCK) { - if (proto == IPPROTO_TCP && errno == ETIMEDOUT) { - /* - * TCP SYN timed out, so exit now. - */ + if (proto == IPPROTO_TCP && + rpc_createerr.cf_error.re_errno == ETIMEDOUT) rpc_createerr.cf_stat = RPC_TIMEDOUT; - } return 0; } @@ -302,8 +352,7 @@ static unsigned short getport(struct sockaddr_in *saddr, else if (port == 0) rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; } - if (socket != 1) - close(socket); + close(socket); return port; } @@ -328,7 +377,6 @@ static int probe_port(clnt_addr_t *server, const unsigned long *versions, 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) { @@ -548,7 +596,8 @@ CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock) CLIENT *clnt = NULL; mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port); - *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, TRUE, FALSE); + *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT, + TRUE, FALSE); if (*msock == RPC_ANYSOCK) { if (rpc_createerr.cf_error.re_errno == EADDRINUSE) /* @@ -603,10 +652,10 @@ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog, static char clnt_res; struct sockaddr dissolve; - rpc_createerr.cf_stat = stat = errno = 0; - sock = get_socket(saddr, prot, FALSE, TRUE); + rpc_createerr.cf_stat = stat = 0; + sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE); if (sock == RPC_ANYSOCK) { - if (errno == ETIMEDOUT) { + if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) { /* * TCP timeout. Bubble up the error to see * how it should be handled.