Example #1
0
File: ip.cpp Project: kmnb/zeromq2
int zmq::resolve_ip_interface (sockaddr_in* addr_, char const *interface_)
{
    //  Find the ':' that separates NIC name from port.
    const char *delimiter = strchr (interface_, ':');
    if (!delimiter) {
        errno = EINVAL;
        return -1;
    }

    //  Clean the structure and fill in protocol family.
    memset (addr_, 0, sizeof (sockaddr_in));
    addr_->sin_family = AF_INET;

    //  Resolve the name of the NIC.
    std::string nic_name (interface_, delimiter - interface_);
    if (resolve_nic_name (&addr_->sin_addr, nic_name.c_str ()) != 0)
        return -1;

    //  Resolve the port.
    addr_->sin_port = htons ((uint16_t) atoi (delimiter + 1));
    if (!addr_->sin_port) {
        errno = EINVAL;
        return 0;
    }

    return 0;
}
Example #2
0
int zmq::tcp_address_t::resolve_interface (const char *interface_,
    bool ipv4only_)
{
    //  Initialize temporary output pointers with storage address.
    sockaddr_storage ss;
    sockaddr *out_addr = (sockaddr*) &ss;
    size_t out_addrlen;

    //  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_addr.s_addr = htonl (INADDR_ANY);
        out_addrlen = 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;
        memcpy (&ip6_addr.sin6_addr, &in6addr_any, sizeof (in6addr_any));
        out_addrlen = sizeof ip6_addr;
        memcpy (out_addr, &ip6_addr, out_addrlen);
    }

    //  * resolves to INADDR_ANY or in6addr_any.
    if (strcmp (interface_, "*") == 0) {
        zmq_assert (out_addrlen <= sizeof address);
        memcpy (&address, out_addr, out_addrlen);
        return 0;
    }

    //  Try to resolve the string as a NIC name.
    int rc = resolve_nic_name (interface_, ipv4only_);
    if (rc != 0 && errno != ENODEV)
        return rc;
    if (rc == 0)
        return 0;

    //  There's no such interface name. Assume literal address.
#if defined ZMQ_HAVE_OPENVMS && defined __ia64
    __addrinfo64 *res = NULL;
    __addrinfo64 req;
#else
    addrinfo *res = NULL;
    addrinfo req;
#endif
    memset (&req, 0, sizeof (req));

    //  Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for
    //  IPv4-in-IPv6 addresses.
    req.ai_family = ipv4only_ ? AF_INET : AF_INET6;

    //  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.
    req.ai_flags = AI_PASSIVE | AI_NUMERICHOST;

#if defined AI_V4MAPPED && !defined ZMQ_HAVE_FREEBSD
    //  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.
    //  Note: While the AI_V4MAPPED flag is defined on FreeBSD system,
    //  it is not supported here. See libzmq issue #331.
    if (req.ai_family == AF_INET6)
        req.ai_flags |= AI_V4MAPPED;
#endif

    //  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 (interface_, NULL, &req, &res);
    if (rc) {
        errno = ENODEV;
        return -1;
    }

    //  Use the first result.
    zmq_assert ((size_t) (res->ai_addrlen) <= sizeof (address));
    memcpy (&address, res->ai_addr, res->ai_addrlen);

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

    return 0;
}
Example #3
0
int zmq::resolve_ip_interface (sockaddr_storage* addr_, socklen_t *addr_len_,
    char const *interface_)
{
    //  Find the ':' at end that separates NIC name from service.
    const char *delimiter = strrchr (interface_, ':');
    if (!delimiter) {
        errno = EINVAL;
        return -1;
    }

    //  Separate the name/port.
    std::string iface (interface_, delimiter - interface_);
    std::string service (delimiter + 1);

    //  Initialize the output parameter.
    memset (addr_, 0, sizeof (*addr_));

    //  Initialise IPv4-format family/port.
    sockaddr_in ip4_addr;
    memset (&ip4_addr, 0, sizeof (ip4_addr));
    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) {
        errno = EINVAL;
        return -1;
    }

    //  * resolves to INADDR_ANY.
    if (iface.compare("*") == 0) {
        ip4_addr.sin_addr.s_addr = htonl (INADDR_ANY);
        zmq_assert (out_addrlen <= sizeof (*addr_));
        memcpy (addr_, out_addr, out_addrlen);
        *addr_len_ = out_addrlen;
        return 0;
    }

    //  Try to resolve the string as a NIC name.
    int rc = resolve_nic_name (&ip4_addr.sin_addr, iface.c_str());
    if (rc != 0 && errno != ENODEV)
        return rc;
    if (rc == 0) {
        zmq_assert (out_addrlen <= sizeof (*addr_));
        memcpy (addr_, out_addr, out_addrlen);
        *addr_len_ = out_addrlen;
        return 0;
    }

    //  There's no such interface name. Assume literal address.
    addrinfo *res = NULL;

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

    //  We only support IPv4 addresses for now.
    req.ai_family = AF_INET;

    //  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.
    req.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV;

    //  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) {
        errno = ENODEV;
        return -1;
    }

    //  Use the first result.
    zmq_assert ((size_t) (res->ai_addrlen) <= sizeof (*addr_));
    memcpy (addr_, res->ai_addr, res->ai_addrlen);
    *addr_len_ = res->ai_addrlen;

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

    return 0;
}
Example #4
0
int zmq::ip_resolver_t::resolve (ip_addr_t *ip_addr_, const char *name_)
{
    std::string addr;
    uint16_t port;

    if (options.expect_port ()) {
        //  We expect 'addr:port'. It's important to use str*r*chr to only get
        //  the latest colon since IPv6 addresses use colons as delemiters.
        const char *delim = strrchr (name_, ':');

        if (delim == NULL) {
            errno = EINVAL;
            return -1;
        }

        addr = std::string (name_, delim - name_);
        std::string port_str = std::string (delim + 1);

        if (port_str == "*") {
            if (options.bindable ()) {
                //  Resolve wildcard to 0 to allow autoselection of port
                port = 0;
            } else {
                errno = EINVAL;
                return -1;
            }
        } else if (port_str == "0") {
            //  Using "0" for a bind address is equivalent to using "*". For a
            //  connectable address it could be used to connect to port 0.
            port = 0;
        } else {
            //  Parse the port number (0 is not a valid port).
            port = static_cast<uint16_t> (atoi (port_str.c_str ()));
            if (port == 0) {
                errno = EINVAL;
                return -1;
            }
        }
    } else {
        addr = std::string (name_);
        port = 0;
    }

    //  Trim any square brackets surrounding the address. Used for
    //  IPv6 addresses to remove the confusion with the port
    //  delimiter. Should we validate that the brackets are present if
    //  'addr' contains ':' ?
    if (addr.size () >= 2 && addr[0] == '[' && addr[addr.size () - 1] == ']') {
        addr = addr.substr (1, addr.size () - 2);
    }

    //  Look for an interface name / zone_id in the address
    //  Reference: https://tools.ietf.org/html/rfc4007
    std::size_t pos = addr.rfind ('%');
    uint32_t zone_id = 0;

    if (pos != std::string::npos) {
        std::string if_str = addr.substr (pos + 1);
        addr = addr.substr (0, pos);

        if (isalpha (if_str.at (0))) {
            zone_id = do_if_nametoindex (if_str.c_str ());
        } else {
            zone_id = static_cast<uint32_t> (atoi (if_str.c_str ()));
        }

        if (zone_id == 0) {
            errno = EINVAL;
            return -1;
        }
    }

    bool resolved = false;
    const char *addr_str = addr.c_str ();

    if (options.bindable () && addr == "*") {
        //  Return an ANY address
        *ip_addr_ = ip_addr_t::any (options.ipv6 () ? AF_INET6 : AF_INET);
        resolved = true;
    }

    if (!resolved && options.allow_nic_name ()) {
        //  Try to resolve the string as a NIC name.
        int rc = resolve_nic_name (ip_addr_, addr_str);

        if (rc == 0) {
            resolved = true;
        } else if (errno != ENODEV) {
            return rc;
        }
    }

    if (!resolved) {
        int rc = resolve_getaddrinfo (ip_addr_, addr_str);

        if (rc != 0) {
            return rc;
        }
        resolved = true;
    }

    //  Store the port into the structure. We could get 'getaddrinfo' to do it
    //  for us but since we don't resolve service names it's a bit overkill and
    //  we'd still have to do it manually when the address is resolved by
    //  'resolve_nic_name'
    ip_addr_->set_port (port);

    if (ip_addr_->family () == AF_INET6) {
        ip_addr_->ipv6.sin6_scope_id = zone_id;
    }

    assert (resolved == true);
    return 0;
}