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