~ubuntu-branches/ubuntu/lucid/nfs-utils/lucid

« back to all changes in this revision

Viewing changes to support/nfs/rpc_socket.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Langasek
  • Date: 2009-06-06 01:19:54 UTC
  • mto: (12.1.24 karmic)
  • mto: This revision was merged to the branch mainline in revision 14.
  • Revision ID: james.westby@ubuntu.com-20090606011954-ojnwbumhfwgivicw
Tags: upstream-1.2.0
ImportĀ upstreamĀ versionĀ 1.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 IPV6_SUPPORTED
 
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   /* !IPV6_SUPPORTED */
 
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  /* !IPV6_SUPPORTED */
 
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
                CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)timeout);
 
330
                CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
 
331
        } else
 
332
                (void)close(sock);
 
333
 
 
334
        return client;
 
335
}
 
336
 
 
337
/*
 
338
 * Set up and connect an RPC client for communicating via a stream socket.
 
339
 *
 
340
 * @timeout is initialized upon return
 
341
 *
 
342
 * Returns a pointer to a prepared and connected RPC client if
 
343
 * successful; caller must destroy a non-NULL returned RPC client.
 
344
 * Otherwise NULL, and rpc_createerr.cf_stat is set to reflect the
 
345
 * error.
 
346
 */
 
347
static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
 
348
                                 const socklen_t salen,
 
349
                                 const rpcprog_t program,
 
350
                                 const rpcvers_t version,
 
351
                                 struct timeval *timeout,
 
352
                                 const int resvport)
 
353
{
 
354
        CLIENT *client;
 
355
        int ret, sock;
 
356
#ifdef HAVE_LIBTIRPC
 
357
        struct sockaddr_storage address;
 
358
        const struct netbuf nbuf = {
 
359
                .maxlen         = salen,
 
360
                .len            = salen,
 
361
                .buf            = &address,
 
362
        };
 
363
 
 
364
#else   /* !HAVE_LIBTIRPC */
 
365
 
 
366
        if (sap->sa_family != AF_INET) {
 
367
                rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
 
368
                return NULL;
 
369
        }
 
370
#endif  /* !HAVE_LIBTIRPC */
 
371
 
 
372
        sock = socket((int)sap->sa_family, SOCK_STREAM, IPPROTO_TCP);
 
373
        if (sock == -1) {
 
374
                rpc_createerr.cf_stat = RPC_SYSTEMERROR;
 
375
                rpc_createerr.cf_error.re_errno = errno;
 
376
                return NULL;
 
377
        }
 
378
 
 
379
        if (resvport)
 
380
                ret = nfs_bindresvport(sock, sap->sa_family);
 
381
        else
 
382
                ret = nfs_bind(sock, sap->sa_family);
 
383
        if (ret < 0) {
 
384
                rpc_createerr.cf_stat = RPC_SYSTEMERROR;
 
385
                rpc_createerr.cf_error.re_errno = errno;
 
386
                (void)close(sock);
 
387
                return NULL;
 
388
        }
 
389
 
 
390
        if (timeout->tv_sec == -1)
 
391
                timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
 
392
 
 
393
        ret = nfs_connect_nb(sock, sap, salen, timeout);
 
394
        if (ret != 0) {
 
395
                rpc_createerr.cf_stat = RPC_SYSTEMERROR;
 
396
                rpc_createerr.cf_error.re_errno = errno;
 
397
                (void)close(sock);
 
398
                return NULL;
 
399
        }
 
400
 
 
401
#ifdef HAVE_LIBTIRPC
 
402
        memcpy(nbuf.buf, sap, (size_t)salen);
 
403
        client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
 
404
#else   /* !HAVE_LIBTIRPC */
 
405
        client = clnttcp_create((struct sockaddr_in *)sap,
 
406
                                        program, version, &sock, 0, 0);
 
407
#endif  /* !HAVE_LIBTIRPC */
 
408
        if (client != NULL)
 
409
                CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
 
410
        else
 
411
                (void)close(sock);
 
412
 
 
413
        return client;
 
414
}
 
415
 
 
416
/**
 
417
 * nfs_get_rpcclient - acquire an RPC client
 
418
 * @sap: pointer to socket address of RPC server
 
419
 * @salen: length of socket address
 
420
 * @transport: IPPROTO_ value of transport protocol to use
 
421
 * @program: RPC program number
 
422
 * @version: RPC version number
 
423
 * @timeout: pointer to request timeout (must not be NULL)
 
424
 *
 
425
 * Set up an RPC client for communicating with an RPC program @program
 
426
 * and @version on the server @sap over @transport.  An unprivileged
 
427
 * source port is used.
 
428
 *
 
429
 * Returns a pointer to a prepared RPC client if successful, and
 
430
 * @timeout is initialized; caller must destroy a non-NULL returned RPC
 
431
 * client.  Otherwise returns NULL, and rpc_createerr.cf_stat is set to
 
432
 * reflect the error.
 
433
 */
 
434
CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
 
435
                          const socklen_t salen,
 
436
                          const unsigned short transport,
 
437
                          const rpcprog_t program,
 
438
                          const rpcvers_t version,
 
439
                          struct timeval *timeout)
 
440
{
 
441
        struct sockaddr_in *sin = (struct sockaddr_in *)sap;
 
442
        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
 
443
 
 
444
        switch (sap->sa_family) {
 
445
        case AF_LOCAL:
 
446
                return nfs_get_localclient(sap, salen, program,
 
447
                                                version, timeout);
 
448
        case AF_INET:
 
449
                if (sin->sin_port == 0) {
 
450
                        rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
 
451
                        return NULL;
 
452
                }
 
453
                break;
 
454
        case AF_INET6:
 
455
                if (sin6->sin6_port == 0) {
 
456
                        rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
 
457
                        return NULL;
 
458
                }
 
459
                break;
 
460
        default:
 
461
                rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
 
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
        struct sockaddr_in *sin = (struct sockaddr_in *)sap;
 
505
        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
 
506
 
 
507
        switch (sap->sa_family) {
 
508
        case AF_LOCAL:
 
509
                return nfs_get_localclient(sap, salen, program,
 
510
                                                version, timeout);
 
511
        case AF_INET:
 
512
                if (sin->sin_port == 0) {
 
513
                        rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
 
514
                        return NULL;
 
515
                }
 
516
                break;
 
517
        case AF_INET6:
 
518
                if (sin6->sin6_port == 0) {
 
519
                        rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
 
520
                        return NULL;
 
521
                }
 
522
                break;
 
523
        default:
 
524
                rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
 
525
                return NULL;
 
526
        }
 
527
 
 
528
        switch (transport) {
 
529
        case IPPROTO_TCP:
 
530
                return nfs_get_tcpclient(sap, salen, program, version,
 
531
                                                timeout, 1);
 
532
        case 0:
 
533
        case IPPROTO_UDP:
 
534
                return nfs_get_udpclient(sap, salen, program, version,
 
535
                                                timeout, 1);
 
536
        }
 
537
 
 
538
        rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
 
539
        return NULL;
 
540
}
 
541
 
 
542
/**
 
543
 * nfs_getrpcbyname - convert an RPC program name to a rpcprog_t
 
544
 * @program: default program number to use if names not found in db
 
545
 * @table: pointer to table of 'char *' names to try to find
 
546
 *
 
547
 * Returns program number of first name to be successfully looked
 
548
 * up, or the default program number if all lookups fail.
 
549
 */
 
550
rpcprog_t nfs_getrpcbyname(const rpcprog_t program, const char *table[])
 
551
{
 
552
#ifdef HAVE_GETRPCBYNAME
 
553
        struct rpcent *entry;
 
554
        unsigned int i;
 
555
 
 
556
        if (table != NULL)
 
557
                for (i = 0; table[i] != NULL; i++) {
 
558
                        entry = getrpcbyname(table[i]);
 
559
                        if (entry)
 
560
                                return (rpcprog_t)entry->r_number;
 
561
                }
 
562
#endif  /* HAVE_GETRPCBYNAME */
 
563
 
 
564
        return program;
 
565
}