~ubuntu-branches/debian/wheezy/autofs/wheezy

« back to all changes in this revision

Viewing changes to lib/rpc_subs.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Langasek
  • Date: 2008-03-08 01:36:09 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20080308013609-cvs4f2ecoyoism02
Tags: 4.1.4+debian-2.1
* Non-maintainer upload.
* High-urgency upload for RC bugfix.
* Add -DLDAP_DEPRECATED to CFLAGS, to fix compatibility with OpenLDAP
  2.4 on 64-bit architectures.  Closes: #463419.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#ident "$Id: rpc_subs.c,v 1.7 2004/11/20 15:08:38 raven Exp $"
 
2
/* ----------------------------------------------------------------------- *
 
3
 *   
 
4
 *  rpc_subs.c - routines for rpc discovery
 
5
 *
 
6
 *   Copyright 2004 Ian Kent <raven@themaw.net> - All Rights Reserved
 
7
 *   Copyright 2004 Jeff Moyer <jmoyer@redaht.com> - All Rights Reserved
 
8
 *
 
9
 *   This program is free software; you can redistribute it and/or modify
 
10
 *   it under the terms of the GNU General Public License as published by
 
11
 *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
 
12
 *   USA; either version 2 of the License, or (at your option) any later
 
13
 *   version; incorporated herein by reference.
 
14
 *
 
15
 * ----------------------------------------------------------------------- */
 
16
 
 
17
#include <rpc/rpc.h>
 
18
#include <rpc/pmap_prot.h>
 
19
#include <nfs/nfs.h>
 
20
#include <linux/nfs2.h>
 
21
#include <linux/nfs3.h>
 
22
#include <rpc/xdr.h>
 
23
 
 
24
#include <unistd.h>
 
25
#include <sys/socket.h>
 
26
#include <netdb.h>
 
27
#include <netinet/in.h>
 
28
#include <sys/fcntl.h>
 
29
#include <errno.h>
 
30
 
 
31
#include "automount.h"
 
32
 
 
33
#define PMAP_TOUT_UDP   2
 
34
#define PMAP_TOUT_TCP   3
 
35
 
 
36
struct conn_info {
 
37
        const char *host;
 
38
        unsigned short port;
 
39
        unsigned long program;
 
40
        unsigned long version;
 
41
        struct protoent *proto;
 
42
        unsigned int send_sz;
 
43
        unsigned int recv_sz;
 
44
        struct timeval timeout;
 
45
};
 
46
 
 
47
/*
 
48
 * Create a UDP RPC client
 
49
 */
 
50
static CLIENT* create_udp_client(struct conn_info *info)
 
51
{
 
52
        int fd;
 
53
        CLIENT *client;
 
54
        struct sockaddr_in laddr, raddr;
 
55
        struct hostent *hp;
 
56
 
 
57
        if (info->proto->p_proto != IPPROTO_UDP)
 
58
                return NULL;
 
59
 
 
60
        memset(&laddr, 0, sizeof(laddr));
 
61
        memset(&raddr, 0, sizeof(raddr));
 
62
 
 
63
        hp = gethostbyname(info->host);
 
64
        if (!hp)
 
65
                return NULL;
 
66
 
 
67
        raddr.sin_family = AF_INET;
 
68
        raddr.sin_port = htons(info->port);
 
69
        memcpy(&raddr.sin_addr.s_addr, hp->h_addr, hp->h_length);
 
70
 
 
71
        /*
 
72
         * bind to any unused port.  If we left this up to the rpc
 
73
         * layer, it would bind to a reserved port, which has been shown
 
74
         * to exhaust the reserved port range in some situations.
 
75
         */
 
76
        fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
 
77
        if (fd < 0)
 
78
                return NULL;
 
79
        laddr.sin_family = AF_INET;
 
80
        laddr.sin_port = 0;
 
81
        laddr.sin_addr.s_addr = htonl(INADDR_ANY);
 
82
 
 
83
        if (bind(fd, (struct sockaddr *)&laddr, 
 
84
                 sizeof(struct sockaddr_in)) < 0) {
 
85
                close(fd);
 
86
                fd = RPC_ANYSOCK;
 
87
                /* FALLTHROUGH */
 
88
        }
 
89
 
 
90
        client = clntudp_bufcreate(&raddr,
 
91
                                   info->program, info->version,
 
92
                                   info->timeout, &fd,
 
93
                                   info->send_sz, info->recv_sz);
 
94
        if (client)
 
95
                clnt_control(client, CLSET_FD_CLOSE, NULL);
 
96
 
 
97
        return client;
 
98
}
 
99
 
 
100
/*
 
101
 *  Perform a non-blocking connect on the socket fd.
 
102
 *
 
103
 *  tout contains the timeout.  It will be modified to contain the time
 
104
 *  remaining (i.e. time provided - time elasped).
 
105
 */
 
106
static int connect_nb(int fd, struct sockaddr_in *addr, struct timeval *tout)
 
107
{
 
108
        int flags, ret, len;
 
109
        fd_set wset, rset;
 
110
 
 
111
        flags = fcntl(fd, F_GETFL, 0);
 
112
        if (flags < 0)
 
113
                return -1;
 
114
 
 
115
        ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
 
116
        if (ret < 0)
 
117
                return -1;
 
118
 
 
119
        /* 
 
120
         * From here on subsequent sys calls could change errno so
 
121
         * we set ret = -errno to capture it in case we decide to
 
122
         * use it later.
 
123
         */
 
124
        ret = connect(fd, (struct sockaddr *)addr, sizeof(struct sockaddr));
 
125
        if (ret < 0 && errno != EINPROGRESS) {
 
126
                ret = -errno;
 
127
                goto done;
 
128
        }
 
129
 
 
130
        if (ret == 0)
 
131
                goto done;
 
132
 
 
133
        /* now wait */
 
134
        FD_ZERO(&rset);
 
135
        FD_SET(fd, &rset);
 
136
        wset = rset;
 
137
 
 
138
        ret = select(fd + 1, &rset, &wset, NULL, tout);
 
139
        if (ret <= 0) {
 
140
                if (ret == 0)
 
141
                        ret = -ETIMEDOUT;
 
142
                else
 
143
                        ret = -errno;
 
144
                goto done;
 
145
        }
 
146
 
 
147
        if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
 
148
                int stat;
 
149
 
 
150
                len = sizeof(ret);
 
151
                stat = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
 
152
                if (stat < 0) {
 
153
                        ret = -errno;
 
154
                        goto done;
 
155
                }
 
156
 
 
157
                /* Oops - something wrong with connect */
 
158
                if (ret)
 
159
                        ret = -ret;
 
160
        }
 
161
 
 
162
done:
 
163
        fcntl(fd, F_SETFL, flags);
 
164
        return ret;
 
165
}
 
166
 
 
167
/*
 
168
 * Create a TCP RPC client using non-blocking connect
 
169
 */
 
170
static CLIENT* create_tcp_client(struct conn_info *info)
 
171
{
 
172
        int fd;
 
173
        CLIENT *client;
 
174
        struct sockaddr_in addr;
 
175
        struct hostent *hp;
 
176
        int ret;
 
177
 
 
178
        if (info->proto->p_proto != IPPROTO_TCP)
 
179
                return NULL;
 
180
 
 
181
        memset(&addr, 0, sizeof(addr));
 
182
 
 
183
        hp = gethostbyname(info->host);
 
184
        if (!hp)
 
185
                return NULL;
 
186
 
 
187
        addr.sin_family = AF_INET;
 
188
        addr.sin_port = htons(info->port);
 
189
        memcpy(&addr.sin_addr.s_addr, hp->h_addr, hp->h_length);
 
190
 
 
191
        fd = socket(PF_INET, SOCK_STREAM, info->proto->p_proto);
 
192
        if (fd < 0)
 
193
                return NULL;
 
194
 
 
195
        ret = connect_nb(fd, &addr, &info->timeout);
 
196
        if (ret < 0)
 
197
                goto out_close;
 
198
 
 
199
        client = clnttcp_create(&addr,
 
200
                                info->program, info->version, &fd,
 
201
                                info->send_sz, info->recv_sz);
 
202
        if (!client)
 
203
                goto out_close;
 
204
 
 
205
        /* Close socket fd on destroy, as is default for rpcowned fds */
 
206
        if  (!clnt_control(client, CLSET_FD_CLOSE, NULL)) {
 
207
                clnt_destroy(client);
 
208
                goto out_close;
 
209
        }
 
210
 
 
211
        return client;
 
212
 
 
213
out_close:
 
214
        close(fd);
 
215
        return NULL;
 
216
}
 
217
 
 
218
static unsigned short portmap_getport(struct conn_info *info)
 
219
{
 
220
        struct conn_info pmap_info;
 
221
        unsigned short port = 0;
 
222
        CLIENT *client;
 
223
        enum clnt_stat stat;
 
224
        struct pmap parms;
 
225
 
 
226
        pmap_info.host = info->host;
 
227
        pmap_info.port = PMAPPORT;
 
228
        pmap_info.program = PMAPPROG;
 
229
        pmap_info.version = PMAPVERS;
 
230
        pmap_info.proto = info->proto;
 
231
        pmap_info.send_sz = RPCSMALLMSGSIZE;
 
232
        pmap_info.recv_sz = RPCSMALLMSGSIZE;
 
233
        pmap_info.timeout.tv_sec = PMAP_TOUT_UDP;
 
234
        pmap_info.timeout.tv_usec = 0;
 
235
 
 
236
        if (info->proto->p_proto == IPPROTO_TCP) {
 
237
                pmap_info.timeout.tv_sec = PMAP_TOUT_TCP;
 
238
                client = create_tcp_client(&pmap_info);
 
239
        } else
 
240
                client = create_udp_client(&pmap_info);
 
241
 
 
242
        if (!client)
 
243
                return 0;
 
244
        
 
245
        parms.pm_prog = info->program;
 
246
        parms.pm_vers = info->version;
 
247
        parms.pm_prot = info->proto->p_proto;
 
248
        parms.pm_port = 0;
 
249
 
 
250
        stat = clnt_call(client, PMAPPROC_GETPORT,
 
251
                         (xdrproc_t) xdr_pmap, (caddr_t) &parms,
 
252
                         (xdrproc_t) xdr_u_short, (caddr_t) &port,
 
253
                         pmap_info.timeout);
 
254
 
 
255
        clnt_destroy(client);
 
256
 
 
257
        if (stat != RPC_SUCCESS)
 
258
                return 0;
 
259
 
 
260
        return port;
 
261
}
 
262
 
 
263
static int rpc_ping_proto(const char *host,
 
264
                          unsigned long nfs_version,
 
265
                          const char *proto,
 
266
                          long seconds, long micros)
 
267
{
 
268
        struct conn_info info;
 
269
        CLIENT *client;
 
270
        enum clnt_stat stat;
 
271
        struct protoent *prot;
 
272
 
 
273
        prot = getprotobyname(proto);
 
274
        if (!prot)
 
275
                return 1;
 
276
 
 
277
        info.host = host;
 
278
        info.program = NFS_PROGRAM;
 
279
        info.version = nfs_version;
 
280
        info.proto = prot;
 
281
        info.send_sz = 0;
 
282
        info.recv_sz = 0;
 
283
        info.timeout.tv_sec = seconds;
 
284
        info.timeout.tv_usec = micros;
 
285
 
 
286
        info.port = portmap_getport(&info);
 
287
        if (!info.port)
 
288
                return 0;
 
289
 
 
290
        if (prot->p_proto == IPPROTO_UDP) {
 
291
                info.send_sz = UDPMSGSIZE;
 
292
                info.recv_sz = UDPMSGSIZE;
 
293
                client = create_udp_client(&info);
 
294
        } else
 
295
                client = create_tcp_client(&info);
 
296
 
 
297
        if (!client)
 
298
                return 0;
 
299
 
 
300
        clnt_control(client, CLSET_TIMEOUT, (char *) &info.timeout);
 
301
        clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) &info.timeout);
 
302
 
 
303
        stat = clnt_call(client, NFSPROC_NULL,
 
304
                         (xdrproc_t) xdr_void, 0, (xdrproc_t) xdr_void, 0,
 
305
                         info.timeout);
 
306
 
 
307
        clnt_destroy(client);
 
308
 
 
309
        if (stat != RPC_SUCCESS)
 
310
                return 0;
 
311
 
 
312
        return 1;
 
313
}
 
314
 
 
315
static unsigned int rpc_ping_v2(const char *host, long seconds, long micros)
 
316
{
 
317
        unsigned int status = RPC_PING_FAIL;
 
318
 
 
319
        status = rpc_ping_proto(host, NFS2_VERSION, "udp", seconds, micros);
 
320
        if (status)
 
321
                return RPC_PING_V2 | RPC_PING_UDP;
 
322
 
 
323
        status = rpc_ping_proto(host, NFS2_VERSION, "tcp", seconds, micros);
 
324
        if (status)
 
325
                return RPC_PING_V2 | RPC_PING_TCP;
 
326
 
 
327
        return status;
 
328
}
 
329
 
 
330
static unsigned int rpc_ping_v3(const char *host, long seconds, long micros)
 
331
{
 
332
        unsigned int status = RPC_PING_FAIL;
 
333
 
 
334
        status = rpc_ping_proto(host, NFS3_VERSION, "udp", seconds, micros);
 
335
        if (status)
 
336
                return RPC_PING_V3 | RPC_PING_UDP;
 
337
 
 
338
        status = rpc_ping_proto(host, NFS3_VERSION, "tcp", seconds, micros);
 
339
        if (status)
 
340
                return RPC_PING_V3 | RPC_PING_TCP;
 
341
 
 
342
        return status;
 
343
}
 
344
 
 
345
unsigned int rpc_ping(const char *host, long seconds, long micros)
 
346
{
 
347
        unsigned int status;
 
348
 
 
349
        status = rpc_ping_v2(host, seconds, micros);
 
350
        if (status)
 
351
                return status;
 
352
 
 
353
        status = rpc_ping_v3(host, seconds, micros);
 
354
        
 
355
        return status;
 
356
}
 
357
 
 
358
static double elapsed(struct timeval start, struct timeval end)
 
359
{
 
360
        double t1, t2;
 
361
        t1 =  (double)start.tv_sec + (double)start.tv_usec/(1000*1000);
 
362
        t2 =  (double)end.tv_sec + (double)end.tv_usec/(1000*1000);
 
363
        return t2-t1;
 
364
}
 
365
 
 
366
int rpc_time(const char *host,
 
367
             unsigned int ping_vers, unsigned int ping_proto,
 
368
             long seconds, long micros, double *result)
 
369
{
 
370
        int status;
 
371
        double taken;
 
372
        struct timeval start, end;
 
373
        struct timezone tz;
 
374
        char *proto = (ping_proto & RPC_PING_UDP) ? "udp" : "tcp";
 
375
 
 
376
        gettimeofday(&start, &tz);
 
377
        status = rpc_ping_proto(host, ping_vers, proto, seconds, micros);
 
378
        gettimeofday(&end, &tz);
 
379
 
 
380
        if (!status) {
 
381
                return 0;
 
382
        }
 
383
 
 
384
        taken = elapsed(start, end);
 
385
 
 
386
        if (result != NULL)
 
387
                *result = taken;
 
388
 
 
389
        return status;
 
390
}
 
391
 
 
392
#if 0
 
393
#include <stdio.h>
 
394
 
 
395
int main(int argc, char **argv)
 
396
{
 
397
        int ret;
 
398
        double res = 0.0;
 
399
 
 
400
        ret = rpc_ping("budgie", 10, 0);
 
401
        printf("ret = %d\n", ret);
 
402
 
 
403
        res = 0.0;
 
404
        ret = rpc_time("raven", NFS2_VERSION, RPC_PING_TCP, 10, 0, &res);
 
405
        printf("v2 tcp ret = %d, res = %f\n", ret, res);
 
406
 
 
407
        res = 0.0;
 
408
        ret = rpc_time("raven", NFS3_VERSION, RPC_PING_TCP, 10, 0, &res);
 
409
        printf("v3 tcp ret = %d, res = %f\n", ret, res);
 
410
 
 
411
        res = 0.0;
 
412
        ret = rpc_time("raven", NFS2_VERSION, RPC_PING_UDP, 10, 0, &res);
 
413
        printf("v2 udp ret = %d, res = %f\n", ret, res);
 
414
 
 
415
        res = 0.0;
 
416
        ret = rpc_time("raven", NFS3_VERSION, RPC_PING_UDP, 10, 0, &res);
 
417
        printf("v3 udp ret = %d, res = %f\n", ret, res);
 
418
 
 
419
        exit(0);
 
420
}
 
421
#endif