From a142a2b83868f92e5502f649708f2d3b9c562b6a Mon Sep 17 00:00:00 2001 From: Severin Leonhardt Date: Wed, 12 Jul 2023 09:33:02 +0200 Subject: [PATCH] Choose correct local address for targeted IP family To connect to the right shard the driver needs to specify the local port. This is only possible by also choosing an IP address family (v4 or v6). The code uses IPv4 by default, unless the user configures a different listen address. This means in the default configuration the Scylla driver is unable to connect to IPv6 addresses with a rather surprising "address not available" error. This commit addresses that surprising behavior by not hard coding a default listen address but instead choosing one based on the IP address family that is being connected to. --- src/config.hpp | 1 - src/constants.hpp | 2 -- src/socket_connector.cpp | 30 +++++++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/config.hpp b/src/config.hpp index 9a7861070..9930cde47 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -70,7 +70,6 @@ class Config { , max_reusable_write_objects_(CASS_DEFAULT_MAX_REUSABLE_WRITE_OBJECTS) , prepare_on_all_hosts_(CASS_DEFAULT_PREPARE_ON_ALL_HOSTS) , prepare_on_up_or_add_host_(CASS_DEFAULT_PREPARE_ON_UP_OR_ADD_HOST) - , local_address_(CASS_DEFAULT_LOCAL_ADDRESS, CASS_DEFAULT_LOCAL_ADDRESS_PORT) , no_compact_(CASS_DEFAULT_NO_COMPACT) , is_client_id_set_(false) , host_listener_(new DefaultHostListener()) diff --git a/src/constants.hpp b/src/constants.hpp index 89586a353..2d2a743a7 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -119,8 +119,6 @@ #define CASS_DEFAULT_NUM_CONNECTIONS_PER_HOST 1 #define CASS_DEFAULT_PREPARE_ON_ALL_HOSTS true #define CASS_DEFAULT_PREPARE_ON_UP_OR_ADD_HOST true -#define CASS_DEFAULT_LOCAL_ADDRESS "0.0.0.0" -#define CASS_DEFAULT_LOCAL_ADDRESS_PORT 0 #define CASS_DEFAULT_PORT 9042 #define CASS_DEFAULT_QUEUE_SIZE_IO 8192 #define CASS_DEFAULT_CONSTANT_RECONNECT_WAIT_TIME_MS 2000u diff --git a/src/socket_connector.cpp b/src/socket_connector.cpp index 2b13082ed..59887181b 100644 --- a/src/socket_connector.cpp +++ b/src/socket_connector.cpp @@ -177,6 +177,7 @@ void SocketConnector::internal_connect(uv_loop_t* loop) { // This needs to be done after setting the socket to properly cleanup. const Address& local_address = settings_.local_address; if (local_address.is_valid()) { + // use configured local address Address::SocketStorage storage; LOG_DEBUG("Binding socket. local_address=%s, remote=%s", local_address.to_string(true).c_str(), socket_->address().to_string(true).c_str()); @@ -187,7 +188,34 @@ void SocketConnector::internal_connect(uv_loop_t* loop) { return; } } else { - LOG_WARN("Cannot bind to an invalid `local_address` %s:%d", local_address.to_string().c_str(), local_address.port()); + // determine local address matching resolved addresses family + if (resolved_address_.family() == Address::IPv4) { + Address::SocketStorage storage; + storage.addr_in()->sin_family = AF_INET; + storage.addr_in()->sin_addr.s_addr = INADDR_ANY; + storage.addr_in()->sin_port = local_address.port(); + int rc = uv_tcp_bind(socket->handle(), storage.addr(), 0); + if (rc != 0) { + on_error(SOCKET_ERROR_BIND, "Unable to bind local address: " + String(uv_strerror(rc))); + + return; + } + } else if (resolved_address_.family() == Address::IPv6) { + Address::SocketStorage storage; + storage.addr_in6()->sin6_family = AF_INET6; + storage.addr_in6()->sin6_addr = in6addr_any; + storage.addr_in6()->sin6_port = local_address.port(); + storage.addr_in6()->sin6_flowinfo = 0; + storage.addr_in6()->sin6_scope_id = 0; + int rc = uv_tcp_bind(socket->handle(), storage.addr(), 0); + if (rc != 0) { + on_error(SOCKET_ERROR_BIND, "Unable to bind local address: " + String(uv_strerror(rc))); + + return; + } + } else { + LOG_WARN("Cannot bind to local address for unknown family (%d) of resolved address %s.", resolved_address_.family(), resolved_address_.to_string().c_str()); + } } if (settings_.ssl_context) {