]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/network.c
libnfs.a: move more mount-only functions out of libnfs.a
[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 "nfs_mount.h"
42 #include "mount_constants.h"
43 #include "network.h"
44
45 #ifdef HAVE_RPCSVC_NFS_PROT_H
46 #include <rpcsvc/nfs_prot.h>
47 #else
48 #include <linux/nfs.h>
49 #define nfsstat nfs_stat
50 #endif
51
52 #ifndef NFS_PORT
53 #define NFS_PORT 2049
54 #endif
55
56 extern int nfs_mount_data_version;
57 extern char *progname;
58 extern int verbose;
59
60 static const unsigned long nfs_to_mnt[] = {
61         0,
62         0,
63         1,
64         3,
65 };
66
67 static const unsigned long mnt_to_nfs[] = {
68         0,
69         2,
70         2,
71         3,
72 };
73
74 /*
75  * Map an NFS version into the corresponding Mountd version
76  */
77 unsigned long nfsvers_to_mnt(const unsigned long vers)
78 {
79         if (vers <= 3)
80                 return nfs_to_mnt[vers];
81         return 0;
82 }
83
84 /*
85  * Map a Mountd version into the corresponding NFS version
86  */
87 static unsigned long mntvers_to_nfs(const unsigned long vers)
88 {
89         if (vers <= 3)
90                 return mnt_to_nfs[vers];
91         return 0;
92 }
93
94 static const unsigned int probe_udp_only[] = {
95         IPPROTO_UDP,
96         0,
97 };
98
99 static const unsigned int probe_udp_first[] = {
100         IPPROTO_UDP,
101         IPPROTO_TCP,
102         0,
103 };
104
105 static const unsigned int probe_tcp_first[] = {
106         IPPROTO_TCP,
107         IPPROTO_UDP,
108         0,
109 };
110
111 static const unsigned long probe_nfs2_only[] = {
112         2,
113         0,
114 };
115
116 static const unsigned long probe_nfs3_first[] = {
117         3,
118         2,
119         0,
120 };
121
122 static const unsigned long probe_mnt1_first[] = {
123         1,
124         2,
125         0,
126 };
127
128 static const unsigned long probe_mnt3_first[] = {
129         3,
130         1,
131         2,
132         0,
133 };
134
135 int nfs_gethostbyname(const char *hostname, struct sockaddr_in *saddr)
136 {
137         struct hostent *hp;
138
139         saddr->sin_family = AF_INET;
140         if (!inet_aton(hostname, &saddr->sin_addr)) {
141                 if ((hp = gethostbyname(hostname)) == NULL) {
142                         nfs_error(_("%s: can't get address for %s\n"),
143                                         progname, hostname);
144                         return 0;
145                 } else {
146                         if (hp->h_length > sizeof(*saddr)) {
147                                 nfs_error(_("%s: got bad hp->h_length\n"),
148                                                 progname);
149                                 hp->h_length = sizeof(*saddr);
150                         }
151                         memcpy(&saddr->sin_addr, hp->h_addr, hp->h_length);
152                 }
153         }
154         return 1;
155 }
156
157 /*
158  * getport() is very similar to pmap_getport() with the exception that
159  * this version tries to use an ephemeral port, since reserved ports are
160  * not needed for GETPORT queries.  This conserves the very limited
161  * reserved port space, which helps reduce failed socket binds
162  * during mount storms.
163  *
164  * A side effect of calling this function is that rpccreateerr is set.
165  */
166 static unsigned short getport(struct sockaddr_in *saddr,
167                                 unsigned long program,
168                                 unsigned long version,
169                                 unsigned int proto)
170 {
171         unsigned short port = 0;
172         int socket;
173         CLIENT *clnt = NULL;
174         enum clnt_stat stat;
175
176         saddr->sin_port = htons(PMAPPORT);
177
178         /*
179          * Try to get a socket with a non-privileged port.
180          * clnt*create() will create one anyway if this
181          * fails.
182          */
183         socket = get_socket(saddr, proto, FALSE, FALSE);
184         if (socket == RPC_ANYSOCK) {
185                 if (proto == IPPROTO_TCP && errno == ETIMEDOUT) {
186                         /*
187                          * TCP SYN timed out, so exit now.
188                          */
189                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
190                 }
191                 return 0;
192         }
193
194         switch (proto) {
195         case IPPROTO_UDP:
196                 clnt = clntudp_bufcreate(saddr,
197                                          PMAPPROG, PMAPVERS,
198                                          RETRY_TIMEOUT, &socket,
199                                          RPCSMALLMSGSIZE,
200                                          RPCSMALLMSGSIZE);
201                 break;
202         case IPPROTO_TCP:
203                 clnt = clnttcp_create(saddr, PMAPPROG, PMAPVERS, &socket,
204                                       RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
205                 break;
206         }
207         if (clnt != NULL) {
208                 struct pmap parms = {
209                         .pm_prog        = program,
210                         .pm_vers        = version,
211                         .pm_prot        = proto,
212                 };
213
214                 stat = clnt_call(clnt, PMAPPROC_GETPORT,
215                                  (xdrproc_t)xdr_pmap, (caddr_t)&parms,
216                                  (xdrproc_t)xdr_u_short, (caddr_t)&port,
217                                  TIMEOUT);
218                 if (stat) {
219                         clnt_geterr(clnt, &rpc_createerr.cf_error);
220                         rpc_createerr.cf_stat = stat;
221                 }
222                 clnt_destroy(clnt);
223                 if (stat != RPC_SUCCESS)
224                         port = 0;
225                 else if (port == 0)
226                         rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
227         }
228         if (socket != 1)
229                 close(socket);
230
231         return port;
232 }
233
234 /*
235  * Use the portmapper to discover whether or not the service we want is
236  * available. The lists 'versions' and 'protos' define ordered sequences
237  * of service versions and udp/tcp protocols to probe for.
238  */
239 static int probe_port(clnt_addr_t *server, const unsigned long *versions,
240                         const unsigned int *protos)
241 {
242         struct sockaddr_in *saddr = &server->saddr;
243         struct pmap *pmap = &server->pmap;
244         const unsigned long prog = pmap->pm_prog, *p_vers;
245         const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
246         const u_short port = (u_short) pmap->pm_port;
247         unsigned long vers = pmap->pm_vers;
248         unsigned short p_port;
249
250         p_prot = prot ? &prot : protos;
251         p_vers = vers ? &vers : versions;
252         rpc_createerr.cf_stat = 0;
253         for (;;) {
254                 saddr->sin_port = htons(PMAPPORT);
255                 p_port = getport(saddr, prog, *p_vers, *p_prot);
256                 if (p_port) {
257                         if (!port || port == p_port) {
258                                 saddr->sin_port = htons(p_port);
259                                 if (verbose) {
260                                         printf(_("%s: trying %s prog %ld vers "
261                                                 "%ld prot %s port %d\n"),
262                                                 progname,
263                                                 inet_ntoa(saddr->sin_addr),
264                                                 prog, *p_vers,
265                                                 *p_prot == IPPROTO_UDP ?
266                                                         "udp" : "tcp",
267                                                 p_port);
268                                 }
269                                 if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL))
270                                         goto out_ok;
271                                 if (rpc_createerr.cf_stat == RPC_TIMEDOUT)
272                                         goto out_bad;
273                         }
274                 }
275                 if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED)
276                         goto out_bad;
277
278                 if (!prot) {
279                         if (*++p_prot)
280                                 continue;
281                         p_prot = protos;
282                 }
283                 if (vers == pmap->pm_vers) {
284                         p_vers = versions;
285                         vers = 0;
286                 }
287                 if (vers || !*++p_vers)
288                         break;
289         }
290
291 out_bad:
292         return 0;
293
294 out_ok:
295         if (!vers)
296                 pmap->pm_vers = *p_vers;
297         if (!prot)
298                 pmap->pm_prot = *p_prot;
299         if (!port)
300                 pmap->pm_port = p_port;
301         rpc_createerr.cf_stat = 0;
302         return 1;
303 }
304
305 static int probe_nfsport(clnt_addr_t *nfs_server)
306 {
307         struct pmap *pmap = &nfs_server->pmap;
308
309         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
310                 return 1;
311
312         if (nfs_mount_data_version >= 4)
313                 return probe_port(nfs_server, probe_nfs3_first, probe_tcp_first);
314         else
315                 return probe_port(nfs_server, probe_nfs2_only, probe_udp_only);
316 }
317
318 static int probe_mntport(clnt_addr_t *mnt_server)
319 {
320         struct pmap *pmap = &mnt_server->pmap;
321
322         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
323                 return 1;
324
325         if (nfs_mount_data_version >= 4)
326                 return probe_port(mnt_server, probe_mnt3_first, probe_udp_first);
327         else
328                 return probe_port(mnt_server, probe_mnt1_first, probe_udp_only);
329 }
330
331 int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
332 {
333         struct pmap *nfs_pmap = &nfs_server->pmap;
334         struct pmap *mnt_pmap = &mnt_server->pmap;
335         struct pmap save_nfs, save_mnt;
336         int res;
337         const unsigned long *probe_vers;
338
339         if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers)
340                 nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers);
341         else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers)
342                 mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
343         if (nfs_pmap->pm_vers)
344                 goto version_fixed;
345
346         memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
347         memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
348         probe_vers = (nfs_mount_data_version >= 4) ?
349                         probe_mnt3_first : probe_mnt1_first;
350
351         for (; *probe_vers; probe_vers++) {
352                 nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
353                 if ((res = probe_nfsport(nfs_server) != 0)) {
354                         mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
355                         if ((res = probe_mntport(mnt_server)) != 0)
356                                 return 1;
357                         memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
358                 }
359                 switch (rpc_createerr.cf_stat) {
360                 case RPC_PROGVERSMISMATCH:
361                 case RPC_PROGNOTREGISTERED:
362                         break;
363                 default:
364                         goto out_bad;
365                 }
366                 memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
367         }
368
369 out_bad:
370         return 0;
371
372 version_fixed:
373         if (!probe_nfsport(nfs_server))
374                 goto out_bad;
375         return probe_mntport(mnt_server);
376 }
377
378 static int probe_statd(void)
379 {
380         struct sockaddr_in addr;
381         unsigned short port;
382
383         memset(&addr, 0, sizeof(addr));
384         addr.sin_family = AF_INET;
385         addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
386         port = getport(&addr, 100024, 1, IPPROTO_UDP);
387
388         if (port == 0)
389                 return 0;
390         addr.sin_port = htons(port);
391
392         if (clnt_ping(&addr, 100024, 1, IPPROTO_UDP, NULL) <= 0)
393                 return 0;
394
395         return 1;
396 }
397
398 /*
399  * Attempt to start rpc.statd
400  */
401 int start_statd(void)
402 {
403 #ifdef START_STATD
404         struct stat stb;
405 #endif
406
407         if (probe_statd())
408                 return 1;
409
410 #ifdef START_STATD
411         if (stat(START_STATD, &stb) == 0) {
412                 if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) {
413                         system(START_STATD);
414                         if (probe_statd())
415                                 return 1;
416                 }
417         }
418 #endif
419
420         return 0;
421 }
422
423 /*
424  * nfs_call_umount - ask the server to remove a share from it's rmtab
425  * @mnt_server: address of RPC MNT program server
426  * @argp: directory path of share to "unmount"
427  *
428  * Returns one if the unmount call succeeded; zero if the unmount
429  * failed for any reason.
430  *
431  * Note that a side effect of calling this function is that rpccreateerr
432  * is set.
433  */
434 int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
435 {
436         CLIENT *clnt;
437         enum clnt_stat res = 0;
438         int msock;
439
440         switch (mnt_server->pmap.pm_vers) {
441         case 3:
442         case 2:
443         case 1:
444                 if (!probe_mntport(mnt_server))
445                         return 0;
446                 clnt = mnt_openclnt(mnt_server, &msock);
447                 if (!clnt)
448                         return 0;
449                 res = clnt_call(clnt, MOUNTPROC_UMNT,
450                                 (xdrproc_t)xdr_dirpath, (caddr_t)argp,
451                                 (xdrproc_t)xdr_void, NULL,
452                                 TIMEOUT);
453                 mnt_closeclnt(clnt, msock);
454                 if (res == RPC_SUCCESS)
455                         return 1;
456                 break;
457         default:
458                 res = RPC_SUCCESS;
459                 break;
460         }
461
462         if (res == RPC_SUCCESS)
463                 return 1;
464         return 0;
465 }
466
467 CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
468 {
469         struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
470         struct pmap *mnt_pmap = &mnt_server->pmap;
471         CLIENT *clnt = NULL;
472
473         mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
474         *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, TRUE, FALSE);
475         if (*msock == RPC_ANYSOCK) {
476                 if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
477                         /*
478                          * Probably in-use by a TIME_WAIT connection,
479                          * It is worth waiting a while and trying again.
480                          */
481                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
482                 return NULL;
483         }
484
485         switch (mnt_pmap->pm_prot) {
486         case IPPROTO_UDP:
487                 clnt = clntudp_bufcreate(mnt_saddr,
488                                          mnt_pmap->pm_prog, mnt_pmap->pm_vers,
489                                          RETRY_TIMEOUT, msock,
490                                          MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
491                 break;
492         case IPPROTO_TCP:
493                 clnt = clnttcp_create(mnt_saddr,
494                                       mnt_pmap->pm_prog, mnt_pmap->pm_vers,
495                                       msock,
496                                       MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
497                 break;
498         }
499         if (clnt) {
500                 /* try to mount hostname:dirname */
501                 clnt->cl_auth = authunix_create_default();
502                 return clnt;
503         }
504         return NULL;
505 }
506
507 void mnt_closeclnt(CLIENT *clnt, int msock)
508 {
509         auth_destroy(clnt->cl_auth);
510         clnt_destroy(clnt);
511         close(msock);
512 }