}
/*
- * 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)
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;
}
else if (port == 0)
rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
}
- if (socket != 1)
- close(socket);
+ close(socket);
return port;
}
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))
}
/*
+ * 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,
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.
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;
+}