]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/network.c
mount.nfs: Move network functions into a common source module
[nfs-utils.git] / utils / mount / network.c
1 /*
2  * network.c -- Provide common network functions for NFS mount/umount
3  *
4  * Copyright (C) 2007 Oracle.  All rights reserved.
5  * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public
18  * License along with this program; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 021110-1307, USA.
21  *
22  */
23
24 #include <ctype.h>
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <netdb.h>
31 #include <time.h>
32 #include <rpc/rpc.h>
33 #include <rpc/pmap_prot.h>
34 #include <rpc/pmap_clnt.h>
35 #include <sys/socket.h>
36
37 #include "conn.h"
38 #include "xcommon.h"
39 #include "mount.h"
40 #include "nls.h"
41 #include "nfsumount.h"
42 #include "nfs_mount.h"
43 #include "mount_constants.h"
44 #include "network.h"
45
46 #ifdef HAVE_RPCSVC_NFS_PROT_H
47 #include <rpcsvc/nfs_prot.h>
48 #else
49 #include <linux/nfs.h>
50 #define nfsstat nfs_stat
51 #endif
52
53 #ifndef NFS_PORT
54 #define NFS_PORT 2049
55 #endif
56
57 extern int nfs_mount_data_version;
58 extern char *progname;
59 extern int verbose;
60
61 static const unsigned int probe_udp_only[] = {
62         IPPROTO_UDP,
63         0,
64 };
65
66 static const unsigned int probe_udp_first[] = {
67         IPPROTO_UDP,
68         IPPROTO_TCP,
69         0,
70 };
71
72 static const unsigned int probe_tcp_first[] = {
73         IPPROTO_TCP,
74         IPPROTO_UDP,
75         0,
76 };
77
78 static const unsigned long probe_nfs2_only[] = {
79         2,
80         0,
81 };
82
83 static const unsigned long probe_nfs3_first[] = {
84         3,
85         2,
86         0,
87 };
88
89 static const unsigned long probe_mnt1_first[] = {
90         1,
91         2,
92         0,
93 };
94
95 static const unsigned long probe_mnt3_first[] = {
96         3,
97         1,
98         2,
99         0,
100 };
101
102 int nfs_gethostbyname(const char *hostname, struct sockaddr_in *saddr)
103 {
104         struct hostent *hp;
105
106         saddr->sin_family = AF_INET;
107         if (!inet_aton(hostname, &saddr->sin_addr)) {
108                 if ((hp = gethostbyname(hostname)) == NULL) {
109                         nfs_error(_("mount: can't get address for %s\n"),
110                                 hostname);
111                         return 0;
112                 } else {
113                         if (hp->h_length > sizeof(*saddr)) {
114                                 nfs_error(_("mount: got bad hp->h_length\n"));
115                                 hp->h_length = sizeof(*saddr);
116                         }
117                         memcpy(&saddr->sin_addr, hp->h_addr, hp->h_length);
118                 }
119         }
120         return 1;
121 }
122
123 /*
124  * getport() is very similar to pmap_getport() with
125  * the exception this version uses a non-reserve ports
126  * instead of reserve ports since reserve ports
127  * are not needed for pmap requests.
128  */
129 unsigned short getport(struct sockaddr_in *saddr, unsigned long prog,
130                         unsigned long vers, unsigned int prot)
131 {
132         unsigned short port = 0;
133         int socket;
134         CLIENT *clnt = NULL;
135         struct pmap parms;
136         enum clnt_stat stat;
137
138         saddr->sin_port = htons (PMAPPORT);
139         socket = get_socket(saddr, prot, FALSE, FALSE);
140
141         switch (prot) {
142         case IPPROTO_UDP:
143                 clnt = clntudp_bufcreate(saddr,
144                                          PMAPPROG, PMAPVERS, TIMEOUT, &socket,
145                                          UDPMSGSIZE, UDPMSGSIZE);
146                 break;
147         case IPPROTO_TCP:
148                 clnt = clnttcp_create(saddr,
149                         PMAPPROG, PMAPVERS, &socket, 50, 500);
150                 break;
151         }
152         if (clnt != NULL) {
153                 parms.pm_prog = prog;
154                 parms.pm_vers = vers;
155                 parms.pm_prot = prot;
156                 parms.pm_port = 0;    /* not needed or used */
157
158                 stat = clnt_call(clnt, PMAPPROC_GETPORT, (xdrproc_t)xdr_pmap,
159                         (caddr_t)&parms, (xdrproc_t)xdr_u_short, (caddr_t)&port, TIMEOUT);
160                 if (stat) {
161                         clnt_geterr(clnt, &rpc_createerr.cf_error);
162                         rpc_createerr.cf_stat = stat;
163                 }
164                 clnt_destroy(clnt);
165                 if (stat != RPC_SUCCESS)
166                         port = 0;
167                 else if (port == 0)
168                         rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
169         }
170         if (socket != 1)
171                 close(socket);
172
173         return port;
174 }
175
176 /*
177  * Use the portmapper to discover whether or not the service we want is
178  * available. The lists 'versions' and 'protos' define ordered sequences
179  * of service versions and udp/tcp protocols to probe for.
180  */
181 static int probe_port(clnt_addr_t *server, const unsigned long *versions,
182                         const unsigned int *protos)
183 {
184         struct sockaddr_in *saddr = &server->saddr;
185         struct pmap *pmap = &server->pmap;
186         const unsigned long prog = pmap->pm_prog, *p_vers;
187         const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
188         const u_short port = (u_short) pmap->pm_port;
189         unsigned long vers = pmap->pm_vers;
190         unsigned short p_port;
191
192         p_prot = prot ? &prot : protos;
193         p_vers = vers ? &vers : versions;
194         rpc_createerr.cf_stat = 0;
195         for (;;) {
196                 saddr->sin_port = htons(PMAPPORT);
197                 p_port = getport(saddr, prog, *p_vers, *p_prot);
198                 if (p_port) {
199                         if (!port || port == p_port) {
200                                 saddr->sin_port = htons(p_port);
201                                 if (verbose) {
202                                         printf(_("%s: trying %s prog %ld vers "
203                                                 "%ld prot %s port %d\n"),
204                                                 progname,
205                                                 inet_ntoa(saddr->sin_addr),
206                                                 prog, *p_vers,
207                                                 *p_prot == IPPROTO_UDP ?
208                                                         "udp" : "tcp",
209                                                 p_port);
210                                 }
211                                 if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL))
212                                         goto out_ok;
213                                 if (rpc_createerr.cf_stat == RPC_TIMEDOUT)
214                                         goto out_bad;
215                         }
216                 }
217                 if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED)
218                         goto out_bad;
219
220                 if (!prot) {
221                         if (*++p_prot)
222                                 continue;
223                         p_prot = protos;
224                 }
225                 if (vers == pmap->pm_vers) {
226                         p_vers = versions;
227                         vers = 0;
228                 }
229                 if (vers || !*++p_vers)
230                         break;
231         }
232
233 out_bad:
234         return 0;
235
236 out_ok:
237         if (!vers)
238                 pmap->pm_vers = *p_vers;
239         if (!prot)
240                 pmap->pm_prot = *p_prot;
241         if (!port)
242                 pmap->pm_port = p_port;
243         rpc_createerr.cf_stat = 0;
244         return 1;
245 }
246
247 int probe_nfsport(clnt_addr_t *nfs_server)
248 {
249         struct pmap *pmap = &nfs_server->pmap;
250
251         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
252                 return 1;
253
254         if (nfs_mount_data_version >= 4)
255                 return probe_port(nfs_server, probe_nfs3_first, probe_tcp_first);
256         else
257                 return probe_port(nfs_server, probe_nfs2_only, probe_udp_only);
258 }
259
260 int probe_mntport(clnt_addr_t *mnt_server)
261 {
262         struct pmap *pmap = &mnt_server->pmap;
263
264         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
265                 return 1;
266
267         if (nfs_mount_data_version >= 4)
268                 return probe_port(mnt_server, probe_mnt3_first, probe_udp_first);
269         else
270                 return probe_port(mnt_server, probe_mnt1_first, probe_udp_only);
271 }
272
273 int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
274 {
275         struct pmap *nfs_pmap = &nfs_server->pmap;
276         struct pmap *mnt_pmap = &mnt_server->pmap;
277         struct pmap save_nfs, save_mnt;
278         int res;
279         const unsigned long *probe_vers;
280
281         if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers)
282                 nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers);
283         else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers)
284                 mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
285         if (nfs_pmap->pm_vers)
286                 goto version_fixed;
287
288         memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
289         memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
290         probe_vers = (nfs_mount_data_version >= 4) ?
291                         probe_mnt3_first : probe_mnt1_first;
292
293         for (; *probe_vers; probe_vers++) {
294                 nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
295                 if ((res = probe_nfsport(nfs_server) != 0)) {
296                         mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
297                         if ((res = probe_mntport(mnt_server)) != 0)
298                                 return 1;
299                         memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
300                 }
301                 switch (rpc_createerr.cf_stat) {
302                 case RPC_PROGVERSMISMATCH:
303                 case RPC_PROGNOTREGISTERED:
304                         break;
305                 default:
306                         goto out_bad;
307                 }
308                 memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
309         }
310
311 out_bad:
312         return 0;
313
314 version_fixed:
315         if (!probe_nfsport(nfs_server))
316                 goto out_bad;
317         return probe_mntport(mnt_server);
318 }