]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/network.c
3f2721b802450134acd165b673f059615cc6f35f
[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 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <netdb.h>
35 #include <time.h>
36
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <rpc/rpc.h>
41 #include <rpc/pmap_prot.h>
42 #include <rpc/pmap_clnt.h>
43
44 #include "xcommon.h"
45 #include "mount.h"
46 #include "nls.h"
47 #include "nfs_mount.h"
48 #include "mount_constants.h"
49 #include "network.h"
50
51 #ifdef HAVE_RPCSVC_NFS_PROT_H
52 #include <rpcsvc/nfs_prot.h>
53 #else
54 #include <linux/nfs.h>
55 #define nfsstat nfs_stat
56 #endif
57
58 #ifndef NFS_PORT
59 #define NFS_PORT 2049
60 #endif
61
62 #define PMAP_TIMEOUT    (10)
63 #define CONNECT_TIMEOUT (20)
64 #define MOUNT_TIMEOUT   (30)
65
66 #if SIZEOF_SOCKLEN_T - 0 == 0
67 #define socklen_t unsigned int
68 #endif
69
70 extern int nfs_mount_data_version;
71 extern char *progname;
72 extern int verbose;
73
74 static const unsigned long nfs_to_mnt[] = {
75         0,
76         0,
77         1,
78         3,
79 };
80
81 static const unsigned long mnt_to_nfs[] = {
82         0,
83         2,
84         2,
85         3,
86 };
87
88 /*
89  * Map an NFS version into the corresponding Mountd version
90  */
91 unsigned long nfsvers_to_mnt(const unsigned long vers)
92 {
93         if (vers <= 3)
94                 return nfs_to_mnt[vers];
95         return 0;
96 }
97
98 /*
99  * Map a Mountd version into the corresponding NFS version
100  */
101 static unsigned long mntvers_to_nfs(const unsigned long vers)
102 {
103         if (vers <= 3)
104                 return mnt_to_nfs[vers];
105         return 0;
106 }
107
108 static const unsigned int probe_udp_only[] = {
109         IPPROTO_UDP,
110         0,
111 };
112
113 static const unsigned int probe_udp_first[] = {
114         IPPROTO_UDP,
115         IPPROTO_TCP,
116         0,
117 };
118
119 static const unsigned int probe_tcp_first[] = {
120         IPPROTO_TCP,
121         IPPROTO_UDP,
122         0,
123 };
124
125 static const unsigned long probe_nfs2_only[] = {
126         2,
127         0,
128 };
129
130 static const unsigned long probe_nfs3_first[] = {
131         3,
132         2,
133         0,
134 };
135
136 static const unsigned long probe_mnt1_first[] = {
137         1,
138         2,
139         0,
140 };
141
142 static const unsigned long probe_mnt3_first[] = {
143         3,
144         1,
145         2,
146         0,
147 };
148
149 /**
150  * nfs_name_to_address - resolve hostname to an IPv4 or IPv6 socket address
151  * @hostname: pointer to C string containing DNS hostname to resolve
152  * @sap: pointer to buffer to fill with socket address
153  * @len: IN: size of buffer to fill; OUT: size of socket address
154  *
155  * Returns 1 and places a socket address at @sap if successful;
156  * otherwise zero.
157  */
158 int nfs_name_to_address(const char *hostname,
159                         const sa_family_t af_hint,
160                         struct sockaddr *sap, socklen_t *salen)
161 {
162         struct addrinfo *gai_results;
163         struct addrinfo gai_hint = {
164                 .ai_family      = af_hint,
165                 .ai_flags       = AI_ADDRCONFIG,
166         };
167         socklen_t len = *salen;
168         int error, ret = 0;
169
170         if (af_hint == AF_INET6)
171                 gai_hint.ai_flags |= AI_V4MAPPED|AI_ALL;
172
173         *salen = 0;
174
175         error = getaddrinfo(hostname, NULL, &gai_hint, &gai_results);
176         if (error) {
177                 nfs_error(_("%s: DNS resolution failed for %s: %s"),
178                         progname, hostname, (error == EAI_SYSTEM ?
179                                 strerror(errno) : gai_strerror(error)));
180                 return ret;
181         }
182
183         switch (gai_results->ai_addr->sa_family) {
184         case AF_INET:
185         case AF_INET6:
186                 if (len >= gai_results->ai_addrlen) {
187                         *salen = gai_results->ai_addrlen;
188                         memcpy(sap, gai_results->ai_addr, *salen);
189                         ret = 1;
190                 }
191                 break;
192         default:
193                 /* things are really broken if we get here, so warn */
194                 nfs_error(_("%s: unrecognized DNS resolution results for %s"),
195                                 progname, hostname);
196                 break;
197         }
198
199         freeaddrinfo(gai_results);
200         return ret;
201 }
202
203 /**
204  * nfs_gethostbyname - resolve a hostname to an IPv4 address
205  * @hostname: pointer to a C string containing a DNS hostname
206  * @saddr: returns an IPv4 address 
207  *
208  * Returns 1 if successful, otherwise zero.
209  */
210 int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin)
211 {
212         socklen_t len = sizeof(*sin);
213
214         return nfs_name_to_address(hostname, AF_INET,
215                                         (struct sockaddr *)sin, &len);
216 }
217
218 /**
219  * nfs_string_to_sockaddr - convert string address to sockaddr
220  * @address:    pointer to presentation format address to convert
221  * @addrlen:    length of presentation address
222  * @sap:        pointer to socket address buffer to fill in
223  * @salen:      IN: length of address buffer
224  *              OUT: length of converted socket address
225  *
226  * Convert a presentation format address string to a socket address.
227  * Similar to nfs_name_to_address(), but the DNS query is squelched,
228  * and won't make any noise if the getaddrinfo() call fails.
229  *
230  * Returns 1 and fills in @sap and @salen if successful; otherwise zero.
231  *
232  * See RFC 4038 section 5.1 or RFC 3513 section 2.2 for more details
233  * on presenting IPv6 addresses as text strings.
234  */
235 int nfs_string_to_sockaddr(const char *address, const size_t addrlen,
236                            struct sockaddr *sap, socklen_t *salen)
237 {
238         struct addrinfo *gai_results;
239         struct addrinfo gai_hint = {
240                 .ai_flags       = AI_NUMERICHOST,
241         };
242         socklen_t len = *salen;
243         int ret = 0;
244
245         *salen = 0;
246
247         if (getaddrinfo(address, NULL, &gai_hint, &gai_results) == 0) {
248                 switch (gai_results->ai_addr->sa_family) {
249                 case AF_INET:
250                 case AF_INET6:
251                         if (len >= gai_results->ai_addrlen) {
252                                 *salen = gai_results->ai_addrlen;
253                                 memcpy(sap, gai_results->ai_addr, *salen);
254                                 ret = 1;
255                         }
256                         break;
257                 }
258                 freeaddrinfo(gai_results);
259         }
260
261         return ret;
262 }
263
264 /**
265  * nfs_present_sockaddr - convert sockaddr to string
266  * @sap: pointer to socket address to convert
267  * @salen: length of socket address
268  * @buf: pointer to buffer to fill in
269  * @buflen: length of buffer
270  *
271  * Convert the passed-in sockaddr-style address to presentation format.
272  * The presentation format address is placed in @buf and is
273  * '\0'-terminated.
274  *
275  * Returns 1 if successful; otherwise zero.
276  *
277  * See RFC 4038 section 5.1 or RFC 3513 section 2.2 for more details
278  * on presenting IPv6 addresses as text strings.
279  */
280 int nfs_present_sockaddr(const struct sockaddr *sap, const socklen_t salen,
281                          char *buf, const size_t buflen)
282 {
283 #ifdef HAVE_GETNAMEINFO
284         int result;
285
286         result = getnameinfo(sap, salen, buf, buflen,
287                                         NULL, 0, NI_NUMERICHOST);
288         if (!result)
289                 return 1;
290
291         nfs_error(_("%s: invalid server address: %s"), progname,
292                         gai_strerror(result));
293         return 0;
294 #else   /* HAVE_GETNAMEINFO */
295         char *addr;
296
297         if (sap->sa_family == AF_INET) {
298                 addr = inet_ntoa(((struct sockaddr_in *)sap)->sin_addr);
299                 if (addr && strlen(addr) < buflen) {
300                         strcpy(buf, addr);
301                         return 1;
302                 }
303         }
304
305         nfs_error(_("%s: invalid server address"), progname);
306         return 0;
307 #endif  /* HAVE_GETNAMEINFO */
308 }
309
310 /*
311  * Attempt to connect a socket, but time out after "timeout" seconds.
312  *
313  * On error return, caller closes the socket.
314  */
315 static int connect_to(int fd, struct sockaddr *addr,
316                         socklen_t addrlen, int timeout)
317 {
318         int ret, saved;
319         fd_set rset, wset;
320         struct timeval tv = {
321                 .tv_sec = timeout,
322         };
323
324         saved = fcntl(fd, F_GETFL, 0);
325         fcntl(fd, F_SETFL, saved | O_NONBLOCK);
326
327         ret = connect(fd, addr, addrlen);
328         if (ret < 0 && errno != EINPROGRESS)
329                 return -1;
330         if (ret == 0)
331                 goto out;
332
333         FD_ZERO(&rset);
334         FD_SET(fd, &rset);
335         wset = rset;
336         ret = select(fd + 1, &rset, &wset, NULL, &tv);
337         if (ret == 0) {
338                 errno = ETIMEDOUT;
339                 return -1;
340         }
341         if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
342                 int error;
343                 socklen_t len = sizeof(error);
344                 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
345                         return -1;
346                 if (error) {
347                         errno = error;
348                         return -1;
349                 }
350         } else
351                 return -1;
352
353 out:
354         fcntl(fd, F_SETFL, saved);
355         return 0;
356 }
357
358 /*
359  * Create a socket that is locally bound to a reserved or non-reserved port.
360  *
361  * The caller should check rpc_createerr to determine the cause of any error.
362  */
363 static int get_socket(struct sockaddr_in *saddr, unsigned int p_prot,
364                         unsigned int timeout, int resvp, int conn)
365 {
366         int so, cc, type;
367         struct sockaddr_in laddr;
368         socklen_t namelen = sizeof(laddr);
369
370         type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
371         if ((so = socket (AF_INET, type, p_prot)) < 0)
372                 goto err_socket;
373
374         laddr.sin_family = AF_INET;
375         laddr.sin_port = 0;
376         laddr.sin_addr.s_addr = htonl(INADDR_ANY);
377         if (resvp) {
378                 if (bindresvport(so, &laddr) < 0)
379                         goto err_bindresvport;
380         } else {
381                 cc = bind(so, (struct sockaddr *)&laddr, namelen);
382                 if (cc < 0)
383                         goto err_bind;
384         }
385         if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
386                 cc = connect_to(so, (struct sockaddr *)saddr, namelen,
387                                 timeout);
388                 if (cc < 0)
389                         goto err_connect;
390         }
391         return so;
392
393 err_socket:
394         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
395         rpc_createerr.cf_error.re_errno = errno;
396         if (verbose) {
397                 nfs_error(_("%s: Unable to create %s socket: errno %d (%s)\n"),
398                         progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
399                         errno, strerror(errno));
400         }
401         return RPC_ANYSOCK;
402
403 err_bindresvport:
404         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
405         rpc_createerr.cf_error.re_errno = errno;
406         if (verbose) {
407                 nfs_error(_("%s: Unable to bindresvport %s socket: errno %d"
408                                 " (%s)\n"),
409                         progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
410                         errno, strerror(errno));
411         }
412         close(so);
413         return RPC_ANYSOCK;
414
415 err_bind:
416         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
417         rpc_createerr.cf_error.re_errno = errno;
418         if (verbose) {
419                 nfs_error(_("%s: Unable to bind to %s socket: errno %d (%s)\n"),
420                         progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
421                         errno, strerror(errno));
422         }
423         close(so);
424         return RPC_ANYSOCK;
425
426 err_connect:
427         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
428         rpc_createerr.cf_error.re_errno = errno;
429         if (verbose) {
430                 nfs_error(_("%s: Unable to connect to %s:%d, errno %d (%s)\n"),
431                         progname, inet_ntoa(saddr->sin_addr),
432                         ntohs(saddr->sin_port), errno, strerror(errno));
433         }
434         close(so);
435         return RPC_ANYSOCK;
436 }
437
438 /*
439  * getport() is very similar to pmap_getport() with the exception that
440  * this version tries to use an ephemeral port, since reserved ports are
441  * not needed for GETPORT queries.  This conserves the very limited
442  * reserved port space, which helps reduce failed socket binds
443  * during mount storms.
444  *
445  * A side effect of calling this function is that rpccreateerr is set.
446  */
447 static unsigned short getport(struct sockaddr_in *saddr,
448                                 unsigned long program,
449                                 unsigned long version,
450                                 unsigned int proto)
451 {
452         struct sockaddr_in bind_saddr;
453         unsigned short port = 0;
454         int socket;
455         CLIENT *clnt = NULL;
456         enum clnt_stat stat;
457  
458         bind_saddr = *saddr;
459         bind_saddr.sin_port = htons(PMAPPORT);
460
461         socket = get_socket(&bind_saddr, proto, PMAP_TIMEOUT, FALSE, FALSE);
462         if (socket == RPC_ANYSOCK) {
463                 if (proto == IPPROTO_TCP &&
464                     rpc_createerr.cf_error.re_errno == ETIMEDOUT)
465                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
466                 return 0;
467         }
468
469         switch (proto) {
470         case IPPROTO_UDP:
471                 clnt = clntudp_bufcreate(&bind_saddr,
472                                          PMAPPROG, PMAPVERS,
473                                          RETRY_TIMEOUT, &socket,
474                                          RPCSMALLMSGSIZE,
475                                          RPCSMALLMSGSIZE);
476                 break;
477         case IPPROTO_TCP:
478                 clnt = clnttcp_create(&bind_saddr,
479                                       PMAPPROG, PMAPVERS,
480                                       &socket,
481                                       RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
482                 break;
483         }
484         if (clnt != NULL) {
485                 struct pmap parms = {
486                         .pm_prog        = program,
487                         .pm_vers        = version,
488                         .pm_prot        = proto,
489                 };
490
491                 stat = clnt_call(clnt, PMAPPROC_GETPORT,
492                                  (xdrproc_t)xdr_pmap, (caddr_t)&parms,
493                                  (xdrproc_t)xdr_u_short, (caddr_t)&port,
494                                  TIMEOUT);
495                 if (stat) {
496                         clnt_geterr(clnt, &rpc_createerr.cf_error);
497                         rpc_createerr.cf_stat = stat;
498                 }
499                 clnt_destroy(clnt);
500                 if (stat != RPC_SUCCESS)
501                         port = 0;
502                 else if (port == 0)
503                         rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
504         }
505         close(socket);
506
507         return port;
508 }
509
510 /*
511  * Use the portmapper to discover whether or not the service we want is
512  * available. The lists 'versions' and 'protos' define ordered sequences
513  * of service versions and udp/tcp protocols to probe for.
514  */
515 static int probe_port(clnt_addr_t *server, const unsigned long *versions,
516                         const unsigned int *protos)
517 {
518         struct sockaddr_in *saddr = &server->saddr;
519         struct pmap *pmap = &server->pmap;
520         const unsigned long prog = pmap->pm_prog, *p_vers;
521         const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
522         const u_short port = (u_short) pmap->pm_port;
523         unsigned long vers = pmap->pm_vers;
524         unsigned short p_port;
525
526         p_prot = prot ? &prot : protos;
527         p_vers = vers ? &vers : versions;
528         rpc_createerr.cf_stat = 0;
529         for (;;) {
530                 p_port = getport(saddr, prog, *p_vers, *p_prot);
531                 if (p_port) {
532                         if (!port || port == p_port) {
533                                 saddr->sin_port = htons(p_port);
534                                 if (verbose) {
535                                         printf(_("%s: trying %s prog %ld vers "
536                                                 "%ld prot %s port %d\n"),
537                                                 progname,
538                                                 inet_ntoa(saddr->sin_addr),
539                                                 prog, *p_vers,
540                                                 *p_prot == IPPROTO_UDP ?
541                                                         _("UDP") : _("TCP"),
542                                                 p_port);
543                                 }
544                                 if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL))
545                                         goto out_ok;
546                                 if (rpc_createerr.cf_stat == RPC_TIMEDOUT)
547                                         goto out_bad;
548                         }
549                 }
550                 if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED &&
551                     rpc_createerr.cf_stat != RPC_PROGVERSMISMATCH)
552                         goto out_bad;
553
554                 if (!prot) {
555                         if (*++p_prot)
556                                 continue;
557                         p_prot = protos;
558                 }
559                 if (vers || !*++p_vers)
560                         break;
561         }
562
563 out_bad:
564         return 0;
565
566 out_ok:
567         if (!vers)
568                 pmap->pm_vers = *p_vers;
569         if (!prot)
570                 pmap->pm_prot = *p_prot;
571         if (!port)
572                 pmap->pm_port = p_port;
573         rpc_createerr.cf_stat = 0;
574         return 1;
575 }
576
577 static int probe_nfsport(clnt_addr_t *nfs_server)
578 {
579         struct pmap *pmap = &nfs_server->pmap;
580
581         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
582                 return 1;
583
584         if (nfs_mount_data_version >= 4)
585                 return probe_port(nfs_server, probe_nfs3_first, probe_tcp_first);
586         else
587                 return probe_port(nfs_server, probe_nfs2_only, probe_udp_only);
588 }
589
590 static int probe_mntport(clnt_addr_t *mnt_server)
591 {
592         struct pmap *pmap = &mnt_server->pmap;
593
594         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
595                 return 1;
596
597         if (nfs_mount_data_version >= 4)
598                 return probe_port(mnt_server, probe_mnt3_first, probe_udp_first);
599         else
600                 return probe_port(mnt_server, probe_mnt1_first, probe_udp_only);
601 }
602
603 /**
604  * probe_bothports - discover the RPC endpoints of mountd and NFS server
605  * @mnt_server: pointer to address and pmap argument for mountd results
606  * @nfs_server: pointer to address and pmap argument for NFS server
607  *
608  * Returns 1 if successful, otherwise zero if some error occurred.
609  * Note that the arguments are both input and output arguments.
610  *
611  * A side effect of calling this function is that rpccreateerr is set.
612  */
613 int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
614 {
615         struct pmap *nfs_pmap = &nfs_server->pmap;
616         struct pmap *mnt_pmap = &mnt_server->pmap;
617         struct pmap save_nfs, save_mnt;
618         int res;
619         const unsigned long *probe_vers;
620
621         if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers)
622                 nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers);
623         else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers)
624                 mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
625         if (nfs_pmap->pm_vers)
626                 goto version_fixed;
627
628         memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
629         memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
630         probe_vers = (nfs_mount_data_version >= 4) ?
631                         probe_mnt3_first : probe_mnt1_first;
632
633         for (; *probe_vers; probe_vers++) {
634                 nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
635                 if ((res = probe_nfsport(nfs_server) != 0)) {
636                         mnt_pmap->pm_vers = *probe_vers;
637                         if ((res = probe_mntport(mnt_server)) != 0)
638                                 return 1;
639                         memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
640                 }
641                 switch (rpc_createerr.cf_stat) {
642                 case RPC_PROGVERSMISMATCH:
643                 case RPC_PROGNOTREGISTERED:
644                         break;
645                 default:
646                         goto out_bad;
647                 }
648                 memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
649         }
650
651 out_bad:
652         return 0;
653
654 version_fixed:
655         if (!probe_nfsport(nfs_server))
656                 goto out_bad;
657         return probe_mntport(mnt_server);
658 }
659
660 static int probe_statd(void)
661 {
662         struct sockaddr_in addr;
663         unsigned short port;
664
665         memset(&addr, 0, sizeof(addr));
666         addr.sin_family = AF_INET;
667         addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
668         port = getport(&addr, 100024, 1, IPPROTO_UDP);
669
670         if (port == 0)
671                 return 0;
672         addr.sin_port = htons(port);
673
674         if (clnt_ping(&addr, 100024, 1, IPPROTO_UDP, NULL) <= 0)
675                 return 0;
676
677         return 1;
678 }
679
680 /**
681  * start_statd - attempt to start rpc.statd
682  *
683  * Returns 1 if statd is running; otherwise zero.
684  */
685 int start_statd(void)
686 {
687 #ifdef START_STATD
688         struct stat stb;
689 #endif
690
691         if (probe_statd())
692                 return 1;
693
694 #ifdef START_STATD
695         if (stat(START_STATD, &stb) == 0) {
696                 if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) {
697                         system(START_STATD);
698                         if (probe_statd())
699                                 return 1;
700                 }
701         }
702 #endif
703
704         return 0;
705 }
706
707 /**
708  * nfs_call_umount - ask the server to remove a share from it's rmtab
709  * @mnt_server: address of RPC MNT program server
710  * @argp: directory path of share to "unmount"
711  *
712  * Returns one if the unmount call succeeded; zero if the unmount
713  * failed for any reason.
714  *
715  * Note that a side effect of calling this function is that rpccreateerr
716  * is set.
717  */
718 int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
719 {
720         CLIENT *clnt;
721         enum clnt_stat res = 0;
722         int msock;
723
724         if (!probe_mntport(mnt_server))
725                 return 0;
726         clnt = mnt_openclnt(mnt_server, &msock);
727         if (!clnt)
728                 return 0;
729         res = clnt_call(clnt, MOUNTPROC_UMNT,
730                         (xdrproc_t)xdr_dirpath, (caddr_t)argp,
731                         (xdrproc_t)xdr_void, NULL,
732                         TIMEOUT);
733         mnt_closeclnt(clnt, msock);
734
735         if (res == RPC_SUCCESS)
736                 return 1;
737         return 0;
738 }
739
740 /**
741  * mnt_openclnt - get a handle for a remote mountd service
742  * @mnt_server: address and pmap arguments of mountd service
743  * @msock: returns a file descriptor of the underlying transport socket
744  *
745  * Returns an active handle for the remote's mountd service
746  */
747 CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
748 {
749         struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
750         struct pmap *mnt_pmap = &mnt_server->pmap;
751         CLIENT *clnt = NULL;
752
753         mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
754         *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
755                                 TRUE, FALSE);
756         if (*msock == RPC_ANYSOCK) {
757                 if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
758                         /*
759                          * Probably in-use by a TIME_WAIT connection,
760                          * It is worth waiting a while and trying again.
761                          */
762                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
763                 return NULL;
764         }
765
766         switch (mnt_pmap->pm_prot) {
767         case IPPROTO_UDP:
768                 clnt = clntudp_bufcreate(mnt_saddr,
769                                          mnt_pmap->pm_prog, mnt_pmap->pm_vers,
770                                          RETRY_TIMEOUT, msock,
771                                          MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
772                 break;
773         case IPPROTO_TCP:
774                 clnt = clnttcp_create(mnt_saddr,
775                                       mnt_pmap->pm_prog, mnt_pmap->pm_vers,
776                                       msock,
777                                       MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
778                 break;
779         }
780         if (clnt) {
781                 /* try to mount hostname:dirname */
782                 clnt->cl_auth = authunix_create_default();
783                 return clnt;
784         }
785         return NULL;
786 }
787
788 /**
789  * mnt_closeclnt - terminate a handle for a remote mountd service
790  * @clnt: pointer to an active handle for a remote mountd service
791  * @msock: file descriptor of the underlying transport socket
792  *
793  */
794 void mnt_closeclnt(CLIENT *clnt, int msock)
795 {
796         auth_destroy(clnt->cl_auth);
797         clnt_destroy(clnt);
798         close(msock);
799 }
800
801 /**
802  * clnt_ping - send an RPC ping to the remote RPC service endpoint
803  * @saddr: server's address
804  * @prog: target RPC program number
805  * @vers: target RPC version number
806  * @prot: target RPC protocol
807  * @caddr: filled in with our network address
808  *
809  * Sigh... getport() doesn't actually check the version number.
810  * In order to make sure that the server actually supports the service
811  * we're requesting, we open and RPC client, and fire off a NULL
812  * RPC call.
813  *
814  * caddr is the network address that the server will use to call us back.
815  * On multi-homed clients, this address depends on which NIC we use to
816  * route requests to the server.
817  *
818  * Returns one if successful, otherwise zero.
819  */
820 int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
821                 const unsigned long vers, const unsigned int prot,
822                 struct sockaddr_in *caddr)
823 {
824         CLIENT *clnt = NULL;
825         int sock, stat;
826         static char clnt_res;
827         struct sockaddr dissolve;
828
829         rpc_createerr.cf_stat = stat = 0;
830         sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
831         if (sock == RPC_ANYSOCK) {
832                 if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
833                         /*
834                          * TCP timeout. Bubble up the error to see 
835                          * how it should be handled.
836                          */
837                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
838                 }
839                 return 0;
840         }
841
842         if (caddr) {
843                 /* Get the address of our end of this connection */
844                 socklen_t len = sizeof(*caddr);
845                 if (getsockname(sock, caddr, &len) != 0)
846                         caddr->sin_family = 0;
847         }
848
849         switch(prot) {
850         case IPPROTO_UDP:
851                 /* The socket is connected (so we could getsockname successfully),
852                  * but some servers on multi-homed hosts reply from
853                  * the wrong address, so if we stay connected, we lose the reply.
854                  */
855                 dissolve.sa_family = AF_UNSPEC;
856                 connect(sock, &dissolve, sizeof(dissolve));
857
858                 clnt = clntudp_bufcreate(saddr, prog, vers,
859                                          RETRY_TIMEOUT, &sock,
860                                          RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
861                 break;
862         case IPPROTO_TCP:
863                 clnt = clnttcp_create(saddr, prog, vers, &sock,
864                                       RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
865                 break;
866         }
867         if (!clnt) {
868                 close(sock);
869                 return 0;
870         }
871         memset(&clnt_res, 0, sizeof(clnt_res));
872         stat = clnt_call(clnt, NULLPROC,
873                          (xdrproc_t)xdr_void, (caddr_t)NULL,
874                          (xdrproc_t)xdr_void, (caddr_t)&clnt_res,
875                          TIMEOUT);
876         if (stat) {
877                 clnt_geterr(clnt, &rpc_createerr.cf_error);
878                 rpc_createerr.cf_stat = stat;
879         }
880         clnt_destroy(clnt);
881         close(sock);
882
883         if (stat == RPC_SUCCESS)
884                 return 1;
885         else
886                 return 0;
887 }
888
889 /**
890  * get_client_address - acquire our local network address
891  * @saddr: server's address
892  * @caddr: filled in with our network address
893  *
894  * Discover a network address that the server will use to call us back.
895  * On multi-homed clients, this address depends on which NIC we use to
896  * route requests to the server.
897  *
898  * Use a connected datagram socket so as not to leave a socket in TIME_WAIT.
899  *
900  * Returns one if successful, otherwise zero.
901  */
902 int get_client_address(struct sockaddr_in *saddr, struct sockaddr_in *caddr)
903 {
904         socklen_t len = sizeof(*caddr);
905         int socket, err;
906
907         socket = get_socket(saddr, IPPROTO_UDP, CONNECT_TIMEOUT, FALSE, TRUE);
908         if (socket == RPC_ANYSOCK)
909                 return 0;
910
911         err = getsockname(socket, caddr, &len);
912         close(socket);
913
914         if (err && verbose) {
915                 nfs_error(_("%s: getsockname failed: %s"),
916                                 progname, strerror(errno));
917                 return 0;
918         }
919         return 1;
920 }