ip.cpp 12.9 KB
Newer Older
Martin Sustrik's avatar
Martin Sustrik committed
1
/*
2 3
    Copyright (c) 2007-2011 iMatix Corporation
    Copyright (c) 2007-2011 Other contributors as noted in the AUTHORS file
Martin Sustrik's avatar
Martin Sustrik committed
4 5 6 7

    This file is part of 0MQ.

    0MQ is free software; you can redistribute it and/or modify it under
8
    the terms of the GNU Lesser General Public License as published by
Martin Sustrik's avatar
Martin Sustrik committed
9 10 11 12 13 14
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    0MQ is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
    GNU Lesser General Public License for more details.
Martin Sustrik's avatar
Martin Sustrik committed
16

17
    You should have received a copy of the GNU Lesser General Public License
Martin Sustrik's avatar
Martin Sustrik committed
18 19 20 21 22 23 24 25 26 27
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <string>

#include "ip.hpp"
#include "err.hpp"
28
#include "platform.hpp"
Martin Sustrik's avatar
Martin Sustrik committed
29 30
#include "stdint.hpp"

31 32 33 34 35 36 37 38 39 40 41 42
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS
#include <sys/un.h>
#endif

#if !defined ZMQ_HAVE_WINDOWS
#include <fcntl.h>
#endif

#if defined ZMQ_HAVE_OPENVMS
#include <ioctl.h>
#endif

Martin Sustrik's avatar
Martin Sustrik committed
43
#if defined ZMQ_HAVE_SOLARIS
Martin Sustrik's avatar
Martin Sustrik committed
44 45 46 47 48 49

#include <sys/sockio.h>
#include <net/if.h>
#include <unistd.h>

//  On Solaris platform, network interface name can be queried by ioctl.
50 51
static int resolve_nic_name (struct sockaddr* addr_, char const *interface_,
    bool ipv4only_)
Martin Sustrik's avatar
Martin Sustrik committed
52
{
53
    //  TODO: Unused parameter, IPv6 support not implemented for Solaris.
54
    (void) ipv4only_;
55

Martin Sustrik's avatar
Martin Sustrik committed
56 57
    //  Create a socket.
    int fd = socket (AF_INET, SOCK_DGRAM, 0);
Martin Sustrik's avatar
Martin Sustrik committed
58
    zmq_assert (fd != -1);
Martin Sustrik's avatar
Martin Sustrik committed
59 60 61

    //  Retrieve number of interfaces.
    lifnum ifn;
62
    ifn.lifn_family = AF_INET;
Martin Sustrik's avatar
Martin Sustrik committed
63 64
    ifn.lifn_flags = 0;
    int rc = ioctl (fd, SIOCGLIFNUM, (char*) &ifn);
Martin Sustrik's avatar
Martin Sustrik committed
65
    zmq_assert (rc != -1);
Martin Sustrik's avatar
Martin Sustrik committed
66 67 68 69

    //  Allocate memory to get interface names.
    size_t ifr_size = sizeof (struct lifreq) * ifn.lifn_count;
    char *ifr = (char*) malloc (ifr_size);
70
    alloc_assert (ifr);
Martin Sustrik's avatar
Martin Sustrik committed
71 72 73
    
    //  Retrieve interface names.
    lifconf ifc;
74
    ifc.lifc_family = AF_INET;
Martin Sustrik's avatar
Martin Sustrik committed
75 76 77 78
    ifc.lifc_flags = 0;
    ifc.lifc_len = ifr_size;
    ifc.lifc_buf = ifr;
    rc = ioctl (fd, SIOCGLIFCONF, (char*) &ifc);
Martin Sustrik's avatar
Martin Sustrik committed
79
    zmq_assert (rc != -1);
Martin Sustrik's avatar
Martin Sustrik committed
80 81 82 83 84 85 86 87

    //  Find the interface with the specified name and AF_INET family.
    bool found = false;
    lifreq *ifrp = ifc.lifc_req;
    for (int n = 0; n < (int) (ifc.lifc_len / sizeof (lifreq));
          n ++, ifrp ++) {
        if (!strcmp (interface_, ifrp->lifr_name)) {
            rc = ioctl (fd, SIOCGLIFADDR, (char*) ifrp);
Martin Sustrik's avatar
Martin Sustrik committed
88
            zmq_assert (rc != -1);
Martin Sustrik's avatar
Martin Sustrik committed
89
            if (ifrp->lifr_addr.ss_family == AF_INET) {
90
                *(sockaddr_in*) addr_ = *(sockaddr_in*) &ifrp->lifr_addr;
Martin Sustrik's avatar
Martin Sustrik committed
91 92 93 94 95 96 97 98 99 100 101
                found = true;
                break;
            }
        }
    }

    //  Clean-up.
    free (ifr);
    close (fd);

    if (!found) {
Martin Sustrik's avatar
Martin Sustrik committed
102 103
        errno = ENODEV;
        return -1;
Martin Sustrik's avatar
Martin Sustrik committed
104 105 106 107 108
    }

    return 0;
}

Martin Sustrik's avatar
Martin Sustrik committed
109
#elif defined ZMQ_HAVE_AIX || ZMQ_HAVE_HPUX
Martin Sustrik's avatar
Martin Sustrik committed
110 111 112 113 114 115

#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>

116 117
static int resolve_nic_name (struct sockaddr* addr_, char const *interface_,
    bool ipv4only_)
Martin Sustrik's avatar
Martin Sustrik committed
118
{
119
    //  TODO: Unused parameter, IPv6 support not implemented for AIX or HP/UX.
120
    (void) ipv4only_;
121

Martin Sustrik's avatar
Martin Sustrik committed
122 123
    //  Create a socket.
    int sd = socket (AF_INET, SOCK_DGRAM, 0);
Martin Sustrik's avatar
Martin Sustrik committed
124
    zmq_assert (sd != -1);
Martin Sustrik's avatar
Martin Sustrik committed
125 126 127 128

    struct ifreq ifr; 

    //  Copy interface name for ioctl get.
129
    strncpy (ifr.ifr_name, interface_, sizeof (ifr.ifr_name));
Martin Sustrik's avatar
Martin Sustrik committed
130 131 132 133 134 135 136

    //  Fetch interface address.
    int rc = ioctl (sd, SIOCGIFADDR, (caddr_t) &ifr, sizeof (struct ifreq));

    //  Clean up.
    close (sd);

Martin Sustrik's avatar
Martin Sustrik committed
137 138
    if (rc == -1) {
        errno = ENODEV;
Martin Sustrik's avatar
Martin Sustrik committed
139 140 141
        return -1;
    }

Martin Sustrik's avatar
Martin Sustrik committed
142 143 144
    struct sockaddr *sa = (struct sockaddr *) &ifr.ifr_addr;
    *addr_ = ((sockaddr_in*)sa)->sin_addr;
    return 0;    
Martin Sustrik's avatar
Martin Sustrik committed
145 146
}

Martin Sustrik's avatar
Martin Sustrik committed
147 148
#elif ((defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD ||\
    defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_OPENBSD ||\
Martin Lucina's avatar
Martin Lucina committed
149 150
    defined ZMQ_HAVE_QNXNTO || defined ZMQ_HAVE_NETBSD)\
    && defined ZMQ_HAVE_IFADDRS)
Martin Sustrik's avatar
Martin Sustrik committed
151 152 153 154 155

#include <ifaddrs.h>

//  On these platforms, network interface name can be queried
//  using getifaddrs function.
156 157
static int resolve_nic_name (struct sockaddr* addr_, char const *interface_,
    bool ipv4only_)
Martin Sustrik's avatar
Martin Sustrik committed
158 159 160 161
{
    //  Get the addresses.
    ifaddrs* ifa = NULL;
    int rc = getifaddrs (&ifa);
Martin Sustrik's avatar
Martin Sustrik committed
162 163
    zmq_assert (rc == 0);    
    zmq_assert (ifa != NULL);
Martin Sustrik's avatar
Martin Sustrik committed
164 165 166 167

    //  Find the corresponding network interface.
    bool found = false;
    for (ifaddrs *ifp = ifa; ifp != NULL ;ifp = ifp->ifa_next)
168 169 170 171 172 173 174 175 176
    {
        if (ifp->ifa_addr == NULL)
            continue;

        int family = ifp->ifa_addr->sa_family;

        if ((family == AF_INET
             || (!ipv4only_ && family == AF_INET6))
            && !strcmp (interface_, ifp->ifa_name))
Martin Sustrik's avatar
Martin Sustrik committed
177
        {
178 179 180
            memcpy (addr_, ifp->ifa_addr,
                    (family == AF_INET) ? sizeof (struct sockaddr_in)
                                        : sizeof (struct sockaddr_in6));
Martin Sustrik's avatar
Martin Sustrik committed
181
            found = true;
182
            break;
Martin Sustrik's avatar
Martin Sustrik committed
183
        }
184
    }
Martin Sustrik's avatar
Martin Sustrik committed
185 186 187 188 189

    //  Clean-up;
    freeifaddrs (ifa);

    if (!found) {
Martin Sustrik's avatar
Martin Sustrik committed
190 191
        errno = ENODEV;
        return -1;
Martin Sustrik's avatar
Martin Sustrik committed
192 193 194 195 196 197 198
    }

    return 0;
}

#else

Martin Sustrik's avatar
Martin Sustrik committed
199 200
//  On other platforms we assume there are no sane interface names.
//  This is true especially of Windows.
201 202
static int resolve_nic_name (struct sockaddr* addr_, char const *interface_,
    bool ipv4only_)
Martin Sustrik's avatar
Martin Sustrik committed
203
{
204 205 206 207 208
    //  All unused parameters.
    (void) addr_;
    (void) interface_;
    (void) ipv4only_;

Martin Sustrik's avatar
Martin Sustrik committed
209 210
    errno = ENODEV;
    return -1;
Martin Sustrik's avatar
Martin Sustrik committed
211 212 213 214
}

#endif

Martin Sustrik's avatar
Martin Sustrik committed
215
int zmq::resolve_ip_interface (sockaddr_storage* addr_, socklen_t *addr_len_,
216
    char const *interface_, bool ipv4only_)
Martin Sustrik's avatar
Martin Sustrik committed
217
{
Martin Sustrik's avatar
Martin Sustrik committed
218
    //  Find the ':' at end that separates NIC name from service.
Martin Sustrik's avatar
Martin Sustrik committed
219
    const char *delimiter = strrchr (interface_, ':');
Martin Sustrik's avatar
Martin Sustrik committed
220 221 222 223 224
    if (!delimiter) {
        errno = EINVAL;
        return -1;
    }

Martin Sustrik's avatar
Martin Sustrik committed
225
    //  Separate the name/port.
226
    std::string iface (interface_, delimiter - interface_);
Martin Sustrik's avatar
Martin Sustrik committed
227
    std::string service (delimiter + 1);
Martin Sustrik's avatar
Martin Sustrik committed
228

229 230 231 232 233 234 235
    //  0 is not a valid port.
    uint16_t sin_port = htons ((uint16_t) atoi (service.c_str()));
    if (!sin_port) {
        errno = EINVAL;
        return -1;
    }

Martin Sustrik's avatar
Martin Sustrik committed
236 237
    //  Initialize the output parameter.
    memset (addr_, 0, sizeof (*addr_));
Martin Sustrik's avatar
Martin Sustrik committed
238

239 240 241 242 243
    //  Initialize temporary output pointers with storage address.
    sockaddr_storage ss;
    sockaddr *out_addr = (sockaddr *) &ss;
    socklen_t out_addrlen;

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
    //  Initialise IP-format family/port and populate temporary output pointers
    //  with the address.
    if (ipv4only_) {
        sockaddr_in ip4_addr;
        memset (&ip4_addr, 0, sizeof (ip4_addr));
        ip4_addr.sin_family = AF_INET;
        ip4_addr.sin_port = sin_port;
        ip4_addr.sin_addr.s_addr = htonl (INADDR_ANY);
        out_addrlen = (socklen_t) sizeof (ip4_addr);
        memcpy (out_addr, &ip4_addr, out_addrlen);
    } else {
        sockaddr_in6 ip6_addr;
        memset (&ip6_addr, 0, sizeof (ip6_addr));
        ip6_addr.sin6_family = AF_INET6;
        ip6_addr.sin6_port = sin_port;
        memcpy (&ip6_addr.sin6_addr, &in6addr_any, sizeof (in6addr_any));
        out_addrlen = (socklen_t) sizeof (ip6_addr);
        memcpy (out_addr, &ip6_addr, out_addrlen);
    }
Martin Sustrik's avatar
Martin Sustrik committed
263

264
    //  * resolves to INADDR_ANY or in6addr_any.
265
    if (iface.compare("*") == 0) {
266
        zmq_assert (out_addrlen <= (socklen_t) sizeof (*addr_));
Martin Sustrik's avatar
Martin Sustrik committed
267
        memcpy (addr_, out_addr, out_addrlen);
Martin Sustrik's avatar
Martin Sustrik committed
268
        *addr_len_ = out_addrlen;
Martin Sustrik's avatar
Martin Sustrik committed
269 270 271 272
        return 0;
    }

    //  Try to resolve the string as a NIC name.
273
    int rc = resolve_nic_name (out_addr, iface.c_str(), ipv4only_);
Martin Sustrik's avatar
Martin Sustrik committed
274 275 276
    if (rc != 0 && errno != ENODEV)
        return rc;
    if (rc == 0) {
277
        zmq_assert (out_addrlen <= (socklen_t) sizeof (*addr_));
Martin Sustrik's avatar
Martin Sustrik committed
278
        memcpy (addr_, out_addr, out_addrlen);
Martin Sustrik's avatar
Martin Sustrik committed
279
        *addr_len_ = out_addrlen;
Martin Sustrik's avatar
Martin Sustrik committed
280 281 282
        return 0;
    }

Martin Sustrik's avatar
Martin Sustrik committed
283
    //  There's no such interface name. Assume literal address.
284 285 286 287
#if defined ZMQ_HAVE_OPENVMS && defined __ia64
    __addrinfo64 *res = NULL;
    __addrinfo64 req;
#else
Martin Sustrik's avatar
Martin Sustrik committed
288 289
    addrinfo *res = NULL;
    addrinfo req;
290
#endif
Martin Sustrik's avatar
Martin Sustrik committed
291 292
    memset (&req, 0, sizeof (req));

293 294 295
    //  Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for
    //  IPv4-in-IPv6 addresses.
    req.ai_family = ipv4only_ ? AF_INET : AF_INET6;
Martin Sustrik's avatar
Martin Sustrik committed
296 297 298 299 300 301

    //  Arbitrary, not used in the output, but avoids duplicate results.
    req.ai_socktype = SOCK_STREAM;

    //  Restrict hostname/service to literals to avoid any DNS lookups or
    //  service-name irregularity due to indeterminate socktype.
Martin Lucina's avatar
Martin Lucina committed
302
    req.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV;
Martin Sustrik's avatar
Martin Sustrik committed
303

304 305 306 307 308 309 310 311 312
#ifndef ZMQ_HAVE_WINDOWS
    //  Windows by default maps IPv4 addresses into IPv6. In this API we only
    //  require IPv4-mapped addresses when no native IPv6 interfaces are
    //  available (~AI_ALL).  This saves an additional DNS roundtrip for IPv4
    //  addresses.
    if (req.ai_family == AF_INET6)
        req.ai_flags |= AI_V4MAPPED;
#endif

Martin Sustrik's avatar
Martin Sustrik committed
313 314 315 316
    //  Resolve the literal address. Some of the error info is lost in case
    //  of error, however, there's no way to report EAI errors via errno.
    rc = getaddrinfo (iface.c_str(), service.c_str(), &req, &res);
    if (rc) {
Martin Sustrik's avatar
Martin Sustrik committed
317 318 319
        errno = ENODEV;
        return -1;
    }
Martin Sustrik's avatar
Martin Sustrik committed
320 321

    //  Use the first result.
322
    zmq_assert ((size_t) (res->ai_addrlen) <= sizeof (*addr_));
unknown's avatar
unknown committed
323
    memcpy (addr_, res->ai_addr, res->ai_addrlen);
324
    *addr_len_ = (socklen_t) res->ai_addrlen;
Martin Sustrik's avatar
Martin Sustrik committed
325 326 327 328 329

    //  Cleanup getaddrinfo after copying the possibly referenced result.
    if (res)
        freeaddrinfo (res);

Martin Sustrik's avatar
Martin Sustrik committed
330 331 332
    return 0;
}

Martin Sustrik's avatar
Martin Sustrik committed
333
int zmq::resolve_ip_hostname (sockaddr_storage *addr_, socklen_t *addr_len_,
334
    const char *hostname_, bool ipv4only_)
Martin Sustrik's avatar
Martin Sustrik committed
335
{
Martin Sustrik's avatar
Martin Sustrik committed
336
    //  Find the ':' that separates hostname name from service.
337
    const char *delimiter = strrchr (hostname_, ':');
Martin Sustrik's avatar
Martin Sustrik committed
338 339 340 341 342
    if (!delimiter) {
        errno = EINVAL;
        return -1;
    }

Martin Sustrik's avatar
Martin Sustrik committed
343
    //  Separate the hostname and service.
Martin Sustrik's avatar
Martin Sustrik committed
344
    std::string hostname (hostname_, delimiter - hostname_);
Martin Sustrik's avatar
Martin Sustrik committed
345
    std::string service (delimiter + 1);
Martin Sustrik's avatar
Martin Sustrik committed
346

Martin Sustrik's avatar
Martin Sustrik committed
347
    //  Set up the query.
Martin Sustrik's avatar
Martin Sustrik committed
348 349
    addrinfo req;
    memset (&req, 0, sizeof (req));
Martin Sustrik's avatar
Martin Sustrik committed
350

351 352 353
    //  Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for
    //  IPv4-in-IPv6 addresses.
    req.ai_family = ipv4only_ ? AF_INET : AF_INET6;
Martin Sustrik's avatar
Martin Sustrik committed
354 355 356 357 358

    //  Need to choose one to avoid duplicate results from getaddrinfo() - this
    //  doesn't really matter, since it's not included in the addr-output.
    req.ai_socktype = SOCK_STREAM;
    
359 360
    //  Avoid named services due to unclear socktype.
    req.ai_flags = AI_NUMERICSERV;
Martin Sustrik's avatar
Martin Sustrik committed
361

362 363 364 365 366 367 368 369
#ifndef ZMQ_HAVE_WINDOWS
    //  Windows by default maps IPv4 addresses into IPv6. In this API we only
    //  require IPv4-mapped addresses when no native IPv6 interfaces are
    //  available.  This saves an additional DNS roundtrip for IPv4 addresses.
    if (req.ai_family == AF_INET6)
        req.ai_flags |= AI_V4MAPPED;
#endif

Martin Sustrik's avatar
Martin Sustrik committed
370 371
    //  Resolve host name. Some of the error info is lost in case of error,
    //  however, there's no way to report EAI errors via errno.
Martin Sustrik's avatar
Martin Sustrik committed
372
    addrinfo *res;
Martin Sustrik's avatar
Martin Sustrik committed
373
    int rc = getaddrinfo (hostname.c_str (), service.c_str (), &req, &res);
Martin Sustrik's avatar
Martin Sustrik committed
374
    if (rc) {
375 376 377 378 379 380

        switch (rc) {
        case EAI_MEMORY:
            errno = ENOMEM;
            break;
        default:
Martin Sustrik's avatar
Martin Sustrik committed
381
        errno = EINVAL;
382 383 384
            break;
        }

Martin Sustrik's avatar
Martin Sustrik committed
385 386
        return -1;
    }
Martin Sustrik's avatar
Martin Sustrik committed
387 388

    //  Copy first result to output addr with hostname and service.
389
    zmq_assert ((size_t) (res->ai_addrlen) <= sizeof (*addr_));
Martin Sustrik's avatar
Martin Sustrik committed
390
    memcpy (addr_, res->ai_addr, res->ai_addrlen);
391
    *addr_len_ = (socklen_t) res->ai_addrlen;
Martin Sustrik's avatar
Martin Sustrik committed
392
 
Martin Sustrik's avatar
Martin Sustrik committed
393 394 395 396
    freeaddrinfo (res);
    
    return 0;
}
397

Martin Sustrik's avatar
Martin Sustrik committed
398 399
int zmq::resolve_local_path (sockaddr_storage *addr_, socklen_t *addr_len_,
    const char *path_)
400
{
401 402 403 404
#if defined ZMQ_HAVE_WINDOWS || defined ZMQ_HAVE_OPENVMS
    errno = EPROTONOSUPPORT;
    return -1;
#else
Martin Sustrik's avatar
Martin Sustrik committed
405 406
    sockaddr_un *un = (sockaddr_un*) addr_;
    if (strlen (path_) >= sizeof (un->sun_path))
407 408 409 410
    {
        errno = ENAMETOOLONG;
        return -1;
    }
Martin Sustrik's avatar
Martin Sustrik committed
411 412 413
    strcpy (un->sun_path, path_);
    un->sun_family = AF_UNIX;
    *addr_len_ = sizeof (sockaddr_un);
414 415
    return 0;
#endif
416
}
417

418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
void zmq::tune_tcp_socket (fd_t s_)
{
    //  Disable Nagle's algorithm. We are doing data batching on 0MQ level,
    //  so using Nagle wouldn't improve throughput in anyway, but it would
    //  hurt latency.
    int nodelay = 1;
    int rc = setsockopt (s_, IPPROTO_TCP, TCP_NODELAY, (char*) &nodelay,
        sizeof (int));
#ifdef ZMQ_HAVE_WINDOWS
    wsa_assert (rc != SOCKET_ERROR);
#else
    errno_assert (rc == 0);
#endif

#ifdef ZMQ_HAVE_OPENVMS
    //  Disable delayed acknowledgements as they hurt latency is serious manner.
    int nodelack = 1;
    rc = setsockopt (s_, IPPROTO_TCP, TCP_NODELACK, (char*) &nodelack,
        sizeof (int));
    errno_assert (rc != SOCKET_ERROR);
#endif
}

441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
void zmq::unblock_socket (fd_t s_)
{
#ifdef ZMQ_HAVE_WINDOWS
    u_long nonblock = 1;
    int rc = ioctlsocket (s_, FIONBIO, &nonblock);
    wsa_assert (rc != SOCKET_ERROR);
#elif ZMQ_HAVE_OPENVMS
	int nonblock = 1;
	int rc = ioctl (s_, FIONBIO, &nonblock);
    errno_assert (rc != -1);
#else
	int flags = fcntl (s_, F_GETFL, 0);
	if (flags == -1)
        flags = 0;
	int rc = fcntl (s_, F_SETFL, flags | O_NONBLOCK);
    errno_assert (rc != -1);
#endif
}

460