X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=utils%2Fmount%2Fnetwork.c;h=16b802de56acddac4727a5195eb1c53345aa9786;hb=be77debb799a97bab5c7ecf9f1082f967a692eef;hp=89cb976d0fda0309666853d6a7d3baf510827eea;hpb=1f5420e7ecb19022236dfe7eb54dea7d508c04c8;p=nfs-utils.git diff --git a/utils/mount/network.c b/utils/mount/network.c index 89cb976..16b802d 100644 --- a/utils/mount/network.c +++ b/utils/mount/network.c @@ -210,9 +210,9 @@ out: } /* - * 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. + * 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, unsigned int timeout, int resvp, int conn) @@ -303,39 +303,35 @@ static unsigned short getport(struct sockaddr_in *saddr, unsigned long version, unsigned int proto) { + struct sockaddr_in bind_saddr; unsigned short port = 0; int socket; CLIENT *clnt = NULL; enum clnt_stat stat; + + bind_saddr = *saddr; + bind_saddr.sin_port = htons(PMAPPORT); - 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, PMAP_TIMEOUT, FALSE, FALSE); + socket = get_socket(&bind_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; } switch (proto) { case IPPROTO_UDP: - clnt = clntudp_bufcreate(saddr, + clnt = clntudp_bufcreate(&bind_saddr, PMAPPROG, PMAPVERS, RETRY_TIMEOUT, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); break; case IPPROTO_TCP: - clnt = clnttcp_create(saddr, PMAPPROG, PMAPVERS, &socket, + clnt = clnttcp_create(&bind_saddr, + PMAPPROG, PMAPVERS, + &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); break; } @@ -360,8 +356,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; } @@ -397,7 +392,7 @@ static int probe_port(clnt_addr_t *server, const unsigned long *versions, inet_ntoa(saddr->sin_addr), prog, *p_vers, *p_prot == IPPROTO_UDP ? - "udp" : "tcp", + _("UDP") : _("TCP"), p_port); } if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL)) @@ -647,10 +642,23 @@ void mnt_closeclnt(CLIENT *clnt, int msock) } /* + * clnt_ping - send an RPC ping to the remote RPC service endpoint + * @saddr: server's address + * @prog: target RPC program number + * @vers: target RPC version number + * @prot: target RPC protocol + * @caddr: filled in with our network address + * * 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. + * + * caddr is the network address that the server will use to call us back. + * On multi-homed clients, this address depends on which NIC we use to + * route requests to the server. + * + * Returns one if successful, otherwise zero. */ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog, const unsigned long vers, const unsigned int prot, @@ -661,10 +669,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; + 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. @@ -720,3 +728,36 @@ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog, else return 0; } + +/* + * get_client_address - acquire our local network address + * @saddr: server's address + * @caddr: filled in with our network address + * + * Discover a network address that the server will use to call us back. + * On multi-homed clients, this address depends on which NIC we use to + * route requests to the server. + * + * Use a connected datagram socket so as not to leave a socket in TIME_WAIT. + * + * Returns one if successful, otherwise zero. + */ +int get_client_address(struct sockaddr_in *saddr, struct sockaddr_in *caddr) +{ + socklen_t len = sizeof(*caddr); + int socket, err; + + socket = get_socket(saddr, IPPROTO_UDP, CONNECT_TIMEOUT, FALSE, TRUE); + if (socket == RPC_ANYSOCK) + return 0; + + err = getsockname(socket, caddr, &len); + close(socket); + + if (err && verbose) { + nfs_error(_("%s: getsockname failed: %s"), + progname, strerror(errno)); + return 0; + } + return 1; +}