]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/network.c
12fc7620877b11eb6d8e84e042302a445b42f465
[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(_("%s: can't get address for %s\n"),
110                                         progname, hostname);
111                         return 0;
112                 } else {
113                         if (hp->h_length > sizeof(*saddr)) {
114                                 nfs_error(_("%s: got bad hp->h_length\n"),
115                                                 progname);
116                                 hp->h_length = sizeof(*saddr);
117                         }
118                         memcpy(&saddr->sin_addr, hp->h_addr, hp->h_length);
119                 }
120         }
121         return 1;
122 }
123
124 /*
125  * getport() is very similar to pmap_getport() with
126  * the exception this version uses a non-reserve ports
127  * instead of reserve ports since reserve ports
128  * are not needed for pmap requests.
129  */
130 unsigned short getport(struct sockaddr_in *saddr, unsigned long prog,
131                         unsigned long vers, unsigned int prot)
132 {
133         unsigned short port = 0;
134         int socket;
135         CLIENT *clnt = NULL;
136         struct pmap parms;
137         enum clnt_stat stat;
138
139         saddr->sin_port = htons (PMAPPORT);
140         socket = get_socket(saddr, prot, FALSE, FALSE);
141
142         switch (prot) {
143         case IPPROTO_UDP:
144                 clnt = clntudp_bufcreate(saddr,
145                                          PMAPPROG, PMAPVERS, TIMEOUT, &socket,
146                                          UDPMSGSIZE, UDPMSGSIZE);
147                 break;
148         case IPPROTO_TCP:
149                 clnt = clnttcp_create(saddr,
150                         PMAPPROG, PMAPVERS, &socket, 50, 500);
151                 break;
152         }
153         if (clnt != NULL) {
154                 parms.pm_prog = prog;
155                 parms.pm_vers = vers;
156                 parms.pm_prot = prot;
157                 parms.pm_port = 0;    /* not needed or used */
158
159                 stat = clnt_call(clnt, PMAPPROC_GETPORT, (xdrproc_t)xdr_pmap,
160                         (caddr_t)&parms, (xdrproc_t)xdr_u_short, (caddr_t)&port, TIMEOUT);
161                 if (stat) {
162                         clnt_geterr(clnt, &rpc_createerr.cf_error);
163                         rpc_createerr.cf_stat = stat;
164                 }
165                 clnt_destroy(clnt);
166                 if (stat != RPC_SUCCESS)
167                         port = 0;
168                 else if (port == 0)
169                         rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
170         }
171         if (socket != 1)
172                 close(socket);
173
174         return port;
175 }
176
177 /*
178  * Use the portmapper to discover whether or not the service we want is
179  * available. The lists 'versions' and 'protos' define ordered sequences
180  * of service versions and udp/tcp protocols to probe for.
181  */
182 static int probe_port(clnt_addr_t *server, const unsigned long *versions,
183                         const unsigned int *protos)
184 {
185         struct sockaddr_in *saddr = &server->saddr;
186         struct pmap *pmap = &server->pmap;
187         const unsigned long prog = pmap->pm_prog, *p_vers;
188         const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
189         const u_short port = (u_short) pmap->pm_port;
190         unsigned long vers = pmap->pm_vers;
191         unsigned short p_port;
192
193         p_prot = prot ? &prot : protos;
194         p_vers = vers ? &vers : versions;
195         rpc_createerr.cf_stat = 0;
196         for (;;) {
197                 saddr->sin_port = htons(PMAPPORT);
198                 p_port = getport(saddr, prog, *p_vers, *p_prot);
199                 if (p_port) {
200                         if (!port || port == p_port) {
201                                 saddr->sin_port = htons(p_port);
202                                 if (verbose) {
203                                         printf(_("%s: trying %s prog %ld vers "
204                                                 "%ld prot %s port %d\n"),
205                                                 progname,
206                                                 inet_ntoa(saddr->sin_addr),
207                                                 prog, *p_vers,
208                                                 *p_prot == IPPROTO_UDP ?
209                                                         "udp" : "tcp",
210                                                 p_port);
211                                 }
212                                 if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL))
213                                         goto out_ok;
214                                 if (rpc_createerr.cf_stat == RPC_TIMEDOUT)
215                                         goto out_bad;
216                         }
217                 }
218                 if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED)
219                         goto out_bad;
220
221                 if (!prot) {
222                         if (*++p_prot)
223                                 continue;
224                         p_prot = protos;
225                 }
226                 if (vers == pmap->pm_vers) {
227                         p_vers = versions;
228                         vers = 0;
229                 }
230                 if (vers || !*++p_vers)
231                         break;
232         }
233
234 out_bad:
235         return 0;
236
237 out_ok:
238         if (!vers)
239                 pmap->pm_vers = *p_vers;
240         if (!prot)
241                 pmap->pm_prot = *p_prot;
242         if (!port)
243                 pmap->pm_port = p_port;
244         rpc_createerr.cf_stat = 0;
245         return 1;
246 }
247
248 static int probe_nfsport(clnt_addr_t *nfs_server)
249 {
250         struct pmap *pmap = &nfs_server->pmap;
251
252         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
253                 return 1;
254
255         if (nfs_mount_data_version >= 4)
256                 return probe_port(nfs_server, probe_nfs3_first, probe_tcp_first);
257         else
258                 return probe_port(nfs_server, probe_nfs2_only, probe_udp_only);
259 }
260
261 static int probe_mntport(clnt_addr_t *mnt_server)
262 {
263         struct pmap *pmap = &mnt_server->pmap;
264
265         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
266                 return 1;
267
268         if (nfs_mount_data_version >= 4)
269                 return probe_port(mnt_server, probe_mnt3_first, probe_udp_first);
270         else
271                 return probe_port(mnt_server, probe_mnt1_first, probe_udp_only);
272 }
273
274 int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
275 {
276         struct pmap *nfs_pmap = &nfs_server->pmap;
277         struct pmap *mnt_pmap = &mnt_server->pmap;
278         struct pmap save_nfs, save_mnt;
279         int res;
280         const unsigned long *probe_vers;
281
282         if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers)
283                 nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers);
284         else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers)
285                 mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
286         if (nfs_pmap->pm_vers)
287                 goto version_fixed;
288
289         memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
290         memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
291         probe_vers = (nfs_mount_data_version >= 4) ?
292                         probe_mnt3_first : probe_mnt1_first;
293
294         for (; *probe_vers; probe_vers++) {
295                 nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
296                 if ((res = probe_nfsport(nfs_server) != 0)) {
297                         mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
298                         if ((res = probe_mntport(mnt_server)) != 0)
299                                 return 1;
300                         memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
301                 }
302                 switch (rpc_createerr.cf_stat) {
303                 case RPC_PROGVERSMISMATCH:
304                 case RPC_PROGNOTREGISTERED:
305                         break;
306                 default:
307                         goto out_bad;
308                 }
309                 memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
310         }
311
312 out_bad:
313         return 0;
314
315 version_fixed:
316         if (!probe_nfsport(nfs_server))
317                 goto out_bad;
318         return probe_mntport(mnt_server);
319 }
320
321 static int probe_statd(void)
322 {
323         struct sockaddr_in addr;
324         unsigned short port;
325
326         memset(&addr, 0, sizeof(addr));
327         addr.sin_family = AF_INET;
328         addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
329         port = getport(&addr, 100024, 1, IPPROTO_UDP);
330
331         if (port == 0)
332                 return 0;
333         addr.sin_port = htons(port);
334
335         if (clnt_ping(&addr, 100024, 1, IPPROTO_UDP, NULL) <= 0)
336                 return 0;
337
338         return 1;
339 }
340
341 /*
342  * Attempt to start rpc.statd
343  */
344 int start_statd(void)
345 {
346 #ifdef START_STATD
347         struct stat stb;
348 #endif
349
350         if (probe_statd())
351                 return 1;
352
353 #ifdef START_STATD
354         if (stat(START_STATD, &stb) == 0) {
355                 if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) {
356                         system(START_STATD);
357                         if (probe_statd())
358                                 return 1;
359                 }
360         }
361 #endif
362
363         return 0;
364 }
365
366 /*
367  * nfs_call_umount - ask the server to remove a share from it's rmtab
368  * @mnt_server: address of RPC MNT program server
369  * @argp: directory path of share to "unmount"
370  *
371  * Returns one if the unmount call succeeded; zero if the unmount
372  * failed for any reason.
373  *
374  * Note that a side effect of calling this function is that rpccreateerr
375  * is set.
376  */
377 int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
378 {
379         CLIENT *clnt;
380         enum clnt_stat res = 0;
381         int msock;
382
383         switch (mnt_server->pmap.pm_vers) {
384         case 3:
385         case 2:
386         case 1:
387                 if (!probe_mntport(mnt_server))
388                         return 0;
389                 clnt = mnt_openclnt(mnt_server, &msock);
390                 if (!clnt)
391                         return 0;
392                 res = clnt_call(clnt, MOUNTPROC_UMNT,
393                                 (xdrproc_t)xdr_dirpath, (caddr_t)argp,
394                                 (xdrproc_t)xdr_void, NULL,
395                                 TIMEOUT);
396                 mnt_closeclnt(clnt, msock);
397                 if (res == RPC_SUCCESS)
398                         return 1;
399                 break;
400         default:
401                 res = RPC_SUCCESS;
402                 break;
403         }
404
405         if (res == RPC_SUCCESS)
406                 return 1;
407         return 0;
408 }