diff options
-rw-r--r-- | src/ip.cpp | 64 | ||||
-rw-r--r-- | src/ip.hpp | 4 | ||||
-rw-r--r-- | src/tcp_connecter.cpp | 2 | ||||
-rw-r--r-- | src/tcp_listener.cpp | 2 | ||||
-rw-r--r-- | src/vtcp_connecter.cpp | 6 |
5 files changed, 54 insertions, 24 deletions
@@ -213,7 +213,7 @@ static int resolve_nic_name (struct sockaddr* addr_, char const *interface_, #endif int zmq::resolve_ip_interface (sockaddr_storage* addr_, socklen_t *addr_len_, - char const *interface_) + char const *interface_, bool ipv4only_) { // Find the ':' at end that separates NIC name from service. const char *delimiter = strrchr (interface_, ':'); @@ -241,18 +241,27 @@ int zmq::resolve_ip_interface (sockaddr_storage* addr_, socklen_t *addr_len_, sockaddr *out_addr = (sockaddr *) &ss; socklen_t out_addrlen; - // 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 = sin_port; - ip4_addr.sin_addr.s_addr = htonl (INADDR_ANY); - - // Populate temporary output pointers with ip4_addr. - out_addrlen = (socklen_t) sizeof (ip4_addr); - memcpy (out_addr, &ip4_addr, 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_port = sin_port; + ip4_addr.sin_addr.s_addr = htonl (INADDR_ANY); + out_addrlen = (socklen_t) 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; + ip6_addr.sin6_port = sin_port; + memcpy (&ip6_addr.sin6_addr, &in6addr_any, sizeof (in6addr_any)); + out_addrlen = (socklen_t) sizeof (ip6_addr); + memcpy (out_addr, &ip6_addr, out_addrlen); + } - // * resolves to INADDR_ANY. + // * resolves to INADDR_ANY or in6addr_any. if (iface.compare("*") == 0) { zmq_assert (out_addrlen <= (socklen_t) sizeof (*addr_)); memcpy (addr_, out_addr, out_addrlen); @@ -261,7 +270,7 @@ int zmq::resolve_ip_interface (sockaddr_storage* addr_, socklen_t *addr_len_, } // Try to resolve the string as a NIC name. - int rc = resolve_nic_name (out_addr, iface.c_str(), true); + int rc = resolve_nic_name (out_addr, iface.c_str(), ipv4only_); if (rc != 0 && errno != ENODEV) return rc; if (rc == 0) { @@ -281,8 +290,9 @@ int zmq::resolve_ip_interface (sockaddr_storage* addr_, socklen_t *addr_len_, #endif memset (&req, 0, sizeof (req)); - // We only support IPv4 addresses for now. - req.ai_family = AF_INET; + // 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; @@ -291,6 +301,15 @@ int zmq::resolve_ip_interface (sockaddr_storage* addr_, socklen_t *addr_len_, // service-name irregularity due to indeterminate socktype. req.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV; +#ifndef ZMQ_HAVE_WINDOWS + // Windows by default maps IPv4 addresses into IPv6. 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. + 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 (iface.c_str(), service.c_str(), &req, &res); @@ -312,7 +331,7 @@ int zmq::resolve_ip_interface (sockaddr_storage* addr_, socklen_t *addr_len_, } int zmq::resolve_ip_hostname (sockaddr_storage *addr_, socklen_t *addr_len_, - const char *hostname_) + const char *hostname_, bool ipv4only_) { // Find the ':' that separates hostname name from service. const char *delimiter = strrchr (hostname_, ':'); @@ -329,8 +348,9 @@ int zmq::resolve_ip_hostname (sockaddr_storage *addr_, socklen_t *addr_len_, addrinfo req; memset (&req, 0, sizeof (req)); - // We only support IPv4 addresses for now. - req.ai_family = AF_INET; + // Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for + // IPv4-in-IPv6 addresses. + req.ai_family = ipv4only_ ? AF_INET : AF_INET6; // Need to choose one to avoid duplicate results from getaddrinfo() - this // doesn't really matter, since it's not included in the addr-output. @@ -339,6 +359,14 @@ int zmq::resolve_ip_hostname (sockaddr_storage *addr_, socklen_t *addr_len_, // Avoid named services due to unclear socktype. req.ai_flags = AI_NUMERICSERV; +#ifndef ZMQ_HAVE_WINDOWS + // Windows by default maps IPv4 addresses into IPv6. In this API we only + // require IPv4-mapped addresses when no native IPv6 interfaces are + // available. This saves an additional DNS roundtrip for IPv4 addresses. + if (req.ai_family == AF_INET6) + req.ai_flags |= AI_V4MAPPED; +#endif + // 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. addrinfo *res; @@ -51,12 +51,12 @@ namespace zmq // Resolves network interface name in <nic-name>:<port> format. Symbol "*" // (asterisk) resolves to INADDR_ANY (all network interfaces). int resolve_ip_interface (sockaddr_storage *addr_, socklen_t *addr_len_, - char const *interface_); + char const *interface_, bool ipv4only_); // This function resolves a string in <hostname>:<port-number> format. // Hostname can be either the name of the host or its IP address. int resolve_ip_hostname (sockaddr_storage *addr_, socklen_t *addr_len_, - const char *hostname_); + const char *hostname_, bool ipv4only_); // This function sets up address for UNIX domain transport. int resolve_local_path (sockaddr_storage *addr_, socklen_t *addr_len_, diff --git a/src/tcp_connecter.cpp b/src/tcp_connecter.cpp index bca7085..6badaea 100644 --- a/src/tcp_connecter.cpp +++ b/src/tcp_connecter.cpp @@ -179,7 +179,7 @@ int zmq::tcp_connecter_t::get_new_reconnect_ivl () int zmq::tcp_connecter_t::set_address (const char *addr_) { - return resolve_ip_hostname (&addr, &addr_len, addr_); + return resolve_ip_hostname (&addr, &addr_len, addr_, options.ipv4only); } int zmq::tcp_connecter_t::open () diff --git a/src/tcp_listener.cpp b/src/tcp_listener.cpp index a5c6513..7dae4d1 100644 --- a/src/tcp_listener.cpp +++ b/src/tcp_listener.cpp @@ -123,7 +123,7 @@ void zmq::tcp_listener_t::close () int zmq::tcp_listener_t::set_address (const char *addr_) { // Convert the interface into sockaddr_in structure. - int rc = resolve_ip_interface (&addr, &addr_len, addr_); + int rc = resolve_ip_interface (&addr, &addr_len, addr_, options.ipv4only); if (rc != 0) return -1; diff --git a/src/vtcp_connecter.cpp b/src/vtcp_connecter.cpp index 5c72044..f281b23 100644 --- a/src/vtcp_connecter.cpp +++ b/src/vtcp_connecter.cpp @@ -92,7 +92,8 @@ int zmq::vtcp_connecter_t::set_address (const char *addr_) addr_str += ":9220"; std::string subport_str (delimiter + 1); subport = (vtcp_subport_t) atoi (subport_str.c_str ()); - int rc = resolve_ip_hostname (&addr, &addr_len, addr_str.c_str ()); + int rc = resolve_ip_hostname (&addr, &addr_len, addr_str.c_str (), + true); if (rc != 0) return -1; } @@ -100,7 +101,8 @@ int zmq::vtcp_connecter_t::set_address (const char *addr_) std::string addr_str (addr_, delimiter - addr_); std::string subport_str (delimiter + 1); subport = (vtcp_subport_t) atoi (subport_str.c_str ()); - int rc = resolve_ip_hostname (&addr, &addr_len, addr_str.c_str ()); + int rc = resolve_ip_hostname (&addr, &addr_len, addr_str.c_str (), + true); if (rc != 0) return -1; } |