~ubuntu-branches/ubuntu/saucy/zeromq3/saucy

« back to all changes in this revision

Viewing changes to src/tcp_address.cpp

  • Committer: Package Import Robot
  • Author(s): Alessandro Ghedini
  • Date: 2012-06-04 21:21:09 UTC
  • Revision ID: package-import@ubuntu.com-20120604212109-b7b3m0rn21o8oo2q
Tags: upstream-3.1.0~beta+dfsg
ImportĀ upstreamĀ versionĀ 3.1.0~beta+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    Copyright (c) 2009-2011 250bpm s.r.o.
 
3
    Copyright (c) 2007-2009 iMatix Corporation
 
4
    Copyright (c) 2007-2011 Other contributors as noted in the AUTHORS file
 
5
 
 
6
    This file is part of 0MQ.
 
7
 
 
8
    0MQ is free software; you can redistribute it and/or modify it under
 
9
    the terms of the GNU Lesser General Public License as published by
 
10
    the Free Software Foundation; either version 3 of the License, or
 
11
    (at your option) any later version.
 
12
 
 
13
    0MQ is distributed in the hope that it will be useful,
 
14
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
    GNU Lesser General Public License for more details.
 
17
 
 
18
    You should have received a copy of the GNU Lesser General Public License
 
19
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
*/
 
21
 
 
22
#include <string.h>
 
23
#include <string>
 
24
 
 
25
#include "tcp_address.hpp"
 
26
#include "platform.hpp"
 
27
#include "stdint.hpp"
 
28
#include "err.hpp"
 
29
#include "ip.hpp"
 
30
 
 
31
#ifdef ZMQ_HAVE_WINDOWS
 
32
#include "windows.hpp"
 
33
#else
 
34
#include <sys/types.h>
 
35
#include <arpa/inet.h>
 
36
#include <netinet/tcp.h>
 
37
#include <netdb.h>
 
38
#endif
 
39
 
 
40
//  Some platforms (notably Darwin/OSX and NetBSD) do not define all AI_
 
41
//  flags for getaddrinfo(). This can be worked around safely by defining
 
42
//  these to 0.
 
43
#ifndef AI_ADDRCONFIG
 
44
#define AI_ADDRCONFIG 0
 
45
#endif
 
46
 
 
47
#if defined ZMQ_HAVE_SOLARIS
 
48
 
 
49
#include <sys/sockio.h>
 
50
#include <net/if.h>
 
51
#include <unistd.h>
 
52
#include <stdlib.h>
 
53
 
 
54
//  On Solaris platform, network interface name can be queried by ioctl.
 
55
int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_)
 
56
{
 
57
    //  TODO: Unused parameter, IPv6 support not implemented for Solaris.
 
58
    (void) ipv4only_;
 
59
 
 
60
    //  Create a socket.
 
61
    int fd = open_socket (AF_INET, SOCK_DGRAM, 0);
 
62
    zmq_assert (fd != -1);
 
63
 
 
64
    //  Retrieve number of interfaces.
 
65
    lifnum ifn;
 
66
    ifn.lifn_family = AF_INET;
 
67
    ifn.lifn_flags = 0;
 
68
    int rc = ioctl (fd, SIOCGLIFNUM, (char*) &ifn);
 
69
    zmq_assert (rc != -1);
 
70
 
 
71
    //  Allocate memory to get interface names.
 
72
    size_t ifr_size = sizeof (struct lifreq) * ifn.lifn_count;
 
73
    char *ifr = (char*) malloc (ifr_size);
 
74
    alloc_assert (ifr);
 
75
    
 
76
    //  Retrieve interface names.
 
77
    lifconf ifc;
 
78
    ifc.lifc_family = AF_INET;
 
79
    ifc.lifc_flags = 0;
 
80
    ifc.lifc_len = ifr_size;
 
81
    ifc.lifc_buf = ifr;
 
82
    rc = ioctl (fd, SIOCGLIFCONF, (char*) &ifc);
 
83
    zmq_assert (rc != -1);
 
84
 
 
85
    //  Find the interface with the specified name and AF_INET family.
 
86
    bool found = false;
 
87
    lifreq *ifrp = ifc.lifc_req;
 
88
    for (int n = 0; n < (int) (ifc.lifc_len / sizeof (lifreq));
 
89
          n ++, ifrp ++) {
 
90
        if (!strcmp (nic_, ifrp->lifr_name)) {
 
91
            rc = ioctl (fd, SIOCGLIFADDR, (char*) ifrp);
 
92
            zmq_assert (rc != -1);
 
93
            if (ifrp->lifr_addr.ss_family == AF_INET) {
 
94
                address.ipv4 = *(sockaddr_in*) &ifrp->lifr_addr;
 
95
                found = true;
 
96
                break;
 
97
            }
 
98
        }
 
99
    }
 
100
 
 
101
    //  Clean-up.
 
102
    free (ifr);
 
103
    close (fd);
 
104
 
 
105
    if (!found) {
 
106
        errno = ENODEV;
 
107
        return -1;
 
108
    }
 
109
 
 
110
    return 0;
 
111
}
 
112
 
 
113
#elif defined ZMQ_HAVE_AIX || defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_ANDROID
 
114
 
 
115
#include <sys/types.h>
 
116
#include <unistd.h>
 
117
#include <sys/ioctl.h>
 
118
#include <net/if.h>
 
119
 
 
120
int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_)
 
121
{
 
122
    //  TODO: Unused parameter, IPv6 support not implemented for AIX or HP/UX.
 
123
    (void) ipv4only_;
 
124
 
 
125
    //  Create a socket.
 
126
    int sd = open_socket (AF_INET, SOCK_DGRAM, 0);
 
127
    zmq_assert (sd != -1);
 
128
 
 
129
    struct ifreq ifr; 
 
130
 
 
131
    //  Copy interface name for ioctl get.
 
132
    strncpy (ifr.ifr_name, nic_, sizeof (ifr.ifr_name));
 
133
 
 
134
    //  Fetch interface address.
 
135
    int rc = ioctl (sd, SIOCGIFADDR, (caddr_t) &ifr, sizeof (struct ifreq));
 
136
 
 
137
    //  Clean up.
 
138
    close (sd);
 
139
 
 
140
    if (rc == -1) {
 
141
        errno = ENODEV;
 
142
        return -1;
 
143
    }
 
144
 
 
145
    memcpy (&address.ipv4.sin_addr, &((sockaddr_in*) &ifr.ifr_addr)->sin_addr,
 
146
        sizeof (in_addr));
 
147
 
 
148
    return 0;    
 
149
}
 
150
 
 
151
#elif ((defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD ||\
 
152
    defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_OPENBSD ||\
 
153
    defined ZMQ_HAVE_QNXNTO || defined ZMQ_HAVE_NETBSD)\
 
154
    && defined ZMQ_HAVE_IFADDRS)
 
155
 
 
156
#include <ifaddrs.h>
 
157
 
 
158
//  On these platforms, network interface name can be queried
 
159
//  using getifaddrs function.
 
160
int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_)
 
161
{
 
162
    //  Get the addresses.
 
163
    ifaddrs* ifa = NULL;
 
164
    int rc = getifaddrs (&ifa);
 
165
    zmq_assert (rc == 0);    
 
166
    zmq_assert (ifa != NULL);
 
167
 
 
168
    //  Find the corresponding network interface.
 
169
    bool found = false;
 
170
    for (ifaddrs *ifp = ifa; ifp != NULL ;ifp = ifp->ifa_next)
 
171
    {
 
172
        if (ifp->ifa_addr == NULL)
 
173
            continue;
 
174
 
 
175
        int family = ifp->ifa_addr->sa_family;
 
176
 
 
177
        if ((family == AF_INET
 
178
             || (!ipv4only_ && family == AF_INET6))
 
179
            && !strcmp (nic_, ifp->ifa_name))
 
180
        {
 
181
            memcpy (&address, ifp->ifa_addr,
 
182
                    (family == AF_INET) ? sizeof (struct sockaddr_in)
 
183
                                        : sizeof (struct sockaddr_in6));
 
184
            found = true;
 
185
            break;
 
186
        }
 
187
    }
 
188
 
 
189
    //  Clean-up;
 
190
    freeifaddrs (ifa);
 
191
 
 
192
    if (!found) {
 
193
        errno = ENODEV;
 
194
        return -1;
 
195
    }
 
196
 
 
197
    return 0;
 
198
}
 
199
 
 
200
#else
 
201
 
 
202
//  On other platforms we assume there are no sane interface names.
 
203
//  This is true especially of Windows.
 
204
int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_)
 
205
{
 
206
    //  All unused parameters.
 
207
    (void) nic_;
 
208
    (void) ipv4only_;
 
209
 
 
210
    errno = ENODEV;
 
211
    return -1;
 
212
}
 
213
 
 
214
#endif
 
215
 
 
216
int zmq::tcp_address_t::resolve_interface (char const *interface_,
 
217
    bool ipv4only_)
 
218
{
 
219
    //  Initialize temporary output pointers with storage address.
 
220
    sockaddr_storage ss;
 
221
    sockaddr *out_addr = (sockaddr *) &ss;
 
222
    socklen_t out_addrlen;
 
223
 
 
224
    //  Initialise IP-format family/port and populate temporary output pointers
 
225
    //  with the address.
 
226
    if (ipv4only_) {
 
227
        sockaddr_in ip4_addr;
 
228
        memset (&ip4_addr, 0, sizeof (ip4_addr));
 
229
        ip4_addr.sin_family = AF_INET;
 
230
        ip4_addr.sin_addr.s_addr = htonl (INADDR_ANY);
 
231
        out_addrlen = (socklen_t) sizeof (ip4_addr);
 
232
        memcpy (out_addr, &ip4_addr, out_addrlen);
 
233
    } else {
 
234
        sockaddr_in6 ip6_addr;
 
235
        memset (&ip6_addr, 0, sizeof (ip6_addr));
 
236
        ip6_addr.sin6_family = AF_INET6;
 
237
        memcpy (&ip6_addr.sin6_addr, &in6addr_any, sizeof (in6addr_any));
 
238
        out_addrlen = (socklen_t) sizeof (ip6_addr);
 
239
        memcpy (out_addr, &ip6_addr, out_addrlen);
 
240
    }
 
241
 
 
242
    //  * resolves to INADDR_ANY or in6addr_any.
 
243
    if (strcmp (interface_, "*") == 0) {
 
244
        zmq_assert (out_addrlen <= (socklen_t) sizeof (address));
 
245
        memcpy (&address, out_addr, out_addrlen);
 
246
        return 0;
 
247
    }
 
248
 
 
249
    //  Try to resolve the string as a NIC name.
 
250
    int rc = resolve_nic_name (interface_, ipv4only_);
 
251
    if (rc != 0 && errno != ENODEV)
 
252
        return rc;
 
253
    if (rc == 0) {
 
254
        zmq_assert (out_addrlen <= (socklen_t) sizeof (address));
 
255
        memcpy (&address, out_addr, out_addrlen);
 
256
        return 0;
 
257
    }
 
258
 
 
259
    //  There's no such interface name. Assume literal address.
 
260
#if defined ZMQ_HAVE_OPENVMS && defined __ia64
 
261
    __addrinfo64 *res = NULL;
 
262
    __addrinfo64 req;
 
263
#else
 
264
    addrinfo *res = NULL;
 
265
    addrinfo req;
 
266
#endif
 
267
    memset (&req, 0, sizeof (req));
 
268
 
 
269
    //  Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for
 
270
    //  IPv4-in-IPv6 addresses.
 
271
    req.ai_family = ipv4only_ ? AF_INET : AF_INET6;
 
272
 
 
273
    //  Arbitrary, not used in the output, but avoids duplicate results.
 
274
    req.ai_socktype = SOCK_STREAM;
 
275
 
 
276
    //  Restrict hostname/service to literals to avoid any DNS lookups or
 
277
    //  service-name irregularity due to indeterminate socktype.
 
278
    req.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
 
279
 
 
280
#ifndef ZMQ_HAVE_WINDOWS
 
281
    //  Windows by default maps IPv4 addresses into IPv6. In this API we only
 
282
    //  require IPv4-mapped addresses when no native IPv6 interfaces are
 
283
    //  available (~AI_ALL).  This saves an additional DNS roundtrip for IPv4
 
284
    //  addresses.
 
285
    if (req.ai_family == AF_INET6)
 
286
        req.ai_flags |= AI_V4MAPPED;
 
287
#endif
 
288
 
 
289
    //  Resolve the literal address. Some of the error info is lost in case
 
290
    //  of error, however, there's no way to report EAI errors via errno.
 
291
    rc = getaddrinfo (interface_, NULL, &req, &res);
 
292
    if (rc) {
 
293
        errno = ENODEV;
 
294
        return -1;
 
295
    }
 
296
 
 
297
    //  Use the first result.
 
298
    zmq_assert ((size_t) (res->ai_addrlen) <= sizeof (address));
 
299
    memcpy (&address, res->ai_addr, res->ai_addrlen);
 
300
 
 
301
    //  Cleanup getaddrinfo after copying the possibly referenced result.
 
302
    if (res)
 
303
        freeaddrinfo (res);
 
304
 
 
305
    return 0;
 
306
}
 
307
 
 
308
int zmq::tcp_address_t::resolve_hostname (const char *hostname_, bool ipv4only_)
 
309
{
 
310
    //  Set up the query.
 
311
#if defined ZMQ_HAVE_OPENVMS && defined __ia64 && __INITIAL_POINTER_SIZE == 64
 
312
    __addrinfo64 req;
 
313
#else
 
314
    addrinfo req;
 
315
#endif
 
316
    memset (&req, 0, sizeof (req));
 
317
 
 
318
    //  Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for
 
319
    //  IPv4-in-IPv6 addresses.
 
320
    req.ai_family = ipv4only_ ? AF_INET : AF_INET6;
 
321
 
 
322
    //  Need to choose one to avoid duplicate results from getaddrinfo() - this
 
323
    //  doesn't really matter, since it's not included in the addr-output.
 
324
    req.ai_socktype = SOCK_STREAM;
 
325
    
 
326
#ifndef ZMQ_HAVE_WINDOWS
 
327
    //  Windows by default maps IPv4 addresses into IPv6. In this API we only
 
328
    //  require IPv4-mapped addresses when no native IPv6 interfaces are
 
329
    //  available.  This saves an additional DNS roundtrip for IPv4 addresses.
 
330
    if (req.ai_family == AF_INET6)
 
331
        req.ai_flags |= AI_V4MAPPED;
 
332
#endif
 
333
 
 
334
    //  Resolve host name. Some of the error info is lost in case of error,
 
335
    //  however, there's no way to report EAI errors via errno.
 
336
#if defined ZMQ_HAVE_OPENVMS && defined __ia64 && __INITIAL_POINTER_SIZE == 64
 
337
    __addrinfo64 *res;
 
338
#else
 
339
    addrinfo *res;
 
340
#endif
 
341
    int rc = getaddrinfo (hostname_, NULL, &req, &res);
 
342
    if (rc) {
 
343
        switch (rc) {
 
344
        case EAI_MEMORY:
 
345
            errno = ENOMEM;
 
346
            break;
 
347
        default:
 
348
            errno = EINVAL;
 
349
            break;
 
350
        }
 
351
        return -1;
 
352
    }
 
353
 
 
354
    //  Copy first result to output addr with hostname and service.
 
355
    zmq_assert ((size_t) (res->ai_addrlen) <= sizeof (address));
 
356
    memcpy (&address, res->ai_addr, res->ai_addrlen);
 
357
 
 
358
    freeaddrinfo (res);
 
359
    
 
360
    return 0;
 
361
}
 
362
 
 
363
zmq::tcp_address_t::tcp_address_t ()
 
364
{
 
365
    memset (&address, 0, sizeof (address));
 
366
}
 
367
 
 
368
zmq::tcp_address_t::~tcp_address_t ()
 
369
{
 
370
}
 
371
 
 
372
int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv4only_)
 
373
{
 
374
    //  Find the ':' at end that separates address from the port number.
 
375
    const char *delimiter = strrchr (name_, ':');
 
376
    if (!delimiter) {
 
377
        errno = EINVAL;
 
378
        return -1;
 
379
    }
 
380
 
 
381
    //  Separate the address/port.
 
382
    std::string addr_str (name_, delimiter - name_);
 
383
    std::string port_str (delimiter + 1);
 
384
 
 
385
    //  Remove square brackets around the address, if any.
 
386
    if (!addr_str.empty () && addr_str [0] == '[' &&
 
387
          addr_str [addr_str.size () - 1] == ']')
 
388
        addr_str = addr_str.substr (1, addr_str.size () - 2);
 
389
 
 
390
    //  Parse the port number (0 is not a valid port).
 
391
    uint16_t port = (uint16_t) atoi (port_str.c_str());
 
392
    if (port == 0) {
 
393
        errno = EINVAL;
 
394
        return -1;
 
395
    }
 
396
 
 
397
    //  Resolve the IP address.
 
398
    int rc;
 
399
    if (local_)
 
400
        rc = resolve_interface (addr_str.c_str (), ipv4only_);
 
401
    else
 
402
        rc = resolve_hostname (addr_str.c_str (), ipv4only_);
 
403
    if (rc != 0)
 
404
        return -1;
 
405
 
 
406
    //  Set the port into the address structure.
 
407
    if (address.generic.sa_family == AF_INET6)
 
408
        address.ipv6.sin6_port = htons (port);
 
409
    else
 
410
        address.ipv4.sin_port = htons (port);
 
411
 
 
412
    return 0;
 
413
}
 
414
 
 
415
sockaddr *zmq::tcp_address_t::addr ()
 
416
{
 
417
    return &address.generic;
 
418
}
 
419
 
 
420
socklen_t zmq::tcp_address_t::addrlen ()
 
421
{
 
422
    if (address.generic.sa_family == AF_INET6)
 
423
        return (socklen_t) sizeof (address.ipv6);
 
424
    else
 
425
        return (socklen_t) sizeof (address.ipv4);
 
426
}
 
427
 
 
428
#if defined ZMQ_HAVE_WINDOWS
 
429
unsigned short zmq::tcp_address_t::family ()
 
430
#else
 
431
sa_family_t zmq::tcp_address_t::family ()
 
432
#endif
 
433
{
 
434
    return address.generic.sa_family;
 
435
}
 
436