0
0
mirror of https://github.com/zeromq/libzmq.git synced 2025-01-14 01:37:56 +08:00

Problem: can't unbind with bound addr with IPv6

Solution: try to resolve the TCP endpoint passed by the user in the
zmq_unbind call before giving up, if it doesn't match.
This fixes a breakage in the API, where after a call to
zmq_bind(s, "tcp://127.0.0.1:9999") with IPv6 enabled on s would
result in the call to zmq_unbind(s, "tcp://127.0.0.1:9999") failing.
Add more test cases to increase coverage on all combinations of TCP
endpoints.
This commit is contained in:
Luca Boccassi 2016-04-18 00:02:36 +01:00
parent 58c4e7e6b6
commit c8211bf320
2 changed files with 259 additions and 1 deletions

View File

@ -1029,8 +1029,38 @@ int zmq::socket_base_t::term_endpoint (const char *addr_)
return 0;
}
std::string resolved_addr = std::string (addr_);
std::pair <endpoints_t::iterator, endpoints_t::iterator> range;
// The resolved last_endpoint is used as a key in the endpoints map.
// The address passed by the user might not match in the TCP case due to
// IPv4-in-IPv6 mapping (EG: tcp://[::ffff:127.0.0.1]:9999), so try to
// resolve before giving up. Given at this stage we don't know whether a
// socket is connected or bound, try with both.
if (protocol == "tcp") {
range = endpoints.equal_range (resolved_addr);
if (range.first == range.second) {
tcp_address_t *tcp_addr = new (std::nothrow) tcp_address_t ();
alloc_assert (tcp_addr);
rc = tcp_addr->resolve (address.c_str (), false, options.ipv6);
if (rc == 0) {
tcp_addr->to_string (resolved_addr);
range = endpoints.equal_range (resolved_addr);
if (range.first == range.second) {
rc = tcp_addr->resolve (address.c_str (), true, options.ipv6);
if (rc == 0) {
tcp_addr->to_string (resolved_addr);
}
}
}
LIBZMQ_DELETE(tcp_addr);
}
}
// Find the endpoints range (if any) corresponding to the addr_ string.
std::pair <endpoints_t::iterator, endpoints_t::iterator> range = endpoints.equal_range (std::string (addr_));
range = endpoints.equal_range (resolved_addr);
if (range.first == range.second) {
errno = ENOENT;
EXIT_MUTEX ();

View File

@ -22,11 +22,16 @@
int main (void)
{
setup_test_environment();
int ipv6 = is_ipv6_available ();
void *ctx = zmq_ctx_new ();
assert (ctx);
/* Address wildcard, IPv6 disabled */
void *sb = zmq_socket (ctx, ZMQ_REP);
assert (sb);
void *sc = zmq_socket (ctx, ZMQ_REQ);
assert (sc);
int rc = zmq_bind (sb, "tcp://*:5555");
assert (rc == 0);
@ -35,12 +40,235 @@ int main (void)
rc = zmq_getsockopt (sb, ZMQ_LAST_ENDPOINT, endpoint, &endpoint_len);
assert (rc == 0);
rc = zmq_connect (sc, endpoint);
assert (rc == 0);
bounce (sb, sc);
rc = zmq_disconnect (sc, endpoint);
assert (rc == 0);
rc = zmq_unbind (sb, endpoint);
assert (rc == 0);
rc = zmq_close (sc);
assert (rc == 0);
rc = zmq_close (sb);
assert (rc == 0);
/* Address wildcard, IPv6 enabled */
sb = zmq_socket (ctx, ZMQ_REP);
assert (sb);
sc = zmq_socket (ctx, ZMQ_REQ);
assert (sc);
rc = zmq_setsockopt (sb, ZMQ_IPV6, &ipv6, sizeof (int));
assert (rc == 0);
rc = zmq_setsockopt (sc, ZMQ_IPV6, &ipv6, sizeof (int));
assert (rc == 0);
rc = zmq_bind (sb, "tcp://*:5556");
assert (rc == 0);
endpoint_len = sizeof (endpoint);
memset(endpoint, 0, endpoint_len);
rc = zmq_getsockopt (sb, ZMQ_LAST_ENDPOINT, endpoint, &endpoint_len);
assert (rc == 0);
rc = zmq_connect (sc, endpoint);
assert (rc == 0);
bounce (sb, sc);
rc = zmq_disconnect (sc, endpoint);
assert (rc == 0);
rc = zmq_unbind (sb, endpoint);
assert (rc == 0);
rc = zmq_close (sc);
assert (rc == 0);
rc = zmq_close (sb);
assert (rc == 0);
/* Port wildcard, IPv4 address, IPv6 disabled */
sb = zmq_socket (ctx, ZMQ_REP);
assert (sb);
sc = zmq_socket (ctx, ZMQ_REQ);
assert (sc);
rc = zmq_bind (sb, "tcp://127.0.0.1:*");
assert (rc == 0);
endpoint_len = sizeof (endpoint);
memset(endpoint, 0, endpoint_len);
rc = zmq_getsockopt (sb, ZMQ_LAST_ENDPOINT, endpoint, &endpoint_len);
assert (rc == 0);
rc = zmq_connect (sc, endpoint);
assert (rc == 0);
bounce (sb, sc);
rc = zmq_disconnect (sc, endpoint);
assert (rc == 0);
rc = zmq_unbind (sb, endpoint);
assert (rc == 0);
rc = zmq_close (sc);
assert (rc == 0);
rc = zmq_close (sb);
assert (rc == 0);
/* Port wildcard, IPv4 address, IPv6 enabled */
sb = zmq_socket (ctx, ZMQ_REP);
assert (sb);
sc = zmq_socket (ctx, ZMQ_REQ);
assert (sc);
rc = zmq_setsockopt (sb, ZMQ_IPV6, &ipv6, sizeof (int));
assert (rc == 0);
rc = zmq_setsockopt (sc, ZMQ_IPV6, &ipv6, sizeof (int));
assert (rc == 0);
rc = zmq_bind (sb, "tcp://127.0.0.1:*");
assert (rc == 0);
endpoint_len = sizeof (endpoint);
memset(endpoint, 0, endpoint_len);
rc = zmq_getsockopt (sb, ZMQ_LAST_ENDPOINT, endpoint, &endpoint_len);
assert (rc == 0);
rc = zmq_connect (sc, endpoint);
assert (rc == 0);
bounce (sb, sc);
rc = zmq_disconnect (sc, endpoint);
assert (rc == 0);
rc = zmq_unbind (sb, endpoint);
assert (rc == 0);
rc = zmq_close (sc);
assert (rc == 0);
rc = zmq_close (sb);
assert (rc == 0);
if (ipv6) {
/* Port wildcard, IPv6 address, IPv6 enabled */
sb = zmq_socket (ctx, ZMQ_REP);
assert (sb);
sc = zmq_socket (ctx, ZMQ_REQ);
assert (sc);
rc = zmq_setsockopt (sb, ZMQ_IPV6, &ipv6, sizeof (int));
assert (rc == 0);
rc = zmq_setsockopt (sc, ZMQ_IPV6, &ipv6, sizeof (int));
assert (rc == 0);
rc = zmq_bind (sb, "tcp://[::1]:*");
assert (rc == 0);
endpoint_len = sizeof (endpoint);
memset(endpoint, 0, endpoint_len);
rc = zmq_getsockopt (sb, ZMQ_LAST_ENDPOINT, endpoint, &endpoint_len);
assert (rc == 0);
rc = zmq_connect (sc, endpoint);
assert (rc == 0);
bounce (sb, sc);
rc = zmq_disconnect (sc, endpoint);
assert (rc == 0);
rc = zmq_unbind (sb, endpoint);
assert (rc == 0);
rc = zmq_close (sc);
assert (rc == 0);
rc = zmq_close (sb);
assert (rc == 0);
}
/* No wildcard, IPv4 address, IPv6 disabled */
sb = zmq_socket (ctx, ZMQ_REP);
assert (sb);
sc = zmq_socket (ctx, ZMQ_REQ);
assert (sc);
rc = zmq_bind (sb, "tcp://127.0.0.1:5557");
assert (rc == 0);
rc = zmq_connect (sc, "tcp://127.0.0.1:5557");
assert (rc == 0);
bounce (sb, sc);
rc = zmq_disconnect (sc, "tcp://127.0.0.1:5557");
assert (rc == 0);
rc = zmq_unbind (sb, "tcp://127.0.0.1:5557");
assert (rc == 0);
rc = zmq_close (sc);
assert (rc == 0);
rc = zmq_close (sb);
assert (rc == 0);
/* No wildcard, IPv4 address, IPv6 enabled */
sb = zmq_socket (ctx, ZMQ_REP);
assert (sb);
sc = zmq_socket (ctx, ZMQ_REQ);
assert (sc);
rc = zmq_setsockopt (sb, ZMQ_IPV6, &ipv6, sizeof (int));
assert (rc == 0);
rc = zmq_setsockopt (sc, ZMQ_IPV6, &ipv6, sizeof (int));
assert (rc == 0);
rc = zmq_bind (sb, "tcp://127.0.0.1:5558");
assert (rc == 0);
rc = zmq_connect (sc, "tcp://127.0.0.1:5558");
assert (rc == 0);
bounce (sb, sc);
rc = zmq_disconnect (sc, "tcp://127.0.0.1:5558");
assert (rc == 0);
rc = zmq_unbind (sb, "tcp://127.0.0.1:5558");
assert (rc == 0);
rc = zmq_close (sc);
assert (rc == 0);
rc = zmq_close (sb);
assert (rc == 0);
if (ipv6) {
/* No wildcard, IPv6 address, IPv6 enabled */
sb = zmq_socket (ctx, ZMQ_REP);
assert (sb);
sc = zmq_socket (ctx, ZMQ_REQ);
assert (sc);
rc = zmq_setsockopt (sb, ZMQ_IPV6, &ipv6, sizeof (int));
assert (rc == 0);
rc = zmq_setsockopt (sc, ZMQ_IPV6, &ipv6, sizeof (int));
assert (rc == 0);
rc = zmq_bind (sb, "tcp://[::1]:5559");
assert (rc == 0);
rc = zmq_connect (sc, "tcp://[::1]:5559");
assert (rc == 0);
bounce (sb, sc);
rc = zmq_disconnect (sc, "tcp://[::1]:5559");
assert (rc == 0);
rc = zmq_unbind (sb, "tcp://[::1]:5559");
assert (rc == 0);
rc = zmq_close (sc);
assert (rc == 0);
rc = zmq_close (sb);
assert (rc == 0);
}
rc = zmq_ctx_term (ctx);
assert (rc == 0);