]> git.decadent.org.uk Git - nfs-utils.git/blob - support/nfs/conn.c
Restore use of un-connected socket for UDP mount requests.
[nfs-utils.git] / support / nfs / conn.c
1 /*
2  * conn.c -- NFS client mount / umount connection code support functions
3  *
4  * 2006-06-06 Amit Gud <agud@redhat.com>
5  * - Moved code snippets to nfs-utils/support/nfs from util-linux/mount/nfsmount.c
6  *
7  */
8
9 #include "config.h"
10 #include <errno.h>
11 #include <unistd.h>
12 #include <rpc/rpc.h>
13 #include <rpc/pmap_prot.h>
14 #include <rpc/pmap_clnt.h>
15 #include <sys/socket.h>
16 #include <sys/stat.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19
20 #include "conn.h"
21
22 #if SIZEOF_SOCKLEN_T - 0 == 0
23 #define socklen_t int
24 #endif
25
26 extern int verbose;
27
28 /* Map an NFS version into the corresponding Mountd version */
29 u_long nfsvers_to_mnt(const u_long vers)
30 {
31         static const u_long nfs_to_mnt[] = { 0, 0, 1, 3 };
32         if (vers <= 3)
33                 return nfs_to_mnt[vers];
34         return 0;
35 }
36
37 /* Map a Mountd version into the corresponding NFS version */
38 u_long mntvers_to_nfs(const u_long vers)
39 {
40         static const u_long mnt_to_nfs[] = { 0, 2, 2, 3 };
41         if (vers <= 3)
42                 return mnt_to_nfs[vers];
43         return 0;
44 }
45
46 /*
47  * Create a socket that is locally bound to a 
48  * reserve or non-reserve port. For any failures,
49  * RPC_ANYSOCK is returned which will cause 
50  * the RPC code to create the socket instead. 
51  */
52 int get_socket(struct sockaddr_in *saddr, u_int p_prot, int resvp, int conn)
53 {
54         int so, cc, type;
55         struct sockaddr_in laddr;
56         socklen_t namelen = sizeof(laddr);
57
58         type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
59         if ((so = socket (AF_INET, type, p_prot)) < 0) {
60                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
61                 rpc_createerr.cf_error.re_errno = errno;
62                 if (verbose) {
63                         fprintf(stderr, 
64                                 "mount: Unable to create %s socket: errno %d (%s)\n",
65                                 p_prot == IPPROTO_UDP ? "UDP" : "TCP", 
66                                 errno, strerror(errno));
67                 }
68                 return RPC_ANYSOCK;
69         }
70         laddr.sin_family = AF_INET;
71         laddr.sin_port = 0;
72         laddr.sin_addr.s_addr = htonl(INADDR_ANY);
73         if (resvp) {
74                 if (bindresvport(so, &laddr) < 0) {
75                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
76                         rpc_createerr.cf_error.re_errno = errno;
77                         if (verbose) {
78                                 fprintf(stderr, 
79                                         "mount: Unable to bindresvport %s socket: errno %d (%s)\n",
80                                         p_prot == IPPROTO_UDP ? "UDP" : "TCP", 
81                                         errno, strerror(errno));
82                         }
83                         close(so);
84                         return RPC_ANYSOCK;
85                 }
86         } else {
87                 cc = bind(so, (struct sockaddr *)&laddr, namelen);
88                 if (cc < 0) {
89                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
90                         rpc_createerr.cf_error.re_errno = errno;
91                         if (verbose) {
92                                 fprintf(stderr, 
93                                         "mount: Unable to bind to %s socket: errno %d (%s)\n",
94                                         p_prot == IPPROTO_UDP ? "UDP" : "TCP", 
95                                         errno, strerror(errno));
96                         }
97                         close(so);
98                         return RPC_ANYSOCK;
99                 }
100         }
101         if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
102                 cc = connect(so, (struct sockaddr *)saddr, namelen);
103                 if (cc < 0) {
104                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
105                         rpc_createerr.cf_error.re_errno = errno;
106                         if (verbose) {
107                                 fprintf(stderr, 
108                                         "mount: Unable to connect to %s:%d, errno %d (%s)\n",
109                                         inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port),
110                                         errno, strerror(errno));
111                         }
112                         close(so);
113                         return RPC_ANYSOCK;
114                 }
115         }
116         return so;
117 }
118
119 /*
120  * Sigh... getport() doesn't actually check the version number.
121  * In order to make sure that the server actually supports the service
122  * we're requesting, we open and RPC client, and fire off a NULL
123  * RPC call.
124  */
125 int
126 clnt_ping(struct sockaddr_in *saddr, const u_long prog, const u_long vers,
127           const u_int prot, struct sockaddr_in *caddr)
128 {
129         CLIENT *clnt=NULL;
130         int sock, stat;
131         static char clnt_res;
132         struct sockaddr dissolve;
133
134         rpc_createerr.cf_stat = stat = errno = 0;
135         sock = get_socket(saddr, prot, FALSE, TRUE);
136         if (sock == RPC_ANYSOCK) {
137                 if (errno == ETIMEDOUT) {
138                         /*
139                          * TCP timeout. Bubble up the error to see 
140                          * how it should be handled.
141                          */
142                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
143                 }
144                 return 0;
145         }
146
147         if (caddr) {
148                 /* Get the address of our end of this connection */
149                 socklen_t len = sizeof(*caddr);
150                 if (getsockname(sock, caddr, &len) != 0)
151                         caddr->sin_family = 0;
152         }
153
154         switch(prot) {
155         case IPPROTO_UDP:
156                 /* The socket is connected (so we could getsockname successfully),
157                  * but some servers on multi-homed hosts reply from
158                  * the wrong address, so if we stay connected, we lose the reply.
159                  */
160                 dissolve.sa_family = AF_UNSPEC;
161                 connect(sock, &dissolve, sizeof(dissolve));
162
163                 clnt = clntudp_bufcreate(saddr, prog, vers,
164                                          RETRY_TIMEOUT, &sock,
165                                          RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
166                 break;
167         case IPPROTO_TCP:
168                 clnt = clnttcp_create(saddr, prog, vers, &sock,
169                                       RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
170                 break;
171         }
172         if (!clnt) {
173                 close(sock);
174                 return 0;
175         }
176         memset(&clnt_res, 0, sizeof(clnt_res));
177         stat = clnt_call(clnt, NULLPROC,
178                          (xdrproc_t)xdr_void, (caddr_t)NULL,
179                          (xdrproc_t)xdr_void, (caddr_t)&clnt_res,
180                          TIMEOUT);
181         if (stat) {
182                 clnt_geterr(clnt, &rpc_createerr.cf_error);
183                 rpc_createerr.cf_stat = stat;
184         }
185         clnt_destroy(clnt);
186         close(sock);
187
188         if (stat == RPC_SUCCESS)
189                 return 1;
190         else
191                 return 0;
192 }
193
194 CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
195 {
196         struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
197         struct pmap *mnt_pmap = &mnt_server->pmap;
198         CLIENT *clnt = NULL;
199
200         /* contact the mount daemon via TCP */
201         mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
202         *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, TRUE, FALSE);
203
204         switch (mnt_pmap->pm_prot) {
205         case IPPROTO_UDP:
206                 clnt = clntudp_bufcreate(mnt_saddr,
207                                          mnt_pmap->pm_prog, mnt_pmap->pm_vers,
208                                          RETRY_TIMEOUT, msock,
209                                          MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
210                 break;
211         case IPPROTO_TCP:
212                 clnt = clnttcp_create(mnt_saddr,
213                                       mnt_pmap->pm_prog, mnt_pmap->pm_vers,
214                                       msock,
215                                       MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
216                 break;
217         }
218         if (clnt) {
219                 /* try to mount hostname:dirname */
220                 clnt->cl_auth = authunix_create_default();
221                 return clnt;
222         }
223         return NULL;
224 }
225
226 void mnt_closeclnt(CLIENT *clnt, int msock)
227 {
228         auth_destroy(clnt->cl_auth);
229         clnt_destroy(clnt);
230         close(msock);
231 }
232