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