When connecting to an NFSv4 server we need to find out IP address
as it would be seen by the server, to register an address for
callbacks.
This is most easily done by connecting the socket to the
servers address and then getting the address of our endpoint.
However with a connected UDP socket, replies that come from a
different IP address - as can happen with non-Linux multi-homed
servers - will be rejected.
So if we connected our UDP socket, we need to be sure to
disconnect it before using it.
This patch adds an option to get_socket to say if we want it
connected or not and, in the case where we do, we disconnect
a UDP socket after the connection information has been used.
Also clean up the error handling in clnt_ping which was getting
clumsy.
struct sockaddr_in *);
u_long nfsvers_to_mnt(const u_long);
u_long mntvers_to_nfs(const u_long);
struct sockaddr_in *);
u_long nfsvers_to_mnt(const u_long);
u_long mntvers_to_nfs(const u_long);
-int get_socket(struct sockaddr_in *, u_int, int);
+int get_socket(struct sockaddr_in *, u_int, int, int);
CLIENT * mnt_openclnt(clnt_addr_t *, int *);
void mnt_closeclnt(CLIENT *, int);
CLIENT * mnt_openclnt(clnt_addr_t *, int *);
void mnt_closeclnt(CLIENT *, int);
* RPC_ANYSOCK is returned which will cause
* the RPC code to create the socket instead.
*/
* 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 get_socket(struct sockaddr_in *saddr, u_int p_prot, int resvp, int conn)
{
int so, cc, type;
struct sockaddr_in laddr;
{
int so, cc, type;
struct sockaddr_in laddr;
- if (type == SOCK_STREAM || type == SOCK_DGRAM) {
+ if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
cc = connect(so, (struct sockaddr *)saddr, namelen);
if (cc < 0) {
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
cc = connect(so, (struct sockaddr *)saddr, namelen);
if (cc < 0) {
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
CLIENT *clnt=NULL;
int sock, stat;
static char clnt_res;
CLIENT *clnt=NULL;
int sock, stat;
static char clnt_res;
+ struct sockaddr dissolve;
rpc_createerr.cf_stat = stat = errno = 0;
rpc_createerr.cf_stat = stat = errno = 0;
- sock = get_socket(saddr, prot, FALSE);
- if (sock == RPC_ANYSOCK && errno == ETIMEDOUT) {
- /*
- * TCP timeout. Bubble up the error to see
- * how it should be handled.
- */
- rpc_createerr.cf_stat = RPC_TIMEDOUT;
- goto out_bad;
+ 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:
}
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);
clnt = clntudp_bufcreate(saddr, prog, vers,
RETRY_TIMEOUT, &sock,
RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
clnt = clnttcp_create(saddr, prog, vers, &sock,
RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
break;
clnt = clnttcp_create(saddr, prog, vers, &sock,
RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
break;
- default:
- goto out_bad;
- if (!clnt)
- goto out_bad;
+ 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,
memset(&clnt_res, 0, sizeof(clnt_res));
stat = clnt_call(clnt, NULLPROC,
(xdrproc_t)xdr_void, (caddr_t)NULL,
rpc_createerr.cf_stat = stat;
}
clnt_destroy(clnt);
rpc_createerr.cf_stat = stat;
}
clnt_destroy(clnt);
- if (sock != -1) {
- 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;
- }
- close(sock);
- }
if (stat == RPC_SUCCESS)
return 1;
if (stat == RPC_SUCCESS)
return 1;
}
CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
}
CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
/* contact the mount daemon via TCP */
mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
/* 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);
+ *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, TRUE, FALSE);
switch (mnt_pmap->pm_prot) {
case IPPROTO_UDP:
switch (mnt_pmap->pm_prot) {
case IPPROTO_UDP:
enum clnt_stat stat;
saddr->sin_port = htons (PMAPPORT);
enum clnt_stat stat;
saddr->sin_port = htons (PMAPPORT);
- socket = get_socket(saddr, prot, FALSE);
+ socket = get_socket(saddr, prot, FALSE, FALSE);
switch (prot) {
case IPPROTO_UDP:
switch (prot) {
case IPPROTO_UDP: