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; }
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; }
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; }