]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/network.c
c9fd548c5ac47491b176cc2c73375866d34036a2
[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  * Attempt to connect a socket, but time out after "timeout" seconds.
220  *
221  * On error return, caller closes the socket.
222  */
223 static int connect_to(int fd, struct sockaddr *addr,
224                         socklen_t addrlen, int timeout)
225 {
226         int ret, saved;
227         fd_set rset, wset;
228         struct timeval tv = {
229                 .tv_sec = timeout,
230         };
231
232         saved = fcntl(fd, F_GETFL, 0);
233         fcntl(fd, F_SETFL, saved | O_NONBLOCK);
234
235         ret = connect(fd, addr, addrlen);
236         if (ret < 0 && errno != EINPROGRESS)
237                 return -1;
238         if (ret == 0)
239                 goto out;
240
241         FD_ZERO(&rset);
242         FD_SET(fd, &rset);
243         wset = rset;
244         ret = select(fd + 1, &rset, &wset, NULL, &tv);
245         if (ret == 0) {
246                 errno = ETIMEDOUT;
247                 return -1;
248         }
249         if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
250                 int error;
251                 socklen_t len = sizeof(error);
252                 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
253                         return -1;
254                 if (error) {
255                         errno = error;
256                         return -1;
257                 }
258         } else
259                 return -1;
260
261 out:
262         fcntl(fd, F_SETFL, saved);
263         return 0;
264 }
265
266 /*
267  * Create a socket that is locally bound to a reserved or non-reserved port.
268  *
269  * The caller should check rpc_createerr to determine the cause of any error.
270  */
271 static int get_socket(struct sockaddr_in *saddr, unsigned int p_prot,
272                         unsigned int timeout, int resvp, int conn)
273 {
274         int so, cc, type;
275         struct sockaddr_in laddr;
276         socklen_t namelen = sizeof(laddr);
277
278         type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
279         if ((so = socket (AF_INET, type, p_prot)) < 0)
280                 goto err_socket;
281
282         laddr.sin_family = AF_INET;
283         laddr.sin_port = 0;
284         laddr.sin_addr.s_addr = htonl(INADDR_ANY);
285         if (resvp) {
286                 if (bindresvport(so, &laddr) < 0)
287                         goto err_bindresvport;
288         } else {
289                 cc = bind(so, (struct sockaddr *)&laddr, namelen);
290                 if (cc < 0)
291                         goto err_bind;
292         }
293         if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
294                 cc = connect_to(so, (struct sockaddr *)saddr, namelen,
295                                 timeout);
296                 if (cc < 0)
297                         goto err_connect;
298         }
299         return so;
300
301 err_socket:
302         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
303         rpc_createerr.cf_error.re_errno = errno;
304         if (verbose) {
305                 nfs_error(_("%s: Unable to create %s socket: errno %d (%s)\n"),
306                         progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
307                         errno, strerror(errno));
308         }
309         return RPC_ANYSOCK;
310
311 err_bindresvport:
312         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
313         rpc_createerr.cf_error.re_errno = errno;
314         if (verbose) {
315                 nfs_error(_("%s: Unable to bindresvport %s socket: errno %d"
316                                 " (%s)\n"),
317                         progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
318                         errno, strerror(errno));
319         }
320         close(so);
321         return RPC_ANYSOCK;
322
323 err_bind:
324         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
325         rpc_createerr.cf_error.re_errno = errno;
326         if (verbose) {
327                 nfs_error(_("%s: Unable to bind to %s socket: errno %d (%s)\n"),
328                         progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
329                         errno, strerror(errno));
330         }
331         close(so);
332         return RPC_ANYSOCK;
333
334 err_connect:
335         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
336         rpc_createerr.cf_error.re_errno = errno;
337         if (verbose) {
338                 nfs_error(_("%s: Unable to connect to %s:%d, errno %d (%s)\n"),
339                         progname, inet_ntoa(saddr->sin_addr),
340                         ntohs(saddr->sin_port), errno, strerror(errno));
341         }
342         close(so);
343         return RPC_ANYSOCK;
344 }
345
346 /*
347  * getport() is very similar to pmap_getport() with the exception that
348  * this version tries to use an ephemeral port, since reserved ports are
349  * not needed for GETPORT queries.  This conserves the very limited
350  * reserved port space, which helps reduce failed socket binds
351  * during mount storms.
352  *
353  * A side effect of calling this function is that rpccreateerr is set.
354  */
355 static unsigned short getport(struct sockaddr_in *saddr,
356                                 unsigned long program,
357                                 unsigned long version,
358                                 unsigned int proto)
359 {
360         struct sockaddr_in bind_saddr;
361         unsigned short port = 0;
362         int socket;
363         CLIENT *clnt = NULL;
364         enum clnt_stat stat;
365  
366         bind_saddr = *saddr;
367         bind_saddr.sin_port = htons(PMAPPORT);
368
369         socket = get_socket(&bind_saddr, proto, PMAP_TIMEOUT, FALSE, FALSE);
370         if (socket == RPC_ANYSOCK) {
371                 if (proto == IPPROTO_TCP &&
372                     rpc_createerr.cf_error.re_errno == ETIMEDOUT)
373                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
374                 return 0;
375         }
376
377         switch (proto) {
378         case IPPROTO_UDP:
379                 clnt = clntudp_bufcreate(&bind_saddr,
380                                          PMAPPROG, PMAPVERS,
381                                          RETRY_TIMEOUT, &socket,
382                                          RPCSMALLMSGSIZE,
383                                          RPCSMALLMSGSIZE);
384                 break;
385         case IPPROTO_TCP:
386                 clnt = clnttcp_create(&bind_saddr,
387                                       PMAPPROG, PMAPVERS,
388                                       &socket,
389                                       RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
390                 break;
391         }
392         if (clnt != NULL) {
393                 struct pmap parms = {
394                         .pm_prog        = program,
395                         .pm_vers        = version,
396                         .pm_prot        = proto,
397                 };
398
399                 stat = clnt_call(clnt, PMAPPROC_GETPORT,
400                                  (xdrproc_t)xdr_pmap, (caddr_t)&parms,
401                                  (xdrproc_t)xdr_u_short, (caddr_t)&port,
402                                  TIMEOUT);
403                 if (stat) {
404                         clnt_geterr(clnt, &rpc_createerr.cf_error);
405                         rpc_createerr.cf_stat = stat;
406                 }
407                 clnt_destroy(clnt);
408                 if (stat != RPC_SUCCESS)
409                         port = 0;
410                 else if (port == 0)
411                         rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
412         }
413         close(socket);
414
415         return port;
416 }
417
418 /*
419  * Use the portmapper to discover whether or not the service we want is
420  * available. The lists 'versions' and 'protos' define ordered sequences
421  * of service versions and udp/tcp protocols to probe for.
422  */
423 static int probe_port(clnt_addr_t *server, const unsigned long *versions,
424                         const unsigned int *protos)
425 {
426         struct sockaddr_in *saddr = &server->saddr;
427         struct pmap *pmap = &server->pmap;
428         const unsigned long prog = pmap->pm_prog, *p_vers;
429         const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
430         const u_short port = (u_short) pmap->pm_port;
431         unsigned long vers = pmap->pm_vers;
432         unsigned short p_port;
433
434         p_prot = prot ? &prot : protos;
435         p_vers = vers ? &vers : versions;
436         rpc_createerr.cf_stat = 0;
437         for (;;) {
438                 p_port = getport(saddr, prog, *p_vers, *p_prot);
439                 if (p_port) {
440                         if (!port || port == p_port) {
441                                 saddr->sin_port = htons(p_port);
442                                 if (verbose) {
443                                         printf(_("%s: trying %s prog %ld vers "
444                                                 "%ld prot %s port %d\n"),
445                                                 progname,
446                                                 inet_ntoa(saddr->sin_addr),
447                                                 prog, *p_vers,
448                                                 *p_prot == IPPROTO_UDP ?
449                                                         _("UDP") : _("TCP"),
450                                                 p_port);
451                                 }
452                                 if (clnt_ping(saddr, prog, *p_vers, *p_prot, NULL))
453                                         goto out_ok;
454                                 if (rpc_createerr.cf_stat == RPC_TIMEDOUT)
455                                         goto out_bad;
456                         }
457                 }
458                 if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED &&
459                     rpc_createerr.cf_stat != RPC_PROGVERSMISMATCH)
460                         goto out_bad;
461
462                 if (!prot) {
463                         if (*++p_prot)
464                                 continue;
465                         p_prot = protos;
466                 }
467                 if (vers || !*++p_vers)
468                         break;
469         }
470
471 out_bad:
472         return 0;
473
474 out_ok:
475         if (!vers)
476                 pmap->pm_vers = *p_vers;
477         if (!prot)
478                 pmap->pm_prot = *p_prot;
479         if (!port)
480                 pmap->pm_port = p_port;
481         rpc_createerr.cf_stat = 0;
482         return 1;
483 }
484
485 static int probe_nfsport(clnt_addr_t *nfs_server)
486 {
487         struct pmap *pmap = &nfs_server->pmap;
488
489         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
490                 return 1;
491
492         if (nfs_mount_data_version >= 4)
493                 return probe_port(nfs_server, probe_nfs3_first, probe_tcp_first);
494         else
495                 return probe_port(nfs_server, probe_nfs2_only, probe_udp_only);
496 }
497
498 static int probe_mntport(clnt_addr_t *mnt_server)
499 {
500         struct pmap *pmap = &mnt_server->pmap;
501
502         if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
503                 return 1;
504
505         if (nfs_mount_data_version >= 4)
506                 return probe_port(mnt_server, probe_mnt3_first, probe_udp_first);
507         else
508                 return probe_port(mnt_server, probe_mnt1_first, probe_udp_only);
509 }
510
511 /**
512  * probe_bothports - discover the RPC endpoints of mountd and NFS server
513  * @mnt_server: pointer to address and pmap argument for mountd results
514  * @nfs_server: pointer to address and pmap argument for NFS server
515  *
516  * Returns 1 if successful, otherwise zero if some error occurred.
517  * Note that the arguments are both input and output arguments.
518  *
519  * A side effect of calling this function is that rpccreateerr is set.
520  */
521 int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
522 {
523         struct pmap *nfs_pmap = &nfs_server->pmap;
524         struct pmap *mnt_pmap = &mnt_server->pmap;
525         struct pmap save_nfs, save_mnt;
526         int res;
527         const unsigned long *probe_vers;
528
529         if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers)
530                 nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers);
531         else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers)
532                 mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
533         if (nfs_pmap->pm_vers)
534                 goto version_fixed;
535
536         memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
537         memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
538         probe_vers = (nfs_mount_data_version >= 4) ?
539                         probe_mnt3_first : probe_mnt1_first;
540
541         for (; *probe_vers; probe_vers++) {
542                 nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
543                 if ((res = probe_nfsport(nfs_server) != 0)) {
544                         mnt_pmap->pm_vers = *probe_vers;
545                         if ((res = probe_mntport(mnt_server)) != 0)
546                                 return 1;
547                         memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
548                 }
549                 switch (rpc_createerr.cf_stat) {
550                 case RPC_PROGVERSMISMATCH:
551                 case RPC_PROGNOTREGISTERED:
552                         break;
553                 default:
554                         goto out_bad;
555                 }
556                 memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
557         }
558
559 out_bad:
560         return 0;
561
562 version_fixed:
563         if (!probe_nfsport(nfs_server))
564                 goto out_bad;
565         return probe_mntport(mnt_server);
566 }
567
568 static int probe_statd(void)
569 {
570         struct sockaddr_in addr;
571         unsigned short port;
572
573         memset(&addr, 0, sizeof(addr));
574         addr.sin_family = AF_INET;
575         addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
576         port = getport(&addr, 100024, 1, IPPROTO_UDP);
577
578         if (port == 0)
579                 return 0;
580         addr.sin_port = htons(port);
581
582         if (clnt_ping(&addr, 100024, 1, IPPROTO_UDP, NULL) <= 0)
583                 return 0;
584
585         return 1;
586 }
587
588 /**
589  * start_statd - attempt to start rpc.statd
590  *
591  * Returns 1 if statd is running; otherwise zero.
592  */
593 int start_statd(void)
594 {
595 #ifdef START_STATD
596         struct stat stb;
597 #endif
598
599         if (probe_statd())
600                 return 1;
601
602 #ifdef START_STATD
603         if (stat(START_STATD, &stb) == 0) {
604                 if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) {
605                         system(START_STATD);
606                         if (probe_statd())
607                                 return 1;
608                 }
609         }
610 #endif
611
612         return 0;
613 }
614
615 /**
616  * nfs_call_umount - ask the server to remove a share from it's rmtab
617  * @mnt_server: address of RPC MNT program server
618  * @argp: directory path of share to "unmount"
619  *
620  * Returns one if the unmount call succeeded; zero if the unmount
621  * failed for any reason.
622  *
623  * Note that a side effect of calling this function is that rpccreateerr
624  * is set.
625  */
626 int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
627 {
628         CLIENT *clnt;
629         enum clnt_stat res = 0;
630         int msock;
631
632         if (!probe_mntport(mnt_server))
633                 return 0;
634         clnt = mnt_openclnt(mnt_server, &msock);
635         if (!clnt)
636                 return 0;
637         res = clnt_call(clnt, MOUNTPROC_UMNT,
638                         (xdrproc_t)xdr_dirpath, (caddr_t)argp,
639                         (xdrproc_t)xdr_void, NULL,
640                         TIMEOUT);
641         mnt_closeclnt(clnt, msock);
642
643         if (res == RPC_SUCCESS)
644                 return 1;
645         return 0;
646 }
647
648 /**
649  * mnt_openclnt - get a handle for a remote mountd service
650  * @mnt_server: address and pmap arguments of mountd service
651  * @msock: returns a file descriptor of the underlying transport socket
652  *
653  * Returns an active handle for the remote's mountd service
654  */
655 CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
656 {
657         struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
658         struct pmap *mnt_pmap = &mnt_server->pmap;
659         CLIENT *clnt = NULL;
660
661         mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
662         *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
663                                 TRUE, FALSE);
664         if (*msock == RPC_ANYSOCK) {
665                 if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
666                         /*
667                          * Probably in-use by a TIME_WAIT connection,
668                          * It is worth waiting a while and trying again.
669                          */
670                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
671                 return NULL;
672         }
673
674         switch (mnt_pmap->pm_prot) {
675         case IPPROTO_UDP:
676                 clnt = clntudp_bufcreate(mnt_saddr,
677                                          mnt_pmap->pm_prog, mnt_pmap->pm_vers,
678                                          RETRY_TIMEOUT, msock,
679                                          MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
680                 break;
681         case IPPROTO_TCP:
682                 clnt = clnttcp_create(mnt_saddr,
683                                       mnt_pmap->pm_prog, mnt_pmap->pm_vers,
684                                       msock,
685                                       MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
686                 break;
687         }
688         if (clnt) {
689                 /* try to mount hostname:dirname */
690                 clnt->cl_auth = authunix_create_default();
691                 return clnt;
692         }
693         return NULL;
694 }
695
696 /**
697  * mnt_closeclnt - terminate a handle for a remote mountd service
698  * @clnt: pointer to an active handle for a remote mountd service
699  * @msock: file descriptor of the underlying transport socket
700  *
701  */
702 void mnt_closeclnt(CLIENT *clnt, int msock)
703 {
704         auth_destroy(clnt->cl_auth);
705         clnt_destroy(clnt);
706         close(msock);
707 }
708
709 /**
710  * clnt_ping - send an RPC ping to the remote RPC service endpoint
711  * @saddr: server's address
712  * @prog: target RPC program number
713  * @vers: target RPC version number
714  * @prot: target RPC protocol
715  * @caddr: filled in with our network address
716  *
717  * Sigh... getport() doesn't actually check the version number.
718  * In order to make sure that the server actually supports the service
719  * we're requesting, we open and RPC client, and fire off a NULL
720  * RPC call.
721  *
722  * caddr is the network address that the server will use to call us back.
723  * On multi-homed clients, this address depends on which NIC we use to
724  * route requests to the server.
725  *
726  * Returns one if successful, otherwise zero.
727  */
728 int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
729                 const unsigned long vers, const unsigned int prot,
730                 struct sockaddr_in *caddr)
731 {
732         CLIENT *clnt = NULL;
733         int sock, stat;
734         static char clnt_res;
735         struct sockaddr dissolve;
736
737         rpc_createerr.cf_stat = stat = 0;
738         sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
739         if (sock == RPC_ANYSOCK) {
740                 if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
741                         /*
742                          * TCP timeout. Bubble up the error to see 
743                          * how it should be handled.
744                          */
745                         rpc_createerr.cf_stat = RPC_TIMEDOUT;
746                 }
747                 return 0;
748         }
749
750         if (caddr) {
751                 /* Get the address of our end of this connection */
752                 socklen_t len = sizeof(*caddr);
753                 if (getsockname(sock, caddr, &len) != 0)
754                         caddr->sin_family = 0;
755         }
756
757         switch(prot) {
758         case IPPROTO_UDP:
759                 /* The socket is connected (so we could getsockname successfully),
760                  * but some servers on multi-homed hosts reply from
761                  * the wrong address, so if we stay connected, we lose the reply.
762                  */
763                 dissolve.sa_family = AF_UNSPEC;
764                 connect(sock, &dissolve, sizeof(dissolve));
765
766                 clnt = clntudp_bufcreate(saddr, prog, vers,
767                                          RETRY_TIMEOUT, &sock,
768                                          RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
769                 break;
770         case IPPROTO_TCP:
771                 clnt = clnttcp_create(saddr, prog, vers, &sock,
772                                       RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
773                 break;
774         }
775         if (!clnt) {
776                 close(sock);
777                 return 0;
778         }
779         memset(&clnt_res, 0, sizeof(clnt_res));
780         stat = clnt_call(clnt, NULLPROC,
781                          (xdrproc_t)xdr_void, (caddr_t)NULL,
782                          (xdrproc_t)xdr_void, (caddr_t)&clnt_res,
783                          TIMEOUT);
784         if (stat) {
785                 clnt_geterr(clnt, &rpc_createerr.cf_error);
786                 rpc_createerr.cf_stat = stat;
787         }
788         clnt_destroy(clnt);
789         close(sock);
790
791         if (stat == RPC_SUCCESS)
792                 return 1;
793         else
794                 return 0;
795 }
796
797 /**
798  * get_client_address - acquire our local network address
799  * @saddr: server's address
800  * @caddr: filled in with our network address
801  *
802  * Discover a network address that the server will use to call us back.
803  * On multi-homed clients, this address depends on which NIC we use to
804  * route requests to the server.
805  *
806  * Use a connected datagram socket so as not to leave a socket in TIME_WAIT.
807  *
808  * Returns one if successful, otherwise zero.
809  */
810 int get_client_address(struct sockaddr_in *saddr, struct sockaddr_in *caddr)
811 {
812         socklen_t len = sizeof(*caddr);
813         int socket, err;
814
815         socket = get_socket(saddr, IPPROTO_UDP, CONNECT_TIMEOUT, FALSE, TRUE);
816         if (socket == RPC_ANYSOCK)
817                 return 0;
818
819         err = getsockname(socket, caddr, &len);
820         close(socket);
821
822         if (err && verbose) {
823                 nfs_error(_("%s: getsockname failed: %s"),
824                                 progname, strerror(errno));
825                 return 0;
826         }
827         return 1;
828 }