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