]> git.decadent.org.uk Git - nfs-utils.git/blob - support/nfs/rpc_socket.c
getport: Clear shared error fields before trying rpcbind queries
[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 #include <netconfig.h>
45 #include <rpc/rpcb_prot.h>
46 #endif  /* HAVE_LIBTIRPC */
47
48 /*
49  * If "-1" is specified in the tv_sec field, use these defaults instead.
50  */
51 #define NFSRPC_TIMEOUT_UDP      (3)
52 #define NFSRPC_TIMEOUT_TCP      (10)
53
54 /*
55  * Set up an RPC client for communicating via a AF_LOCAL socket.
56  *
57  * @timeout is initialized upon return
58  *
59  * Returns a pointer to a prepared RPC client if successful; caller
60  * must destroy a non-NULL returned RPC client.  Otherwise NULL, and
61  * rpc_createerr.cf_stat is set to reflect the error.
62  */
63 static CLIENT *nfs_get_localclient(const struct sockaddr *sap,
64                                    const socklen_t salen,
65                                    const rpcprog_t program,
66                                    const rpcvers_t version,
67                                    struct timeval *timeout)
68 {
69 #ifdef HAVE_LIBTIRPC
70         struct sockaddr_storage address;
71         const struct netbuf nbuf = {
72                 .maxlen         = sizeof(struct sockaddr_un),
73                 .len            = (size_t)salen,
74                 .buf            = &address,
75         };
76 #endif  /* HAVE_LIBTIRPC */
77         CLIENT *client;
78         int sock;
79
80         sock = socket(AF_LOCAL, SOCK_STREAM, 0);
81         if (sock == -1) {
82                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
83                 rpc_createerr.cf_error.re_errno = errno;
84                 return NULL;
85         }
86
87         if (timeout->tv_sec == -1)
88                 timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
89
90 #ifdef HAVE_LIBTIRPC
91         memcpy(nbuf.buf, sap, (size_t)salen);
92         client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
93 #else   /* !HAVE_LIBTIRPC */
94         client = clntunix_create((struct sockaddr_un *)sap,
95                                         program, version, &sock, 0, 0);
96 #endif  /* !HAVE_LIBTIRPC */
97         if (client != NULL)
98                 CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
99         else
100                 (void)close(sock);
101
102         return client;
103 }
104
105 /*
106  * Bind a socket using an unused ephemeral source port.
107  *
108  * Returns zero on success, or returns -1 on error.  errno is
109  * set to reflect the nature of the error.
110  */
111 static int nfs_bind(const int sock, const sa_family_t family)
112 {
113         struct sockaddr_in sin = {
114                 .sin_family             = AF_INET,
115                 .sin_addr.s_addr        = htonl(INADDR_ANY),
116         };
117         struct sockaddr_in6 sin6 = {
118                 .sin6_family            = AF_INET6,
119                 .sin6_addr              = IN6ADDR_ANY_INIT,
120         };
121
122         switch (family) {
123         case AF_INET:
124                 return bind(sock, (struct sockaddr *)&sin,
125                                         (socklen_t)sizeof(sin));
126         case AF_INET6:
127                 return bind(sock, (struct sockaddr *)&sin6,
128                                         (socklen_t)sizeof(sin6));
129         }
130
131         errno = EAFNOSUPPORT;
132         return -1;
133 }
134
135 #ifdef HAVE_LIBTIRPC
136
137 /*
138  * Bind a socket using an unused privileged source port.
139  *
140  * Returns zero on success, or returns -1 on error.  errno is
141  * set to reflect the nature of the error.
142  */
143 static int nfs_bindresvport(const int sock, const sa_family_t family)
144 {
145         struct sockaddr_in sin = {
146                 .sin_family             = AF_INET,
147                 .sin_addr.s_addr        = htonl(INADDR_ANY),
148         };
149         struct sockaddr_in6 sin6 = {
150                 .sin6_family            = AF_INET6,
151                 .sin6_addr              = IN6ADDR_ANY_INIT,
152         };
153
154         switch (family) {
155         case AF_INET:
156                 return bindresvport_sa(sock, (struct sockaddr *)&sin);
157         case AF_INET6:
158                 return bindresvport_sa(sock, (struct sockaddr *)&sin6);
159         }
160
161         errno = EAFNOSUPPORT;
162         return -1;
163 }
164
165 #else   /* !HAVE_LIBTIRPC */
166
167 /*
168  * Bind a socket using an unused privileged source port.
169  *
170  * Returns zero on success, or returns -1 on error.  errno is
171  * set to reflect the nature of the error.
172  */
173 static int nfs_bindresvport(const int sock, const sa_family_t family)
174 {
175         if (family != AF_INET) {
176                 errno = EAFNOSUPPORT;
177                 return -1;
178         }
179
180         return bindresvport(sock, NULL);
181 }
182
183 #endif  /* !HAVE_LIBTIRPC */
184
185 /*
186  * Perform a non-blocking connect on the socket fd.
187  *
188  * @timeout is modified to contain the time remaining (i.e. time provided
189  * minus time elasped).
190  *
191  * Returns zero on success, or returns -1 on error.  errno is
192  * set to reflect the nature of the error.
193  */
194 static int nfs_connect_nb(const int fd, const struct sockaddr *sap,
195                           const socklen_t salen, struct timeval *timeout)
196 {
197         int flags, ret;
198         fd_set rset;
199
200         flags = fcntl(fd, F_GETFL, 0);
201         if (flags < 0)
202                 return -1;
203
204         ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
205         if (ret < 0)
206                 return -1;
207
208         /*
209          * From here on subsequent sys calls could change errno so
210          * we set ret = -errno to capture it in case we decide to
211          * use it later.
212          */
213         ret = connect(fd, sap, salen);
214         if (ret < 0 && errno != EINPROGRESS) {
215                 ret = -1;
216                 goto done;
217         }
218
219         if (ret == 0)
220                 goto done;
221
222         /* now wait */
223         FD_ZERO(&rset);
224         FD_SET(fd, &rset);
225
226         ret = select(fd + 1, NULL, &rset, NULL, timeout);
227         if (ret <= 0) {
228                 if (ret == 0)
229                         errno = ETIMEDOUT;
230                 ret = -1;
231                 goto done;
232         }
233
234         if (FD_ISSET(fd, &rset)) {
235                 int status;
236                 socklen_t len = (socklen_t)sizeof(ret);
237
238                 status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
239                 if (status < 0) {
240                         ret = -1;
241                         goto done;
242                 }
243
244                 /* Oops - something wrong with connect */
245                 if (ret != 0) {
246                         errno = ret;
247                         ret = -1;
248                 }
249         }
250
251 done:
252         (void)fcntl(fd, F_SETFL, flags);
253         return ret;
254 }
255
256 /*
257  * Set up an RPC client for communicating via a datagram socket.
258  * A connected UDP socket is used to detect a missing remote
259  * listener as quickly as possible.
260  *
261  * @timeout is initialized upon return
262  *
263  * Returns a pointer to a prepared RPC client if successful; caller
264  * must destroy a non-NULL returned RPC client.  Otherwise NULL, and
265  * rpc_createerr.cf_stat is set to reflect the error.
266  */
267 static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
268                                  const socklen_t salen,
269                                  const rpcprog_t program,
270                                  const rpcvers_t version,
271                                  struct timeval *timeout,
272                                  const int resvport)
273 {
274         CLIENT *client;
275         int ret, sock;
276 #ifdef HAVE_LIBTIRPC
277         struct sockaddr_storage address;
278         const struct netbuf nbuf = {
279                 .maxlen         = salen,
280                 .len            = salen,
281                 .buf            = &address,
282         };
283
284 #else   /* !HAVE_LIBTIRPC */
285
286         if (sap->sa_family != AF_INET) {
287                 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
288                 return NULL;
289         }
290 #endif  /* !HAVE_LIBTIRPC */
291
292         sock = socket((int)sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
293         if (sock == -1) {
294                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
295                 rpc_createerr.cf_error.re_errno = errno;
296                 return NULL;
297         }
298
299         if (resvport)
300                 ret = nfs_bindresvport(sock, sap->sa_family);
301         else
302                 ret = nfs_bind(sock, sap->sa_family);
303         if (ret < 0) {
304                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
305                 rpc_createerr.cf_error.re_errno = errno;
306                 (void)close(sock);
307                 return NULL;
308         }
309
310         if (timeout->tv_sec == -1)
311                 timeout->tv_sec = NFSRPC_TIMEOUT_UDP;
312
313         ret = nfs_connect_nb(sock, sap, salen, timeout);
314         if (ret != 0) {
315                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
316                 rpc_createerr.cf_error.re_errno = errno;
317                 (void)close(sock);
318                 return NULL;
319         }
320
321 #ifdef HAVE_LIBTIRPC
322         memcpy(nbuf.buf, sap, (size_t)salen);
323         client = clnt_dg_create(sock, &nbuf, program, version, 0, 0);
324 #else   /* !HAVE_LIBTIRPC */
325         client = clntudp_create((struct sockaddr_in *)sap, program,
326                                         version, *timeout, &sock);
327 #endif  /* !HAVE_LIBTIRPC */
328         if (client != NULL) {
329                 struct timeval retry_timeout = { 1, 0 };
330                 CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT,
331                                                 (char *)&retry_timeout);
332                 CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
333         } else
334                 (void)close(sock);
335
336         return client;
337 }
338
339 /*
340  * Set up and connect an RPC client for communicating via a stream socket.
341  *
342  * @timeout is initialized upon return
343  *
344  * Returns a pointer to a prepared and connected RPC client if
345  * successful; caller must destroy a non-NULL returned RPC client.
346  * Otherwise NULL, and rpc_createerr.cf_stat is set to reflect the
347  * error.
348  */
349 static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
350                                  const socklen_t salen,
351                                  const rpcprog_t program,
352                                  const rpcvers_t version,
353                                  struct timeval *timeout,
354                                  const int resvport)
355 {
356         CLIENT *client;
357         int ret, sock;
358 #ifdef HAVE_LIBTIRPC
359         struct sockaddr_storage address;
360         const struct netbuf nbuf = {
361                 .maxlen         = salen,
362                 .len            = salen,
363                 .buf            = &address,
364         };
365
366 #else   /* !HAVE_LIBTIRPC */
367
368         if (sap->sa_family != AF_INET) {
369                 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
370                 return NULL;
371         }
372 #endif  /* !HAVE_LIBTIRPC */
373
374         sock = socket((int)sap->sa_family, SOCK_STREAM, IPPROTO_TCP);
375         if (sock == -1) {
376                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
377                 rpc_createerr.cf_error.re_errno = errno;
378                 return NULL;
379         }
380
381         if (resvport)
382                 ret = nfs_bindresvport(sock, sap->sa_family);
383         else
384                 ret = nfs_bind(sock, sap->sa_family);
385         if (ret < 0) {
386                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
387                 rpc_createerr.cf_error.re_errno = errno;
388                 (void)close(sock);
389                 return NULL;
390         }
391
392         if (timeout->tv_sec == -1)
393                 timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
394
395         ret = nfs_connect_nb(sock, sap, salen, timeout);
396         if (ret != 0) {
397                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
398                 rpc_createerr.cf_error.re_errno = errno;
399                 (void)close(sock);
400                 return NULL;
401         }
402
403 #ifdef HAVE_LIBTIRPC
404         memcpy(nbuf.buf, sap, (size_t)salen);
405         client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
406 #else   /* !HAVE_LIBTIRPC */
407         client = clnttcp_create((struct sockaddr_in *)sap,
408                                         program, version, &sock, 0, 0);
409 #endif  /* !HAVE_LIBTIRPC */
410         if (client != NULL)
411                 CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
412         else
413                 (void)close(sock);
414
415         return client;
416 }
417
418 /**
419  * nfs_get_rpcclient - acquire an RPC client
420  * @sap: pointer to socket address of RPC server
421  * @salen: length of socket address
422  * @transport: IPPROTO_ value of transport protocol to use
423  * @program: RPC program number
424  * @version: RPC version number
425  * @timeout: pointer to request timeout (must not be NULL)
426  *
427  * Set up an RPC client for communicating with an RPC program @program
428  * and @version on the server @sap over @transport.  An unprivileged
429  * source port is used.
430  *
431  * Returns a pointer to a prepared RPC client if successful, and
432  * @timeout is initialized; caller must destroy a non-NULL returned RPC
433  * client.  Otherwise returns NULL, and rpc_createerr.cf_stat is set to
434  * reflect the error.
435  */
436 CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
437                           const socklen_t salen,
438                           const unsigned short transport,
439                           const rpcprog_t program,
440                           const rpcvers_t version,
441                           struct timeval *timeout)
442 {
443         struct sockaddr_in *sin = (struct sockaddr_in *)sap;
444         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
445
446         nfs_clear_rpc_createerr();
447
448         switch (sap->sa_family) {
449         case AF_LOCAL:
450                 return nfs_get_localclient(sap, salen, program,
451                                                 version, timeout);
452         case AF_INET:
453                 if (sin->sin_port == 0) {
454                         rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
455                         return NULL;
456                 }
457                 break;
458         case AF_INET6:
459                 if (sin6->sin6_port == 0) {
460                         rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
461                         return NULL;
462                 }
463                 break;
464         default:
465                 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
466                 return NULL;
467         }
468
469         switch (transport) {
470         case IPPROTO_TCP:
471                 return nfs_get_tcpclient(sap, salen, program, version,
472                                                 timeout, 0);
473         case 0:
474         case IPPROTO_UDP:
475                 return nfs_get_udpclient(sap, salen, program, version,
476                                                 timeout, 0);
477         }
478
479         rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
480         return NULL;
481 }
482
483 /**
484  * nfs_get_priv_rpcclient - acquire an RPC client
485  * @sap: pointer to socket address of RPC server
486  * @salen: length of socket address
487  * @transport: IPPROTO_ value of transport protocol to use
488  * @program: RPC program number
489  * @version: RPC version number
490  * @timeout: pointer to request timeout (must not be NULL)
491  *
492  * Set up an RPC client for communicating with an RPC program @program
493  * and @version on the server @sap over @transport.  A privileged
494  * source port is used.
495  *
496  * Returns a pointer to a prepared RPC client if successful, and
497  * @timeout is initialized; caller must destroy a non-NULL returned RPC
498  * client.  Otherwise returns NULL, and rpc_createerr.cf_stat is set to
499  * reflect the error.
500  */
501 CLIENT *nfs_get_priv_rpcclient(const struct sockaddr *sap,
502                                const socklen_t salen,
503                                const unsigned short transport,
504                                const rpcprog_t program,
505                                const rpcvers_t version,
506                                struct timeval *timeout)
507 {
508         struct sockaddr_in *sin = (struct sockaddr_in *)sap;
509         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
510
511         nfs_clear_rpc_createerr();
512
513         switch (sap->sa_family) {
514         case AF_LOCAL:
515                 return nfs_get_localclient(sap, salen, program,
516                                                 version, timeout);
517         case AF_INET:
518                 if (sin->sin_port == 0) {
519                         rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
520                         return NULL;
521                 }
522                 break;
523         case AF_INET6:
524                 if (sin6->sin6_port == 0) {
525                         rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
526                         return NULL;
527                 }
528                 break;
529         default:
530                 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
531                 return NULL;
532         }
533
534         switch (transport) {
535         case IPPROTO_TCP:
536                 return nfs_get_tcpclient(sap, salen, program, version,
537                                                 timeout, 1);
538         case 0:
539         case IPPROTO_UDP:
540                 return nfs_get_udpclient(sap, salen, program, version,
541                                                 timeout, 1);
542         }
543
544         rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
545         return NULL;
546 }
547
548 /**
549  * nfs_getrpcbyname - convert an RPC program name to a rpcprog_t
550  * @program: default program number to use if names not found in db
551  * @table: pointer to table of 'char *' names to try to find
552  *
553  * Returns program number of first name to be successfully looked
554  * up, or the default program number if all lookups fail.
555  */
556 rpcprog_t nfs_getrpcbyname(const rpcprog_t program, const char *table[])
557 {
558 #ifdef HAVE_GETRPCBYNAME
559         struct rpcent *entry;
560         unsigned int i;
561
562         if (table != NULL)
563                 for (i = 0; table[i] != NULL; i++) {
564                         entry = getrpcbyname(table[i]);
565                         if (entry)
566                                 return (rpcprog_t)entry->r_number;
567                 }
568 #endif  /* HAVE_GETRPCBYNAME */
569
570         return program;
571 }