ip.cpp 9.09 KB
Newer Older
Martin Sustrik's avatar
Martin Sustrik committed
1
/*
2
    Copyright (c) 2007-2010 iMatix Corporation
Martin Sustrik's avatar
Martin Sustrik committed
3 4 5 6

    This file is part of 0MQ.

    0MQ is free software; you can redistribute it and/or modify it under
7
    the terms of the GNU Lesser General Public License as published by
Martin Sustrik's avatar
Martin Sustrik committed
8 9 10 11 12 13
    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
14
    GNU Lesser General Public License for more details.
Martin Sustrik's avatar
Martin Sustrik committed
15

16
    You should have received a copy of the GNU Lesser General Public License
Martin Sustrik's avatar
Martin Sustrik committed
17 18 19 20 21 22 23 24 25 26 27 28 29
    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 "platform.hpp"
#include "err.hpp"
#include "stdint.hpp"

Martin Sustrik's avatar
Martin Sustrik committed
30
#if defined ZMQ_HAVE_SOLARIS
Martin Sustrik's avatar
Martin Sustrik committed
31 32 33 34 35 36 37 38 39 40

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

//  On Solaris platform, network interface name can be queried by ioctl.
static int resolve_nic_name (in_addr* addr_, char const *interface_)
{
    //  Create a socket.
    int fd = socket (AF_INET, SOCK_DGRAM, 0);
Martin Sustrik's avatar
Martin Sustrik committed
41
    zmq_assert (fd != -1);
Martin Sustrik's avatar
Martin Sustrik committed
42 43 44 45 46 47

    //  Retrieve number of interfaces.
    lifnum ifn;
    ifn.lifn_family = AF_UNSPEC;
    ifn.lifn_flags = 0;
    int rc = ioctl (fd, SIOCGLIFNUM, (char*) &ifn);
Martin Sustrik's avatar
Martin Sustrik committed
48
    zmq_assert (rc != -1);
Martin Sustrik's avatar
Martin Sustrik committed
49 50 51 52

    //  Allocate memory to get interface names.
    size_t ifr_size = sizeof (struct lifreq) * ifn.lifn_count;
    char *ifr = (char*) malloc (ifr_size);
53
    alloc_assert (ifr);
Martin Sustrik's avatar
Martin Sustrik committed
54 55 56 57 58 59 60 61
    
    //  Retrieve interface names.
    lifconf ifc;
    ifc.lifc_family = AF_UNSPEC;
    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
62
    zmq_assert (rc != -1);
Martin Sustrik's avatar
Martin Sustrik committed
63 64 65 66 67 68 69 70

    //  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
71
            zmq_assert (rc != -1);
Martin Sustrik's avatar
Martin Sustrik committed
72 73 74 75 76 77 78 79 80 81 82 83 84
            if (ifrp->lifr_addr.ss_family == AF_INET) {
                *addr_ = ((sockaddr_in*) &ifrp->lifr_addr)->sin_addr;
                found = true;
                break;
            }
        }
    }

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

    if (!found) {
Martin Sustrik's avatar
Martin Sustrik committed
85 86
        errno = ENODEV;
        return -1;
Martin Sustrik's avatar
Martin Sustrik committed
87 88 89 90 91
    }

    return 0;
}

Martin Sustrik's avatar
Martin Sustrik committed
92
#elif defined ZMQ_HAVE_AIX || ZMQ_HAVE_HPUX
Martin Sustrik's avatar
Martin Sustrik committed
93 94 95 96 97 98 99 100 101 102

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

static int resolve_nic_name (in_addr* addr_, char const *interface_)
{
    //  Create a socket.
    int sd = socket (AF_INET, SOCK_DGRAM, 0);
Martin Sustrik's avatar
Martin Sustrik committed
103
    zmq_assert (sd != -1);
Martin Sustrik's avatar
Martin Sustrik committed
104 105 106 107

    struct ifreq ifr; 

    //  Copy interface name for ioctl get.
108
    strncpy (ifr.ifr_name, interface_, sizeof (ifr.ifr_name));
Martin Sustrik's avatar
Martin Sustrik committed
109 110 111 112 113 114 115

    //  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
116 117
    if (rc == -1) {
        errno = ENODEV;
Martin Sustrik's avatar
Martin Sustrik committed
118 119 120
        return -1;
    }

Martin Sustrik's avatar
Martin Sustrik committed
121 122 123
    struct sockaddr *sa = (struct sockaddr *) &ifr.ifr_addr;
    *addr_ = ((sockaddr_in*)sa)->sin_addr;
    return 0;    
Martin Sustrik's avatar
Martin Sustrik committed
124 125
}

Martin Sustrik's avatar
Martin Sustrik committed
126 127
#elif ((defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD ||\
    defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_OPENBSD ||\
Martin Lucina's avatar
Martin Lucina committed
128 129
    defined ZMQ_HAVE_QNXNTO || defined ZMQ_HAVE_NETBSD)\
    && defined ZMQ_HAVE_IFADDRS)
Martin Sustrik's avatar
Martin Sustrik committed
130 131 132 133 134 135 136 137 138 139

#include <ifaddrs.h>

//  On these platforms, network interface name can be queried
//  using getifaddrs function.
static int resolve_nic_name (in_addr* addr_, char const *interface_)
{
    //  Get the addresses.
    ifaddrs* ifa = NULL;
    int rc = getifaddrs (&ifa);
Martin Sustrik's avatar
Martin Sustrik committed
140 141
    zmq_assert (rc == 0);    
    zmq_assert (ifa != NULL);
Martin Sustrik's avatar
Martin Sustrik committed
142 143 144 145 146 147 148 149 150

    //  Find the corresponding network interface.
    bool found = false;
    for (ifaddrs *ifp = ifa; ifp != NULL ;ifp = ifp->ifa_next)
        if (ifp->ifa_addr && ifp->ifa_addr->sa_family == AF_INET 
            && !strcmp (interface_, ifp->ifa_name)) 
        {
            *addr_ = ((sockaddr_in*) ifp->ifa_addr)->sin_addr;
            found = true;
151
            break;
Martin Sustrik's avatar
Martin Sustrik committed
152 153 154 155 156 157
        }

    //  Clean-up;
    freeifaddrs (ifa);

    if (!found) {
Martin Sustrik's avatar
Martin Sustrik committed
158 159
        errno = ENODEV;
        return -1;
Martin Sustrik's avatar
Martin Sustrik committed
160 161 162 163 164 165 166
    }

    return 0;
}

#else

Martin Sustrik's avatar
Martin Sustrik committed
167 168
//  On other platforms we assume there are no sane interface names.
//  This is true especially of Windows.
Martin Sustrik's avatar
Martin Sustrik committed
169 170
static int resolve_nic_name (in_addr* addr_, char const *interface_)
{
Martin Sustrik's avatar
Martin Sustrik committed
171 172
    errno = ENODEV;
    return -1;
Martin Sustrik's avatar
Martin Sustrik committed
173 174 175 176
}

#endif

Martin Sustrik's avatar
Martin Sustrik committed
177 178
int zmq::resolve_ip_interface (sockaddr_storage* addr_, socklen_t *addr_len_,
    char const *interface_)
Martin Sustrik's avatar
Martin Sustrik committed
179
{
Martin Sustrik's avatar
Martin Sustrik committed
180
    //  Find the ':' at end that separates NIC name from service.
Martin Sustrik's avatar
Martin Sustrik committed
181
    const char *delimiter = strrchr (interface_, ':');
Martin Sustrik's avatar
Martin Sustrik committed
182 183 184 185 186
    if (!delimiter) {
        errno = EINVAL;
        return -1;
    }

Martin Sustrik's avatar
Martin Sustrik committed
187
    //  Separate the name/port.
188
    std::string iface (interface_, delimiter - interface_);
Martin Sustrik's avatar
Martin Sustrik committed
189
    std::string service (delimiter + 1);
Martin Sustrik's avatar
Martin Sustrik committed
190

Martin Sustrik's avatar
Martin Sustrik committed
191 192
    //  Initialize the output parameter.
    memset (addr_, 0, sizeof (*addr_));
Martin Sustrik's avatar
Martin Sustrik committed
193

Martin Sustrik's avatar
Martin Sustrik committed
194 195
    //  Initialise IPv4-format family/port.
    sockaddr_in ip4_addr;
196
    memset (&ip4_addr, 0, sizeof (ip4_addr));
Martin Sustrik's avatar
Martin Sustrik committed
197 198 199 200 201 202 203 204 205
    ip4_addr.sin_family = AF_INET;
    ip4_addr.sin_port = htons ((uint16_t) atoi (service.c_str()));

    //  Initialize temporary output pointers with ip4_addr
    sockaddr *out_addr = (sockaddr *) &ip4_addr;
    size_t out_addrlen = sizeof (ip4_addr);

    //  0 is not a valid port.
    if (!ip4_addr.sin_port) {
Martin Sustrik's avatar
Martin Sustrik committed
206
        errno = EINVAL;
207
        return -1;
Martin Sustrik's avatar
Martin Sustrik committed
208 209
    }

Martin Sustrik's avatar
Martin Sustrik committed
210
    //  * resolves to INADDR_ANY.
211
    if (iface.compare("*") == 0) {
Martin Sustrik's avatar
Martin Sustrik committed
212 213 214
        ip4_addr.sin_addr.s_addr = htonl (INADDR_ANY);
        zmq_assert (out_addrlen <= sizeof (*addr_));
        memcpy (addr_, out_addr, out_addrlen);
Martin Sustrik's avatar
Martin Sustrik committed
215
        *addr_len_ = out_addrlen;
Martin Sustrik's avatar
Martin Sustrik committed
216 217 218 219
        return 0;
    }

    //  Try to resolve the string as a NIC name.
220
    int rc = resolve_nic_name (&ip4_addr.sin_addr, iface.c_str());
Martin Sustrik's avatar
Martin Sustrik committed
221 222 223 224 225
    if (rc != 0 && errno != ENODEV)
        return rc;
    if (rc == 0) {
        zmq_assert (out_addrlen <= sizeof (*addr_));
        memcpy (addr_, out_addr, out_addrlen);
Martin Sustrik's avatar
Martin Sustrik committed
226
        *addr_len_ = out_addrlen;
Martin Sustrik's avatar
Martin Sustrik committed
227 228 229
        return 0;
    }

Martin Sustrik's avatar
Martin Sustrik committed
230 231 232 233 234 235 236
    //  There's no such interface name. Assume literal address.
    addrinfo *res = NULL;

    //  Set up the query.
    addrinfo req;
    memset (&req, 0, sizeof (req));

Martin Lucina's avatar
Martin Lucina committed
237 238
    //  We only support IPv4 addresses for now.
    req.ai_family = AF_INET;
Martin Sustrik's avatar
Martin Sustrik committed
239 240 241 242 243 244

    //  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
245
    req.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV;
Martin Sustrik's avatar
Martin Sustrik committed
246 247 248 249 250

    //  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
251 252 253
        errno = ENODEV;
        return -1;
    }
Martin Sustrik's avatar
Martin Sustrik committed
254 255

    //  Use the first result.
256
    zmq_assert ((size_t) (res->ai_addrlen) <= sizeof (*addr_));
unknown's avatar
unknown committed
257 258
    memcpy (addr_, res->ai_addr, res->ai_addrlen);
    *addr_len_ = res->ai_addrlen;
Martin Sustrik's avatar
Martin Sustrik committed
259 260 261 262 263

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

Martin Sustrik's avatar
Martin Sustrik committed
264 265 266
    return 0;
}

Martin Sustrik's avatar
Martin Sustrik committed
267 268
int zmq::resolve_ip_hostname (sockaddr_storage *addr_, socklen_t *addr_len_,
    const char *hostname_)
Martin Sustrik's avatar
Martin Sustrik committed
269
{
Martin Sustrik's avatar
Martin Sustrik committed
270
    //  Find the ':' that separates hostname name from service.
Martin Sustrik's avatar
Martin Sustrik committed
271 272 273 274 275 276
    const char *delimiter = strchr (hostname_, ':');
    if (!delimiter) {
        errno = EINVAL;
        return -1;
    }

Martin Sustrik's avatar
Martin Sustrik committed
277
    //  Separate the hostname and service.
Martin Sustrik's avatar
Martin Sustrik committed
278
    std::string hostname (hostname_, delimiter - hostname_);
Martin Sustrik's avatar
Martin Sustrik committed
279
    std::string service (delimiter + 1);
Martin Sustrik's avatar
Martin Sustrik committed
280

Martin Sustrik's avatar
Martin Sustrik committed
281
    //  Set up the query.
Martin Sustrik's avatar
Martin Sustrik committed
282 283
    addrinfo req;
    memset (&req, 0, sizeof (req));
Martin Sustrik's avatar
Martin Sustrik committed
284

Martin Lucina's avatar
Martin Lucina committed
285 286
    //  We only support IPv4 addresses for now.
    req.ai_family = AF_INET;
Martin Sustrik's avatar
Martin Sustrik committed
287 288 289 290 291

    //  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;
    
292 293
    //  Avoid named services due to unclear socktype.
    req.ai_flags = AI_NUMERICSERV;
Martin Sustrik's avatar
Martin Sustrik committed
294 295 296

    //  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
297
    addrinfo *res;
Martin Sustrik's avatar
Martin Sustrik committed
298
    int rc = getaddrinfo (hostname.c_str (), service.c_str (), &req, &res);
Martin Sustrik's avatar
Martin Sustrik committed
299 300 301 302
    if (rc) {
        errno = EINVAL;
        return -1;
    }
Martin Sustrik's avatar
Martin Sustrik committed
303 304

    //  Copy first result to output addr with hostname and service.
305
    zmq_assert ((size_t) (res->ai_addrlen) <= sizeof (*addr_));
Martin Sustrik's avatar
Martin Sustrik committed
306 307 308
    memcpy (addr_, res->ai_addr, res->ai_addrlen);
    *addr_len_ = res->ai_addrlen;
 
Martin Sustrik's avatar
Martin Sustrik committed
309 310 311 312
    freeaddrinfo (res);
    
    return 0;
}
313 314 315

#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS

Martin Sustrik's avatar
Martin Sustrik committed
316 317
int zmq::resolve_local_path (sockaddr_storage *addr_, socklen_t *addr_len_,
    const char *path_)
318
{
Martin Sustrik's avatar
Martin Sustrik committed
319 320
    sockaddr_un *un = (sockaddr_un*) addr_;
    if (strlen (path_) >= sizeof (un->sun_path))
321 322 323 324
    {
        errno = ENAMETOOLONG;
        return -1;
    }
Martin Sustrik's avatar
Martin Sustrik committed
325 326 327
    strcpy (un->sun_path, path_);
    un->sun_family = AF_UNIX;
    *addr_len_ = sizeof (sockaddr_un);
328 329 330 331 332
    return 0;
}

#endif