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