From 309740e1972565fdcbfe886439977a4603a7d10e Mon Sep 17 00:00:00 2001 From: Pieter Hintjens Date: Thu, 31 Jan 2013 20:47:45 +0100 Subject: [PATCH] Fixed issue #499 --- doc/zmq_getsockopt.txt | 21 +++++++++--- doc/zmq_setsockopt.txt | 24 ++++++++++--- src/options.cpp | 29 ++++++++-------- src/options.hpp | 6 ++-- src/socket_base.cpp | 2 +- src/tcp_address.cpp | 77 +++++++++++++++++++----------------------- src/tcp_address.hpp | 15 ++++---- src/tcp_listener.cpp | 7 ++-- 8 files changed, 98 insertions(+), 83 deletions(-) diff --git a/doc/zmq_getsockopt.txt b/doc/zmq_getsockopt.txt index 0876ac85..fb26db12 100644 --- a/doc/zmq_getsockopt.txt +++ b/doc/zmq_getsockopt.txt @@ -324,12 +324,24 @@ Default value:: -1 (infinite) Applicable socket types:: all +ZMQ_IPV6: Retrieve IPv6 socket status +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Retrieve the IPv6 option for the socket. A value of `1` means IPv6 is +enabled on the socket, while `0` means the socket will use only IPv4. +When IPv6 is enabled the socket will connect to, or accept connections +from, both IPv4 and IPv6 hosts. + +[horizontal] +Option value type:: int +Option value unit:: boolean +Default value:: 0 (false) +Applicable socket types:: all, when using TCP transports. + + ZMQ_IPV4ONLY: Retrieve IPv4-only socket override status ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Retrieve the underlying native socket type. A value of `1` will use IPv4 -sockets, while the value of `0` will use IPv6 sockets. An IPv6 socket -lets applications connect to and accept connections from both IPv4 and IPv6 -hosts. +Retrieve the IPv4-only option for the socket. This option is deprecated. +Please use the ZMQ_IPV6 option. [horizontal] Option value type:: int @@ -470,6 +482,7 @@ Option value unit:: -1,>0 Default value:: -1 (leave to OS default) Applicable socket types:: all, when using TCP transports. + RETURN VALUE ------------ The _zmq_getsockopt()_ function shall return zero if successful. Otherwise it diff --git a/doc/zmq_setsockopt.txt b/doc/zmq_setsockopt.txt index 8b5617f4..da8e7043 100644 --- a/doc/zmq_setsockopt.txt +++ b/doc/zmq_setsockopt.txt @@ -342,12 +342,26 @@ Default value:: -1 (infinite) Applicable socket types:: all -ZMQ_IPV4ONLY: Use IPv4-only sockets -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +ZMQ_IPV6: Enable IPv6 on socket +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Sets the underlying native socket type. A value of `1` will use IPv4 sockets, -while the value of `0` will use IPv6 sockets. An IPv6 socket lets -applications connect to and accept connections from both IPv4 and IPv6 hosts. +Set the IPv6 option for the socket. A value of `1` means IPv6 is +enabled on the socket, while `0` means the socket will use only IPv4. +When IPv6 is enabled the socket will connect to, or accept connections +from, both IPv4 and IPv6 hosts. + +[horizontal] +Option value type:: int +Option value unit:: boolean +Default value:: 0 (false) +Applicable socket types:: all, when using TCP transports. + + +ZMQ_IPV4ONLY: Use IPv4-only on socket +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Set the IPv4-only ootion for the socket. This option is deprecated. +Please use the ZMQ_IPV6 option. [horizontal] Option value type:: int diff --git a/src/options.cpp b/src/options.cpp index b2f3da9a..57596e05 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -43,7 +43,7 @@ zmq::options_t::options_t () : maxmsgsize (-1), rcvtimeo (-1), sndtimeo (-1), - ipv4only (1), + ipv6 (0), delay_attach_on_connect (0), delay_on_close (true), delay_on_disconnect (true), @@ -182,9 +182,10 @@ int zmq::options_t::setsockopt (int option_, const void *optval_, valid = false; break; + /* Deprecated in favor of ZMQ_IPV6 */ case ZMQ_IPV4ONLY: if (is_int && (value == 0 || value == 1)) - ipv4only = value; + ipv6 = 1 - value; else valid = false; break; @@ -192,7 +193,7 @@ int zmq::options_t::setsockopt (int option_, const void *optval_, /* To replace the somewhat surprising IPV4ONLY */ case ZMQ_IPV6: if (is_int && (value == 0 || value == 1)) - ipv4only = 1 - value; + ipv6 = value; else valid = false; break; @@ -241,7 +242,7 @@ int zmq::options_t::setsockopt (int option_, const void *optval_, else { std::string filter_str ((const char *) optval_, optvallen_); tcp_address_mask_t mask; - int rc = mask.resolve (filter_str.c_str (), ipv4only); + int rc = mask.resolve (filter_str.c_str (), ipv6); if (rc == 0) tcp_accept_filters.push_back (mask); else @@ -423,7 +424,7 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_) errno = EINVAL; return -1; } - *((int*) optval_) = ipv4only; + *((int*) optval_) = 1 - ipv6; *optvallen_ = sizeof (int); return 0; @@ -432,18 +433,18 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_) errno = EINVAL; return -1; } - *((int*) optval_) = 1 - ipv4only; + *((int*) optval_) = ipv6; *optvallen_ = sizeof (int); return 0; case ZMQ_DELAY_ATTACH_ON_CONNECT: - if (*optvallen_ < sizeof (int)) { - errno = EINVAL; - return -1; - } - *((int*) optval_) = delay_attach_on_connect; - *optvallen_ = sizeof (int); - return 0; + if (*optvallen_ < sizeof (int)) { + errno = EINVAL; + return -1; + } + *((int*) optval_) = delay_attach_on_connect; + *optvallen_ = sizeof (int); + return 0; case ZMQ_TCP_KEEPALIVE: if (*optvallen_ < sizeof (int)) { @@ -482,7 +483,7 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_) return 0; case ZMQ_LAST_ENDPOINT: - // don't allow string which cannot contain the entire message + /* don't allow string which cannot contain the entire message */ if (*optvallen_ < last_endpoint.size() + 1) { errno = EINVAL; return -1; diff --git a/src/options.hpp b/src/options.hpp index 2c29e442..45d95191 100644 --- a/src/options.hpp +++ b/src/options.hpp @@ -92,10 +92,8 @@ namespace zmq int rcvtimeo; int sndtimeo; - // If 1, indicates the use of IPv4 sockets only, it will not be - // possible to communicate with IPv6-only hosts. If 0, the socket can - // connect to and accept connections from both IPv4 and IPv6 hosts. - int ipv4only; + // If true, IPv6 is enabled (as well as IPv4) + bool ipv6; // If 1, connecting pipes are not attached immediately, meaning a send() // on a socket with only connecting pipes would block diff --git a/src/socket_base.cpp b/src/socket_base.cpp index 562d4b30..30339d22 100644 --- a/src/socket_base.cpp +++ b/src/socket_base.cpp @@ -499,7 +499,7 @@ int zmq::socket_base_t::connect (const char *addr_) paddr->resolved.tcp_addr = new (std::nothrow) tcp_address_t (); alloc_assert (paddr->resolved.tcp_addr); int rc = paddr->resolved.tcp_addr->resolve ( - address.c_str (), false, options.ipv4only ? true : false); + address.c_str (), false, options.ipv6); if (rc != 0) { delete paddr; return -1; diff --git a/src/tcp_address.cpp b/src/tcp_address.cpp index 8a08b464..473284e0 100644 --- a/src/tcp_address.cpp +++ b/src/tcp_address.cpp @@ -52,10 +52,10 @@ #include // On Solaris platform, network interface name can be queried by ioctl. -int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_) +int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv6_) { // TODO: Unused parameter, IPv6 support not implemented for Solaris. - (void) ipv4only_; + (void) ipv6_; // Create a socket. int fd = open_socket (AF_INET, SOCK_DGRAM, 0); @@ -106,7 +106,6 @@ int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_) errno = ENODEV; return -1; } - return 0; } @@ -117,10 +116,10 @@ int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_) #include #include -int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_) +int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv6_) { // TODO: Unused parameter, IPv6 support not implemented for AIX or HP/UX. - (void) ipv4only_; + (void) ipv6_; // Create a socket. int sd = open_socket (AF_INET, SOCK_DGRAM, 0); @@ -141,7 +140,6 @@ int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_) errno = ENODEV; return -1; } - memcpy (&address.ipv4.sin_addr, &((sockaddr_in*) &ifr.ifr_addr)->sin_addr, sizeof (in_addr)); @@ -157,10 +155,10 @@ int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_) // On these platforms, network interface name can be queried // using getifaddrs function. -int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_) +int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv6_) { // Get the addresses. - ifaddrs* ifa = NULL; + ifaddrs *ifa = NULL; int rc = getifaddrs (&ifa); errno_assert (rc == 0); zmq_assert (ifa != NULL); @@ -173,11 +171,8 @@ int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_) continue; int family = ifp->ifa_addr->sa_family; - - if ((family == AF_INET - || (!ipv4only_ && family == AF_INET6)) - && !strcmp (nic_, ifp->ifa_name)) - { + if ((family == AF_INET || (ipv6_ && family == AF_INET6)) + && !strcmp (nic_, ifp->ifa_name)) { memcpy (&address, ifp->ifa_addr, (family == AF_INET) ? sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)); @@ -193,7 +188,6 @@ int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_) errno = ENODEV; return -1; } - return 0; } @@ -201,11 +195,11 @@ int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_) // On other platforms we assume there are no sane interface names. // This is true especially of Windows. -int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_) +int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv6_) { // All unused parameters. (void) nic_; - (void) ipv4only_; + (void) ipv6_; errno = ENODEV; return -1; @@ -213,8 +207,7 @@ int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_) #endif -int zmq::tcp_address_t::resolve_interface (const char *interface_, - bool ipv4only_) +int zmq::tcp_address_t::resolve_interface (const char *interface_, bool ipv6_) { // Initialize temporary output pointers with storage address. sockaddr_storage ss; @@ -223,15 +216,7 @@ int zmq::tcp_address_t::resolve_interface (const char *interface_, // 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 { + if (ipv6_) { sockaddr_in6 ip6_addr; memset (&ip6_addr, 0, sizeof (ip6_addr)); ip6_addr.sin6_family = AF_INET6; @@ -239,8 +224,15 @@ int zmq::tcp_address_t::resolve_interface (const char *interface_, out_addrlen = sizeof ip6_addr; memcpy (out_addr, &ip6_addr, out_addrlen); } - - // * resolves to INADDR_ANY or in6addr_any. + else { + 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); + } + // "*" resolves to INADDR_ANY or in6addr_any. if (strcmp (interface_, "*") == 0) { zmq_assert (out_addrlen <= sizeof address); memcpy (&address, out_addr, out_addrlen); @@ -248,7 +240,7 @@ int zmq::tcp_address_t::resolve_interface (const char *interface_, } // Try to resolve the string as a NIC name. - int rc = resolve_nic_name (interface_, ipv4only_); + int rc = resolve_nic_name (interface_, ipv6_); if (rc != 0 && errno != ENODEV) return rc; if (rc == 0) @@ -266,7 +258,7 @@ int zmq::tcp_address_t::resolve_interface (const char *interface_, // Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for // IPv4-in-IPv6 addresses. - req.ai_family = ipv4only_ ? AF_INET : AF_INET6; + req.ai_family = ipv6_? AF_INET6: AF_INET; // Arbitrary, not used in the output, but avoids duplicate results. req.ai_socktype = SOCK_STREAM; @@ -304,7 +296,7 @@ int zmq::tcp_address_t::resolve_interface (const char *interface_, return 0; } -int zmq::tcp_address_t::resolve_hostname (const char *hostname_, bool ipv4only_) +int zmq::tcp_address_t::resolve_hostname (const char *hostname_, bool ipv6_) { // Set up the query. #if defined ZMQ_HAVE_OPENVMS && defined __ia64 && __INITIAL_POINTER_SIZE == 64 @@ -316,7 +308,7 @@ int zmq::tcp_address_t::resolve_hostname (const char *hostname_, bool ipv4only_) // Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for // IPv4-in-IPv6 addresses. - req.ai_family = ipv4only_ ? AF_INET : AF_INET6; + req.ai_family = ipv6_? AF_INET6: AF_INET; // Need to choose one to avoid duplicate results from getaddrinfo() - this // doesn't really matter, since it's not included in the addr-output. @@ -382,7 +374,7 @@ zmq::tcp_address_t::~tcp_address_t () { } -int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv4only_) +int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv6_) { // Find the ':' at end that separates address from the port number. const char *delimiter = strrchr (name_, ':'); @@ -390,7 +382,6 @@ int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv4only_) errno = EINVAL; return -1; } - // Separate the address/port. std::string addr_str (name_, delimiter - name_); std::string port_str (delimiter + 1); @@ -400,8 +391,8 @@ int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv4only_) addr_str [addr_str.size () - 1] == ']') addr_str = addr_str.substr (1, addr_str.size () - 2); - uint16_t port; // 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; @@ -417,9 +408,9 @@ int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv4only_) // Resolve the IP address. int rc; if (local_) - rc = resolve_interface (addr_str.c_str (), ipv4only_); + rc = resolve_interface (addr_str.c_str (), ipv6_); else - rc = resolve_hostname (addr_str.c_str (), ipv4only_); + rc = resolve_hostname (addr_str.c_str (), ipv6_); if (rc != 0) return -1; @@ -434,7 +425,8 @@ int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv4only_) int zmq::tcp_address_t::to_string (std::string &addr_) { - if (address.generic.sa_family != AF_INET && address.generic.sa_family != AF_INET6) { + if (address.generic.sa_family != AF_INET + && address.generic.sa_family != AF_INET6) { addr_.clear (); return -1; } @@ -493,7 +485,7 @@ int zmq::tcp_address_mask_t::mask () const return address_mask; } -int zmq::tcp_address_mask_t::resolve (const char *name_, bool ipv4only_) +int zmq::tcp_address_mask_t::resolve (const char *name_, bool ipv6_) { // Find '/' at the end that separates address from the cidr mask number. // Allow empty mask clause and threat it like '/32' for ipv4 or '/128' for ipv6. @@ -507,12 +499,11 @@ int zmq::tcp_address_mask_t::resolve (const char *name_, bool ipv4only_) return -1; } } - else { + else addr_str.assign (name_); - } // Parse address part using standard routines. - int rc = tcp_address_t::resolve_hostname (addr_str.c_str (), ipv4only_); + int rc = tcp_address_t::resolve_hostname (addr_str.c_str (), ipv6_); if (rc != 0) return rc; diff --git a/src/tcp_address.hpp b/src/tcp_address.hpp index 93d73d81..d1f170ec 100644 --- a/src/tcp_address.hpp +++ b/src/tcp_address.hpp @@ -45,8 +45,8 @@ namespace zmq // This function translates textual TCP address into an address // strcuture. If 'local' is true, names are resolved as local interface // names. If it is false, names are resolved as remote hostnames. - // If 'ipv4only' is true, the name will never resolve to IPv6 address. - int resolve (const char* name_, bool local_, bool ipv4only_); + // If 'ipv6' is true, the name may resolve to IPv6 address. + int resolve (const char *name_, bool local_, bool ipv6_); // The opposite to resolve() virtual int to_string (std::string &addr_); @@ -60,10 +60,9 @@ namespace zmq socklen_t addrlen () const; protected: - - int resolve_nic_name (const char *nic_, bool ipv4only_); - int resolve_interface (const char *interface_, bool ipv4only_); - int resolve_hostname (const char *hostname_, bool ipv4only_); + int resolve_nic_name (const char *nic_, bool ipv6_); + int resolve_interface (const char *interface_, bool ipv6_); + int resolve_hostname (const char *hostname_, bool ipv6_); union { sockaddr generic; @@ -75,13 +74,12 @@ namespace zmq class tcp_address_mask_t : public tcp_address_t { public: - tcp_address_mask_t (); // This function enhances tcp_address_t::resolve() with ability to parse // additional cidr-like(/xx) mask value at the end of the name string. // Works only with remote hostnames. - int resolve (const char* name_, bool ipv4only_); + int resolve (const char *name_, bool ipv6_); // The opposite to resolve() int to_string (std::string &addr_); @@ -91,7 +89,6 @@ namespace zmq bool match_address (const struct sockaddr *ss, const socklen_t ss_len) const; private: - int address_mask; }; diff --git a/src/tcp_listener.cpp b/src/tcp_listener.cpp index 3cd29b6c..73fad94b 100644 --- a/src/tcp_listener.cpp +++ b/src/tcp_listener.cpp @@ -148,7 +148,7 @@ int zmq::tcp_listener_t::get_address (std::string &addr_) int zmq::tcp_listener_t::set_address (const char *addr_) { // Convert the textual address into address structure. - int rc = address.resolve (addr_, true, options.ipv4only ? true : false); + int rc = address.resolve (addr_, true, options.ipv6); if (rc != 0) return -1; @@ -160,8 +160,9 @@ int zmq::tcp_listener_t::set_address (const char *addr_) #endif // IPv6 address family not supported, try automatic downgrade to IPv4. - if (address.family () == AF_INET6 && errno == EAFNOSUPPORT && - !options.ipv4only) { + if (address.family () == AF_INET6 + && errno == EAFNOSUPPORT + && options.ipv6) { rc = address.resolve (addr_, true, true); if (rc != 0) return rc;