]> git.decadent.org.uk Git - nfs-utils.git/blob - support/nfs/rpc_socket.c
nfs-utils: replace function-specific switches with HAVE_LIBTIRPC
[nfs-utils.git] / support / nfs / rpc_socket.c
1 /*
2  * Generic RPC client socket-level APIs for nfs-utils
3  *
4  * Copyright (C) 2008 Oracle Corporation.  All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 021110-1307, USA.
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <netdb.h>
36 #include <arpa/inet.h>
37
38 #include <rpc/rpc.h>
39 #include <rpc/pmap_prot.h>
40
41 #include "nfsrpc.h"
42
43 #ifdef HAVE_LIBTIRPC
44
45 /*
46  * Most of the headers under /usr/include/tirpc are currently
47  * unusable for various reasons.  We statically define the bits
48  * we need here until the official headers are fixed.
49  *
50  * The commonly used RPC calls such as CLNT_CALL and CLNT_DESTROY
51  * are actually virtual functions in both the legacy and TI-RPC
52  * implementations.  The proper _CALL or _DESTROY will be invoked
53  * no matter if we used a legacy clnt_create() or clnt_tli_create()
54  * from libtirpc.
55  */
56
57 #include <tirpc/netconfig.h>
58 #include <tirpc/rpc/rpcb_prot.h>
59
60 /* definitions from tirpc/rpc/types.h */
61
62 /*
63  * The netbuf structure is used for transport-independent address storage.
64  */
65 struct netbuf {
66         unsigned int    maxlen;
67         unsigned int    len;
68         void            *buf;
69 };
70
71 /* definitions from tirpc/rpc/clnt.h */
72
73 /*
74  * Low level clnt create routine for connectionless transports, e.g. udp.
75  */
76 extern CLIENT *clnt_dg_create(const int, const struct netbuf *,
77                               const rpcprog_t, const rpcvers_t,
78                               const u_int, const u_int);
79
80 /*
81  * Low level clnt create routine for connectionful transports, e.g. tcp.
82  */
83 extern CLIENT *clnt_vc_create(const int, const struct netbuf *,
84                               const rpcprog_t, const rpcvers_t,
85                               u_int, u_int);
86
87 #endif  /* HAVE_LIBTIRPC */
88
89 /*
90  * If "-1" is specified in the tv_sec field, use these defaults instead.
91  */
92 #define NFSRPC_TIMEOUT_UDP      (3)
93 #define NFSRPC_TIMEOUT_TCP      (10)
94
95 /*
96  * Set up an RPC client for communicating via a AF_LOCAL socket.
97  *
98  * @timeout is initialized upon return
99  *
100  * Returns a pointer to a prepared RPC client if successful; caller
101  * must destroy a non-NULL returned RPC client.  Otherwise NULL, and
102  * rpc_createerr.cf_stat is set to reflect the error.
103  */
104 static CLIENT *nfs_get_localclient(const struct sockaddr *sap,
105                                    const socklen_t salen,
106                                    const rpcprog_t program,
107                                    const rpcvers_t version,
108                                    struct timeval *timeout)
109 {
110 #ifdef HAVE_LIBTIRPC
111         struct sockaddr_storage address;
112         const struct netbuf nbuf = {
113                 .maxlen         = sizeof(struct sockaddr_un),
114                 .len            = (size_t)salen,
115                 .buf            = &address,
116         };
117 #endif  /* HAVE_LIBTIRPC */
118         CLIENT *client;
119         int sock;
120
121         sock = socket(AF_LOCAL, SOCK_STREAM, 0);
122         if (sock == -1) {
123                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
124                 rpc_createerr.cf_error.re_errno = errno;
125                 return NULL;
126         }
127
128         if (timeout->tv_sec == -1)
129                 timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
130
131 #ifdef HAVE_LIBTIRPC
132         memcpy(nbuf.buf, sap, (size_t)salen);
133         client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
134 #else   /* !HAVE_LIBTIRPC */
135         client = clntunix_create((struct sockaddr_un *)sap,
136                                         program, version, &sock, 0, 0);
137 #endif  /* !HAVE_LIBTIRPC */
138         if (client != NULL)
139                 CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
140         else
141                 (void)close(sock);
142
143         return client;
144 }
145
146 /*
147  * Bind a socket using an unused ephemeral source port.
148  *
149  * Returns zero on success, or returns -1 on error.  errno is
150  * set to reflect the nature of the error.
151  */
152 static int nfs_bind(const int sock, const sa_family_t family)
153 {
154         struct sockaddr_in sin = {
155                 .sin_family             = AF_INET,
156                 .sin_addr.s_addr        = htonl(INADDR_ANY),
157         };
158         struct sockaddr_in6 sin6 = {
159                 .sin6_family            = AF_INET6,
160                 .sin6_addr              = IN6ADDR_ANY_INIT,
161         };
162
163         switch (family) {
164         case AF_INET:
165                 return bind(sock, (struct sockaddr *)&sin,
166                                         (socklen_t)sizeof(sin));
167         case AF_INET6:
168                 return bind(sock, (struct sockaddr *)&sin6,
169                                         (socklen_t)sizeof(sin6));
170         }
171
172         errno = EAFNOSUPPORT;
173         return -1;
174 }
175
176 /*
177  * Perform a non-blocking connect on the socket fd.
178  *
179  * @timeout is modified to contain the time remaining (i.e. time provided
180  * minus time elasped).
181  *
182  * Returns zero on success, or returns -1 on error.  errno is
183  * set to reflect the nature of the error.
184  */
185 static int nfs_connect_nb(const int fd, const struct sockaddr *sap,
186                           const socklen_t salen, struct timeval *timeout)
187 {
188         int flags, ret;
189         fd_set rset;
190
191         flags = fcntl(fd, F_GETFL, 0);
192         if (flags < 0)
193                 return -1;
194
195         ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
196         if (ret < 0)
197                 return -1;
198
199         /*
200          * From here on subsequent sys calls could change errno so
201          * we set ret = -errno to capture it in case we decide to
202          * use it later.
203          */
204         ret = connect(fd, sap, salen);
205         if (ret < 0 && errno != EINPROGRESS) {
206                 ret = -1;
207                 goto done;
208         }
209
210         if (ret == 0)
211                 goto done;
212
213         /* now wait */
214         FD_ZERO(&rset);
215         FD_SET(fd, &rset);
216
217         ret = select(fd + 1, NULL, &rset, NULL, timeout);
218         if (ret <= 0) {
219                 if (ret == 0)
220                         errno = ETIMEDOUT;
221                 ret = -1;
222                 goto done;
223         }
224
225         if (FD_ISSET(fd, &rset)) {
226                 int status;
227                 socklen_t len = (socklen_t)sizeof(ret);
228
229                 status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
230                 if (status < 0) {
231                         ret = -1;
232                         goto done;
233                 }
234
235                 /* Oops - something wrong with connect */
236                 if (ret != 0) {
237                         errno = ret;
238                         ret = -1;
239                 }
240         }
241
242 done:
243         (void)fcntl(fd, F_SETFL, flags);
244         return ret;
245 }
246
247 /*
248  * Set up an RPC client for communicating via a datagram socket.
249  * A connected UDP socket is used to detect a missing remote
250  * listener as quickly as possible.
251  *
252  * @timeout is initialized upon return
253  *
254  * Returns a pointer to a prepared RPC client if successful; caller
255  * must destroy a non-NULL returned RPC client.  Otherwise NULL, and
256  * rpc_createerr.cf_stat is set to reflect the error.
257  */
258 static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
259                                  const socklen_t salen,
260                                  const rpcprog_t program,
261                                  const rpcvers_t version,
262                                  struct timeval *timeout)
263 {
264         CLIENT *client;
265         int ret, sock;
266 #ifdef HAVE_LIBTIRPC
267         struct sockaddr_storage address;
268         const struct netbuf nbuf = {
269                 .maxlen         = salen,
270                 .len            = salen,
271                 .buf            = &address,
272         };
273
274 #else   /* !HAVE_LIBTIRPC */
275
276         if (sap->sa_family != AF_INET) {
277                 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
278                 return NULL;
279         }
280 #endif  /* !HAVE_LIBTIRPC */
281
282         sock = socket((int)sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
283         if (sock == -1) {
284                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
285                 rpc_createerr.cf_error.re_errno = errno;
286                 return NULL;
287         }
288
289         ret = nfs_bind(sock, sap->sa_family);
290         if (ret < 0) {
291                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
292                 rpc_createerr.cf_error.re_errno = errno;
293                 (void)close(sock);
294                 return NULL;
295         }
296
297         if (timeout->tv_sec == -1)
298                 timeout->tv_sec = NFSRPC_TIMEOUT_UDP;
299
300         ret = nfs_connect_nb(sock, sap, salen, timeout);
301         if (ret != 0) {
302                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
303                 rpc_createerr.cf_error.re_errno = errno;
304                 (void)close(sock);
305                 return NULL;
306         }
307
308 #ifdef HAVE_LIBTIRPC
309         memcpy(nbuf.buf, sap, (size_t)salen);
310         client = clnt_dg_create(sock, &nbuf, program, version, 0, 0);
311 #else   /* !HAVE_LIBTIRPC */
312         client = clntudp_create((struct sockaddr_in *)sap, program,
313                                         version, *timeout, &sock);
314 #endif  /* !HAVE_LIBTIRPC */
315         if (client != NULL) {
316                 CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)timeout);
317                 CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
318         } else
319                 (void)close(sock);
320
321         return client;
322 }
323
324 /*
325  * Set up and connect an RPC client for communicating via a stream socket.
326  *
327  * @timeout is initialized upon return
328  *
329  * Returns a pointer to a prepared and connected RPC client if
330  * successful; caller must destroy a non-NULL returned RPC client.
331  * Otherwise NULL, and rpc_createerr.cf_stat is set to reflect the
332  * error.
333  */
334 static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
335                                  const socklen_t salen,
336                                  const rpcprog_t program,
337                                  const rpcvers_t version,
338                                  struct timeval *timeout)
339 {
340         CLIENT *client;
341         int ret, sock;
342 #ifdef HAVE_LIBTIRPC
343         struct sockaddr_storage address;
344         const struct netbuf nbuf = {
345                 .maxlen         = salen,
346                 .len            = salen,
347                 .buf            = &address,
348         };
349
350 #else   /* !HAVE_LIBTIRPC */
351
352         if (sap->sa_family != AF_INET) {
353                 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
354                 return NULL;
355         }
356 #endif  /* !HAVE_LIBTIRPC */
357
358         sock = socket((int)sap->sa_family, SOCK_STREAM, IPPROTO_TCP);
359         if (sock == -1) {
360                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
361                 rpc_createerr.cf_error.re_errno = errno;
362                 return NULL;
363         }
364
365         ret = nfs_bind(sock, sap->sa_family);
366         if (ret < 0) {
367                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
368                 rpc_createerr.cf_error.re_errno = errno;
369                 (void)close(sock);
370                 return NULL;
371         }
372
373         if (timeout->tv_sec == -1)
374                 timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
375
376         ret = nfs_connect_nb(sock, sap, salen, timeout);
377         if (ret != 0) {
378                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
379                 rpc_createerr.cf_error.re_errno = errno;
380                 (void)close(sock);
381                 return NULL;
382         }
383
384 #ifdef HAVE_LIBTIRPC
385         memcpy(nbuf.buf, sap, (size_t)salen);
386         client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
387 #else   /* !HAVE_LIBTIRPC */
388         client = clnttcp_create((struct sockaddr_in *)sap,
389                                         program, version, &sock, 0, 0);
390 #endif  /* !HAVE_LIBTIRPC */
391         if (client != NULL)
392                 CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
393         else
394                 (void)close(sock);
395
396         return client;
397 }
398
399 /**
400  * nfs_get_rpcclient - acquire an RPC client
401  * @sap: pointer to socket address of RPC server
402  * @salen: length of socket address
403  * @transport: IPPROTO_ value of transport protocol to use
404  * @program: RPC program number
405  * @version: RPC version number
406  * @timeout: pointer to request timeout (must not be NULL)
407  *
408  * Set up an RPC client for communicating with an RPC program @program
409  * and @version on the server @sap over @transport.
410  *
411  * Returns a pointer to a prepared RPC client if successful, and
412  * @timeout is initialized; caller must destroy a non-NULL returned RPC
413  * client.  Otherwise returns NULL, and rpc_createerr.cf_stat is set to
414  * reflect the error.
415  */
416 CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
417                           const socklen_t salen,
418                           const unsigned short transport,
419                           const rpcprog_t program,
420                           const rpcvers_t version,
421                           struct timeval *timeout)
422 {
423         struct sockaddr_in *sin = (struct sockaddr_in *)sap;
424         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
425
426         switch (sap->sa_family) {
427         case AF_LOCAL:
428                 return nfs_get_localclient(sap, salen, program,
429                                                 version, timeout);
430         case AF_INET:
431                 if (sin->sin_port == 0) {
432                         rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
433                         return NULL;
434                 }
435                 break;
436         case AF_INET6:
437                 if (sin6->sin6_port == 0) {
438                         rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
439                         return NULL;
440                 }
441                 break;
442         default:
443                 rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
444                 return NULL;
445         }
446
447         switch (transport) {
448         case IPPROTO_TCP:
449                 return nfs_get_tcpclient(sap, salen, program, version, timeout);
450         case 0:
451         case IPPROTO_UDP:
452                 return nfs_get_udpclient(sap, salen, program, version, timeout);
453         }
454
455         rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
456         return NULL;
457 }
458
459 /**
460  * nfs_getrpcbyname - convert an RPC program name to a rpcprog_t
461  * @program: default program number to use if names not found in db
462  * @table: pointer to table of 'char *' names to try to find
463  *
464  * Returns program number of first name to be successfully looked
465  * up, or the default program number if all lookups fail.
466  */
467 rpcprog_t nfs_getrpcbyname(const rpcprog_t program, const char *table[])
468 {
469 #ifdef HAVE_GETRPCBYNAME
470         struct rpcent *entry;
471         unsigned int i;
472
473         if (table != NULL)
474                 for (i = 0; table[i] != NULL; i++) {
475                         entry = getrpcbyname(table[i]);
476                         if (entry)
477                                 return (rpcprog_t)entry->r_number;
478                 }
479 #endif  /* HAVE_GETRPCBYNAME */
480
481         return program;
482 }