diff --git a/AUTHORS b/AUTHORS index 4510f679..99084a80 100644 --- a/AUTHORS +++ b/AUTHORS @@ -33,6 +33,7 @@ Gerard Toonstra Ghislain Putois Gonzalo Diethelm Guido Goldstein +Ian Barber Ilja Golshtein Ivo Danihelka Jacob Rideout diff --git a/doc/zmq_getsockopt.txt b/doc/zmq_getsockopt.txt index 50b80fd9..0d5e8a23 100644 --- a/doc/zmq_getsockopt.txt +++ b/doc/zmq_getsockopt.txt @@ -391,6 +391,17 @@ Option value unit:: N/A (flags) Default value:: N/A Applicable socket types:: all +ZMQ_LAST_ENDPOINT: Retrieve the last endpoint set +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The 'ZMQ_LAST_ENDPOINT' option shall retrieve the last endpoint bound for +TCP and IPC transports. The returned value will be a string in the form of +a ZMQ DSN. + +[horizontal] +Option value type:: character string +Option value unit:: N/A +Default value:: NULL +Applicable socket types:: all, when binding TCP or IPC transports RETURN VALUE ------------ diff --git a/include/zmq.h b/include/zmq.h index 84ab1a09..48c0c8d0 100644 --- a/include/zmq.h +++ b/include/zmq.h @@ -221,6 +221,7 @@ ZMQ_EXPORT int zmq_term (zmq_ctx_t context); #define ZMQ_RCVTIMEO 27 #define ZMQ_SNDTIMEO 28 #define ZMQ_IPV4ONLY 31 +#define ZMQ_LAST_ENDPOINT 32 /* Message options */ #define ZMQ_MORE 1 diff --git a/src/ipc_listener.cpp b/src/ipc_listener.cpp index 07a7dff6..668464a3 100644 --- a/src/ipc_listener.cpp +++ b/src/ipc_listener.cpp @@ -95,8 +95,32 @@ void zmq::ipc_listener_t::in_event () send_attach (session, engine, false); } +int zmq::ipc_listener_t::get_address (std::string *addr_) +{ + struct sockaddr_un sun; + int rc; + + // Get the details of the IPC socket + socklen_t sl = sizeof(sockaddr_un); + rc = getsockname (s, (sockaddr *)&sun, &sl); + if (rc != 0) { + return rc; + } + + // Store the address for retrieval by users using wildcards + *addr_ = std::string("ipc://") + std::string(sun.sun_path); + + return 0; +} + int zmq::ipc_listener_t::set_address (const char *addr_) { + + // Allow wildcard file + if(*addr_ == '*') { + addr_ = tempnam(NULL, NULL); + } + // Get rid of the file associated with the UNIX domain socket that // may have been left behind by the previous run of the application. ::unlink (addr_); @@ -124,7 +148,7 @@ int zmq::ipc_listener_t::set_address (const char *addr_) rc = listen (s, options.backlog); if (rc != 0) return -1; - + return 0; } diff --git a/src/ipc_listener.hpp b/src/ipc_listener.hpp index e1f48171..228e9f24 100644 --- a/src/ipc_listener.hpp +++ b/src/ipc_listener.hpp @@ -48,6 +48,9 @@ namespace zmq // Set address to listen on. int set_address (const char *addr_); + + // Get the bound address for use with wildcards + int get_address(std::string *addr_); private: diff --git a/src/options.cpp b/src/options.cpp index 4db1a6cb..5f1d4440 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -213,7 +213,6 @@ int zmq::options_t::setsockopt (int option_, const void *optval_, ipv4only = val; return 0; } - } errno = EINVAL; @@ -385,7 +384,16 @@ int zmq::options_t::getsockopt (int option_, void *optval_, size_t *optvallen_) *((int*) optval_) = ipv4only; *optvallen_ = sizeof (int); return 0; - + + case ZMQ_LAST_ENDPOINT: + // don't allow string which cannot contain the entire message + if (*optvallen_ < last_endpoint.size() + 1) { + errno = EINVAL; + return -1; + } + memcpy (optval_, last_endpoint.c_str(), last_endpoint.size()+1); + *optvallen_ = last_endpoint.size()+1; + return 0; } errno = EINVAL; diff --git a/src/options.hpp b/src/options.hpp index bfc9dc79..2bf793ee 100644 --- a/src/options.hpp +++ b/src/options.hpp @@ -23,6 +23,8 @@ #ifndef __ZMQ_OPTIONS_HPP_INCLUDED__ #define __ZMQ_OPTIONS_HPP_INCLUDED__ +#include + #include "stddef.h" #include "stdint.hpp" @@ -46,6 +48,9 @@ namespace zmq // Socket identity unsigned char identity_size; unsigned char identity [256]; + + // Last socket endpoint URI + std::string last_endpoint; // Maximum tranfer rate [kb/s]. Default 100kb/s. int rate; diff --git a/src/socket_base.cpp b/src/socket_base.cpp index 3761b46c..fc73ce9c 100644 --- a/src/socket_base.cpp +++ b/src/socket_base.cpp @@ -161,6 +161,7 @@ int zmq::socket_base_t::parse_uri (const char *uri_, } protocol_ = uri.substr (0, pos); address_ = uri.substr (pos + 3); + if (protocol_.empty () || address_.empty ()) { errno = EINVAL; return -1; @@ -340,6 +341,8 @@ int zmq::socket_base_t::bind (const char *addr_) delete listener; return -1; } + + rc = listener->get_address (&options.last_endpoint); launch_child (listener); return 0; } @@ -354,6 +357,8 @@ int zmq::socket_base_t::bind (const char *addr_) delete listener; return -1; } + + rc = listener->get_address (&options.last_endpoint); launch_child (listener); return 0; } diff --git a/src/tcp_address.cpp b/src/tcp_address.cpp index de6e0ad6..9fe6083c 100644 --- a/src/tcp_address.cpp +++ b/src/tcp_address.cpp @@ -387,11 +387,18 @@ 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); - // Parse the port number (0 is not a valid port). - uint16_t port = (uint16_t) atoi (port_str.c_str()); - if (port == 0) { - errno = EINVAL; - return -1; + uint16_t port; + // Allow 0 specifically, to detect invalid port error in atoi if not + if (port_str[0] == '*' || port_str[0] == '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. diff --git a/src/tcp_listener.cpp b/src/tcp_listener.cpp index 0b7a90d3..8c65ff7f 100644 --- a/src/tcp_listener.cpp +++ b/src/tcp_listener.cpp @@ -22,6 +22,7 @@ #include #include +#include #include "platform.hpp" #include "tcp_listener.hpp" @@ -119,6 +120,40 @@ void zmq::tcp_listener_t::close () s = retired_fd; } +int zmq::tcp_listener_t::get_address (std::string *addr_) +{ + struct sockaddr sa; + char host[INET6_ADDRSTRLEN]; + int port, rc; + std::stringstream portnum; + + // Get the details of the TCP socket + socklen_t sl = sizeof(sockaddr); + rc = getsockname (s, &sa, &sl); + if (rc != 0) { + return rc; + } + + // Split the retrieval between IPv4 and v6 addresses + if ( sa.sa_family == AF_INET ) { + inet_ntop(AF_INET, &(((struct sockaddr_in *)&sa)->sin_addr), host, INET6_ADDRSTRLEN); + port = ntohs( ((struct sockaddr_in *)&sa)->sin_port); + portnum << port; + + // Store the address for retrieval by users using wildcards + *addr_ = std::string("tcp://") + std::string(host) + std::string(":") + portnum.str(); + } else { + inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)&sa)->sin6_addr), host, INET6_ADDRSTRLEN); + port = ntohs( ((struct sockaddr_in6 *)&sa)->sin6_port); + portnum << port; + + // Store the address for retrieval by users using wildcards + *addr_ = std::string("tcp://[") + std::string(host) + std::string("]:") + portnum.str(); + } + + return 0; +} + int zmq::tcp_listener_t::set_address (const char *addr_) { // Convert the textual address into address structure. @@ -168,6 +203,7 @@ int zmq::tcp_listener_t::set_address (const char *addr_) errno_assert (rc == 0); #endif + // Bind the socket to the network interface and port. rc = bind (s, address.addr (), address.addrlen ()); #ifdef ZMQ_HAVE_WINDOWS diff --git a/src/tcp_listener.hpp b/src/tcp_listener.hpp index c2116b39..66c4fb2e 100644 --- a/src/tcp_listener.hpp +++ b/src/tcp_listener.hpp @@ -44,6 +44,9 @@ namespace zmq // Set address to listen on. int set_address (const char *addr_); + + // Get the bound address for use with wildcard + int get_address(std::string *addr_); private: