Ejemplo n.º 1
0
int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv6_, bool is_src_)
{
    if (!is_src_) {
        // Test the ';' to know if we have a source address in name_
        const char *src_delimiter = strrchr (name_, ';');
        if (src_delimiter) {
            std::string src_name (name_, src_delimiter - name_);
            const int rc = resolve (src_name.c_str (), local_, ipv6_, true);
            if (rc != 0)
                return -1;
            name_ = src_delimiter + 1;
            _has_src_addr = true;
        }
    }

    //  Find the ':' at end that separates address from the port number.
    const char *delimiter = strrchr (name_, ':');
    if (!delimiter) {
        errno = EINVAL;
        return -1;
    }

    //  Separate the address/port.
    std::string addr_str (name_, delimiter - name_);
    std::string port_str (delimiter + 1);

    //  Remove square brackets around the address, if any, as used in IPv6
    if (addr_str.size () >= 2 && addr_str [0] == '[' &&
          addr_str [addr_str.size () - 1] == ']')
        addr_str = addr_str.substr (1, addr_str.size () - 2);

    // Test the '%' to know if we have an interface name / zone_id in the address
    // Reference: https://tools.ietf.org/html/rfc4007
    std::size_t pos = addr_str.rfind("%");
    uint32_t zone_id = 0;
    if (pos != std::string::npos) {
        std::string if_str = addr_str.substr(pos + 1);
        addr_str = addr_str.substr(0, pos);
        if (isalpha (if_str.at (0)))
            zone_id = if_nametoindex(if_str.c_str());
        else
            zone_id = (uint32_t) atoi (if_str.c_str ());
        if (zone_id == 0) {
            errno = EINVAL;
            return -1;
        }
    }

    //  Allow 0 specifically, to detect invalid port error in atoi if not
    uint16_t port;
    if (port_str == "*" || port_str == "0")
        //  Resolve wildcard to 0 to allow autoselection of port
        port = 0;
    else {
        //  Parse the port number (0 is not a valid port).
        port = (uint16_t) atoi (port_str.c_str ());
        if (port == 0) {
            errno = EINVAL;
            return -1;
        }
    }

    //  Resolve the IP address.
    int rc;
    if (local_ || is_src_)
        rc = resolve_interface (addr_str.c_str (), ipv6_, is_src_);
    else
        rc = resolve_hostname (addr_str.c_str (), ipv6_, is_src_);
    if (rc != 0)
        return -1;

    //  Set the port into the address structure.
    if (is_src_) {
        if (source_address.generic.sa_family == AF_INET6) {
            source_address.ipv6.sin6_port = htons (port);
            source_address.ipv6.sin6_scope_id = zone_id;
        }
        else
            source_address.ipv4.sin_port = htons (port);
    }
    else {
        if (address.generic.sa_family == AF_INET6) {
            address.ipv6.sin6_port = htons (port);
            address.ipv6.sin6_scope_id = zone_id;
        }
        else
            address.ipv4.sin_port = htons (port);
    }

    return 0;
}
Ejemplo n.º 2
0
int zmq::udp_address_t::resolve (const char *name_, bool bind_, bool ipv6_)
{
    //  No IPv6 support yet
    bool has_interface = false;

    address = name_;

    //  If we have a semicolon then we should have an interface specifier in the
    //  URL
    const char *src_delimiter = strrchr (name_, ';');
    if (src_delimiter) {
        std::string src_name (name_, src_delimiter - name_);

        ip_resolver_options_t src_resolver_opts;

        src_resolver_opts
          .bindable (true)
          //  Restrict hostname/service to literals to avoid any DNS
          //  lookups or service-name irregularity due to
          //  indeterminate socktype.
          .allow_dns (false)
          .allow_nic_name (true)
          .ipv6 (ipv6_)
          .expect_port (false);

        ip_resolver_t src_resolver (src_resolver_opts);

        const int rc = src_resolver.resolve (&bind_address, src_name.c_str ());

        if (rc != 0) {
            return -1;
        }

        if (bind_address.is_multicast ()) {
            //  It doesn't make sense to have a multicast address as a source
            errno = EINVAL;
            return -1;
        }

        //  This is a hack because we need the interface index when binding
        //  multicast IPv6, we can't do it by address. Unfortunately for the
        //  time being we don't have a generic platform-independent function to
        //  resolve an interface index from an address, so we only support it
        //  when an actual interface name is provided.
        if (src_name == "*") {
            bind_interface = 0;
        } else {
            bind_interface = if_nametoindex (src_name.c_str ());
            if (bind_interface == 0) {
                //  Error, probably not an interface name.
                bind_interface = -1;
            }
        }

        has_interface = true;
        name_ = src_delimiter + 1;
    }

    ip_resolver_options_t resolver_opts;

    resolver_opts.bindable (bind_)
      .allow_dns (!bind_)
      .allow_nic_name (bind_)
      .expect_port (true)
      .ipv6 (ipv6_);

    ip_resolver_t resolver (resolver_opts);

    int rc = resolver.resolve (&target_address, name_);
    if (rc != 0) {
        return -1;
    }

    is_multicast = target_address.is_multicast ();
    uint16_t port = target_address.port ();

    if (has_interface) {
        //  If we have an interface specifier then the target address must be a
        //  multicast address
        if (!is_multicast) {
            errno = EINVAL;
            return -1;
        }

        bind_address.set_port (port);
    } else {
        //  If we don't have an explicit interface specifier then the URL is
        //  ambiguous: if the target address is multicast then it's the
        //  destination address and the bind address is ANY, if it's unicast
        //  then it's the bind address when 'bind_' is true and the destination
        //  otherwise
        if (is_multicast || !bind_) {
            bind_address = ip_addr_t::any (target_address.family ());
            bind_address.set_port (port);
            bind_interface = 0;
        } else {
            //  If we were asked for a bind socket and the address
            //  provided was not multicast then it was really meant as
            //  a bind address and the target_address is useless.
            bind_address = target_address;
        }
    }

    if (bind_address.family () != target_address.family ()) {
        errno = EINVAL;
        return -1;
    }

    //  For IPv6 multicast we *must* have an interface index since we can't
    //  bind by address.
    if (ipv6_ && is_multicast && bind_interface < 0) {
        errno = ENODEV;
        return -1;
    }

    return 0;
}
Ejemplo n.º 3
0
int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv6_, bool is_src_)
{
    if (!is_src_) {
        // Test the ';' to know if we have a source address in name_
        const char *src_delimiter = strrchr (name_, ';');
        if (src_delimiter) {
            std::string src_name (name_, src_delimiter - name_);
            const int rc = resolve (src_name.c_str (), local_, ipv6_, true);
            if (rc != 0)
                return -1;
            name_ = src_delimiter + 1;
            _has_src_addr = true;
        }
    }

    //  Find the ':' at end that separates address from the port number.
    const char *delimiter = strrchr (name_, ':');
    if (!delimiter) {
        errno = EINVAL;
        return -1;
    }

    //  Separate the address/port.
    std::string addr_str (name_, delimiter - name_);
    std::string port_str (delimiter + 1);

    //  Remove square brackets around the address, if any.
    if (addr_str.size () >= 2 && addr_str [0] == '[' &&
            addr_str [addr_str.size () - 1] == ']')
        addr_str = addr_str.substr (1, addr_str.size () - 2);

    //  Allow 0 specifically, to detect invalid port error in atoi if not
    uint16_t port;
    if (port_str == "*" || port_str == "0")
        //  Resolve wildcard to 0 to allow autoselection of port
        port = 0;
    else {
        //  Parse the port number (0 is not a valid port).
        port = (uint16_t) atoi (port_str.c_str ());
        if (port == 0) {
            errno = EINVAL;
            return -1;
        }
    }

    //  Resolve the IP address.
    int rc;
    if (local_)
        rc = resolve_interface (addr_str.c_str (), ipv6_, is_src_);
    else
        rc = resolve_hostname (addr_str.c_str (), ipv6_, is_src_);
    if (rc != 0)
        return -1;

    //  Set the port into the address structure.
    if (is_src_) {
        if (source_address.generic.sa_family == AF_INET6)
            source_address.ipv6.sin6_port = htons (port);
        else
            source_address.ipv4.sin_port = htons (port);
    }
    else {
        if (address.generic.sa_family == AF_INET6)
            address.ipv6.sin6_port = htons (port);
        else
            address.ipv4.sin_port = htons (port);
    }

    return 0;
}