]> git.decadent.org.uk Git - nfs-utils.git/blob - support/nfs/conn.c
Use socklen_t some more to avoid warnings.
[nfs-utils.git] / support / nfs / conn.c
1 /*
2  * conn.c -- NFS client mount / umount connection code support functions
3  *
4  * 2006-06-06 Amit Gud <agud@redhat.com>
5  * - Moved code snippets to nfs-utils/support/nfs from util-linux/mount/nfsmount.c
6  *
7  */
8
9 #include "config.h"
10 #include <errno.h>
11 #include <unistd.h>
12 #include <rpc/rpc.h>
13 #include <rpc/pmap_prot.h>
14 #include <rpc/pmap_clnt.h>
15 #include <sys/socket.h>
16 #include <sys/stat.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19
20 #include "conn.h"
21
22 #if SIZEOF_SOCKLEN_T - 0 == 0
23 #define socklen_t int
24 #endif
25
26 extern int verbose;
27
28 /* Map an NFS version into the corresponding Mountd version */
29 u_long nfsvers_to_mnt(const u_long vers)
30 {
31         static const u_long nfs_to_mnt[] = { 0, 0, 1, 3 };
32         if (vers <= 3)
33                 return nfs_to_mnt[vers];
34         return 0;
35 }
36
37 /* Map a Mountd version into the corresponding NFS version */
38 u_long mntvers_to_nfs(const u_long vers)
39 {
40         static const u_long mnt_to_nfs[] = { 0, 2, 2, 3 };
41         if (vers <= 3)
42                 return mnt_to_nfs[vers];
43         return 0;
44 }
45
46 /*
47  * Create a socket that is locally bound to a 
48  * reserve or non-reserve port. For any failures,
49  * RPC_ANYSOCK is returned which will cause 
50  * the RPC code to create the socket instead. 
51  */
52 int get_socket(struct sockaddr_in *saddr, u_int p_prot, int resvp)
53 {
54         int so, cc, type;
55         struct sockaddr_in laddr;
56         socklen_t namelen = sizeof(laddr);
57
58         type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
59         if ((so = socket (AF_INET, type, p_prot)) < 0) {
60                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
61                 rpc_createerr.cf_error.re_errno = errno;
62                 if (verbose) {
63                         fprintf(stderr, 
64                                 "mount: Unable to create %s socket: errno %d (%s)\n",
65                                 p_prot == IPPROTO_UDP ? "UDP" : "TCP", 
66                                 errno, strerror(errno));
67                 }
68                 return RPC_ANYSOCK;
69         }
70         laddr.sin_family = AF_INET;
71         laddr.sin_port = 0;
72         laddr.sin_addr.s_addr = htonl(INADDR_ANY);
73         if (resvp) {
74                 if (bindresvport(so, &laddr) < 0) {
75                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
76                         rpc_createerr.cf_error.re_errno = errno;
77                         if (verbose) {
78                                 fprintf(stderr, 
79                                         "mount: Unable to bindresvport %s socket: errno %d (%s)\n",
80                                         p_prot == IPPROTO_UDP ? "UDP" : "TCP", 
81                                         errno, strerror(errno));
82                         }
83                         close(so);
84                         return RPC_ANYSOCK;
85                 }
86         } else {
87                 cc = bind(so, (struct sockaddr *)&laddr, namelen);
88                 if (cc < 0) {
89                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
90                         rpc_createerr.cf_error.re_errno = errno;
91                         if (verbose) {
92                                 fprintf(stderr, 
93                                         "mount: Unable to bind to %s socket: errno %d (%s)\n",
94                                         p_prot == IPPROTO_UDP ? "UDP" : "TCP", 
95                                         errno, strerror(errno));
96                         }
97                         close(so);
98                         return RPC_ANYSOCK;
99                 }
100         }
101         if (type == SOCK_STREAM || type == SOCK_DGRAM) {
102                 cc = connect(so, (struct sockaddr *)saddr, namelen);
103                 if (cc < 0) {
104                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
105                         rpc_createerr.cf_error.re_errno = errno;
106                         if (verbose) {
107                                 fprintf(stderr, 
108                                         "mount: Unable to connect to %s:%d, errno %d (%s)\n",
109                                         inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port),
110                                         errno, strerror(errno));
111                         }
112                         close(so);
113                         return RPC_ANYSOCK;
114                 }
115         }
116         return so;
117 }
118
119 /*
120  * Sigh... getport() doesn't actually check the version number.
121  * In order to make sure that the server actually supports the service
122  * we're requesting, we open and RPC client, and fire off a NULL
123  * RPC call.
124  */
125 int
126 clnt_ping(struct sockaddr_in *saddr, const u_long prog, const u_long vers,
127           const u_int prot, struct sockaddr_in *caddr)
128 {
129         CLIENT *clnt=NULL;
130         int sock, stat;
131         static char clnt_res;
132
133         rpc_createerr.cf_stat = stat = errno = 0;
134         sock = get_socket(saddr, prot, FALSE);
135         if (sock == RPC_ANYSOCK && errno == ETIMEDOUT) {
136                 /*
137                  * TCP timeout. Bubble up the error to see 
138                  * how it should be handled.
139                  */
140                 rpc_createerr.cf_stat = RPC_TIMEDOUT;
141                 goto out_bad;
142         }
143
144         switch(prot) {
145         case IPPROTO_UDP:
146                 clnt = clntudp_bufcreate(saddr, prog, vers,
147                                          RETRY_TIMEOUT, &sock,
148                                          RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
149                 break;
150         case IPPROTO_TCP:
151                 clnt = clnttcp_create(saddr, prog, vers, &sock,
152                                       RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
153                 break;
154         default:
155                 goto out_bad;
156         }
157         if (!clnt)
158                 goto out_bad;
159         memset(&clnt_res, 0, sizeof(clnt_res));
160         stat = clnt_call(clnt, NULLPROC,
161                          (xdrproc_t)xdr_void, (caddr_t)NULL,
162                          (xdrproc_t)xdr_void, (caddr_t)&clnt_res,
163                          TIMEOUT);
164         if (stat) {
165                 clnt_geterr(clnt, &rpc_createerr.cf_error);
166                 rpc_createerr.cf_stat = stat;
167         }
168         clnt_destroy(clnt);
169         if (sock != -1) {
170                 if (caddr) {
171                         /* Get the address of our end of this connection */
172                         socklen_t len = sizeof(*caddr);
173                         if (getsockname(sock, caddr, &len) != 0)
174                                 caddr->sin_family = 0;
175                 }
176                 close(sock);
177         }
178
179         if (stat == RPC_SUCCESS)
180                 return 1;
181
182  out_bad:
183         return 0;
184 }
185
186 CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
187 {
188         struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
189         struct pmap *mnt_pmap = &mnt_server->pmap;
190         CLIENT *clnt = NULL;
191
192         /* contact the mount daemon via TCP */
193         mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
194         *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, TRUE);
195
196         switch (mnt_pmap->pm_prot) {
197         case IPPROTO_UDP:
198                 clnt = clntudp_bufcreate(mnt_saddr,
199                                          mnt_pmap->pm_prog, mnt_pmap->pm_vers,
200                                          RETRY_TIMEOUT, msock,
201                                          MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
202                 break;
203         case IPPROTO_TCP:
204                 clnt = clnttcp_create(mnt_saddr,
205                                       mnt_pmap->pm_prog, mnt_pmap->pm_vers,
206                                       msock,
207                                       MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
208                 break;
209         }
210         if (clnt) {
211                 /* try to mount hostname:dirname */
212                 clnt->cl_auth = authunix_create_default();
213                 return clnt;
214         }
215         return NULL;
216 }
217
218 void mnt_closeclnt(CLIENT *clnt, int msock)
219 {
220         auth_destroy(clnt->cl_auth);
221         clnt_destroy(clnt);
222         close(msock);
223 }
224