From f9f25ccc2b4a365edc00d922a2aa056cca7fe861 Mon Sep 17 00:00:00 2001 From: Sergey Matveychuk Date: Thu, 16 Feb 2012 10:03:02 +0900 Subject: Allow to set up a source address for outgoing connections in zmq_connect() Signed-off-by: Sergey Matveychuk Signed-off-by: Martin Sustrik --- AUTHORS | 1 + doc/xs_tcp.txt | 7 ++++--- src/tcp_address.cpp | 42 +++++++++++++++++++++++------------------- src/tcp_address.hpp | 3 ++- src/tcp_connecter.cpp | 24 ++++++++++++++++++++++-- src/tcp_connecter.hpp | 3 +++ 6 files changed, 55 insertions(+), 25 deletions(-) diff --git a/AUTHORS b/AUTHORS index 258a25d..c5ec023 100644 --- a/AUTHORS +++ b/AUTHORS @@ -66,6 +66,7 @@ Pieter Hintjens Piotr Trojanek Robert G. Jakabosky Sebastian Otaegui +Sergey Matveychuk Staffan Gimåker Steven McCoy Stuart Webster diff --git a/doc/xs_tcp.txt b/doc/xs_tcp.txt index 5d5cbca..b97be08 100644 --- a/doc/xs_tcp.txt +++ b/doc/xs_tcp.txt @@ -17,9 +17,10 @@ be your first choice. ADDRESSING ---------- A Crossroads address string consists of two parts as follows: -'transport'`://`'endpoint'. The 'transport' part specifies the underlying -transport protocol to use, and for the TCP transport shall be set to `tcp`. -The meaning of the 'endpoint' part for the TCP transport is defined below. +'transport'`://[`'source address'`;]`'endpoint'. The 'transport' part specifies +the underlying transport protocol to use, and for the TCP transport shall be +set to `tcp`. 'source address' is optional. The meaning of the 'endpoint' part +for the TCP transport is defined below. Assigning a local address to a socket diff --git a/src/tcp_address.cpp b/src/tcp_address.cpp index 3b6ed94..11f3793 100644 --- a/src/tcp_address.cpp +++ b/src/tcp_address.cpp @@ -250,11 +250,8 @@ int xs::tcp_address_t::resolve_interface (char const *interface_, int rc = resolve_nic_name (interface_, ipv4only_); if (rc != 0 && errno != ENODEV) return rc; - if (rc == 0) { - xs_assert (out_addrlen <= (socklen_t) sizeof (address)); - memcpy (&address, out_addr, out_addrlen); + if (rc == 0) return 0; - } // There's no such interface name. Assume literal address. #if defined XS_HAVE_OPENVMS && defined __ia64 @@ -369,31 +366,38 @@ xs::tcp_address_t::~tcp_address_t () { } -int xs::tcp_address_t::resolve (const char *name_, bool local_, bool ipv4only_) +int xs::tcp_address_t::resolve (const char *name_, bool local_, bool ipv4only_, + bool ignore_port_) { // Find the ':' at end that separates address from the port number. const char *delimiter = strrchr (name_, ':'); - if (!delimiter) { - errno = EINVAL; - return -1; - } + std::string addr_str; + uint16_t port = 0; + + if (!ignore_port_) { + if (!delimiter) { + errno = EINVAL; + return -1; + } - // Separate the address/port. - std::string addr_str (name_, delimiter - name_); - std::string port_str (delimiter + 1); + // Separate the address/port. + addr_str = std::string (name_, delimiter - name_); + + // Parse the port number (0 is not a valid port). + port = (uint16_t) atoi (delimiter+1); + if (port == 0) { + errno = EINVAL; + return -1; + } + } + else + addr_str = name_; // Remove square brackets around the address, if any. if (!addr_str.empty () && addr_str [0] == '[' && 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; - } - // Resolve the IP address. int rc; if (local_) diff --git a/src/tcp_address.hpp b/src/tcp_address.hpp index 683f30f..4730cd7 100644 --- a/src/tcp_address.hpp +++ b/src/tcp_address.hpp @@ -45,7 +45,8 @@ namespace xs // 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_); + int resolve (const char* name_, bool local_, bool ipv4only_, + bool ignore_port_=false); #if defined XS_HAVE_WINDOWS unsigned short family (); diff --git a/src/tcp_connecter.cpp b/src/tcp_connecter.cpp index 2844a3b..36ddfa7 100644 --- a/src/tcp_connecter.cpp +++ b/src/tcp_connecter.cpp @@ -178,7 +178,23 @@ int xs::tcp_connecter_t::get_new_reconnect_ivl () int xs::tcp_connecter_t::set_address (const char *addr_) { - return address.resolve (addr_, false, options.ipv4only ? true : false); + // Find the ';'. It separates source address address from a destination. + const char *delimiter = strchr (addr_, ';'); + + std::string addr_str; + if (delimiter) { + std::string saddr_str (addr_, delimiter - addr_); + addr_str = delimiter + 1; + int rc = source_address.resolve (saddr_str.c_str(), true, + options.ipv4only ? true : false, true); + if (rc != 0) + return -1; + } + else + addr_str = addr_; + + return address.resolve (addr_str.c_str(), false, + options.ipv4only ? true : false); } int xs::tcp_connecter_t::open () @@ -202,9 +218,13 @@ int xs::tcp_connecter_t::open () if (address.family () == AF_INET6) enable_ipv4_mapping (s); - // Set the socket to non-blocking mode so that we get async connect(). + // Set the socket to non-blocking mode so that we get async connect(). unblock_socket (s); + // Set a source address for conversations. + if (source_address.family ()) + ::bind (s, source_address.addr (), source_address.addrlen ()); + // Connect to the remote peer. int rc = ::connect (s, address.addr (), address.addrlen ()); diff --git a/src/tcp_connecter.hpp b/src/tcp_connecter.hpp index dcc909d..a174473 100644 --- a/src/tcp_connecter.hpp +++ b/src/tcp_connecter.hpp @@ -87,6 +87,9 @@ namespace xs // Address to connect to. tcp_address_t address; + // Source address. + tcp_address_t source_address; + // Underlying socket. fd_t s; -- cgit v1.2.3