Skip to content

Commit d43159e

Browse files
mpenickMichael Penick
authored andcommitted
CPP-794 Add domain name resolution to SocketConnector (#271)
1 parent 893f338 commit d43159e

5 files changed

Lines changed: 114 additions & 41 deletions

File tree

cpp-driver/gtests/src/unit/tests/test_socket.cpp

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
#include "socket_connector.hpp"
2121
#include "ssl.hpp"
2222

23-
#define SSL_VERIFY_PEER_DNS_RELATIVE_HOSTNAME "cpp-driver.hostname"
24-
#define SSL_VERIFY_PEER_DNS_ABSOLUTE_HOSTNAME SSL_VERIFY_PEER_DNS_RELATIVE_HOSTNAME "."
25-
#define SSL_VERIFY_PEER_DNS_IP_ADDRESS "127.254.254.254"
23+
#define DNS_RELATIVE_HOSTNAME "cpp-driver.hostname"
24+
#define DNS_ABSOLUTE_HOSTNAME DNS_RELATIVE_HOSTNAME "."
25+
#define DNS_IP_ADDRESS "127.254.254.254"
2626

2727
using mockssandra::internal::ClientConnection;
2828
using mockssandra::internal::ClientConnectionFactory;
@@ -152,6 +152,11 @@ class SocketUnitTest : public LoopTest {
152152
close();
153153
}
154154

155+
bool verify_dns() {
156+
verify_dns_check(); // Verify address can be resolved
157+
return HasFailure();
158+
}
159+
155160
static void on_socket_connected(SocketConnector* connector, String* result) {
156161
Socket::Ptr socket = connector->release_socket();
157162
if (connector->error_code() == SocketConnector::SOCKET_OK) {
@@ -190,15 +195,24 @@ class SocketUnitTest : public LoopTest {
190195
static void on_request(uv_getnameinfo_t* handle, int status, const char* hostname,
191196
const char* service) {
192197
if (status) {
193-
FAIL() << "Unable to Execute Test SocketUnitTest.SslVerifyIdentityDns: "
194-
<< "Add /etc/hosts entry " << SSL_VERIFY_PEER_DNS_IP_ADDRESS << "\t"
195-
<< SSL_VERIFY_PEER_DNS_ABSOLUTE_HOSTNAME;
196-
} else if (String(hostname) != String(SSL_VERIFY_PEER_DNS_ABSOLUTE_HOSTNAME)) {
197-
FAIL() << "Invalid /etc/hosts entry for: '" << hostname << "' != '"
198-
<< SSL_VERIFY_PEER_DNS_ABSOLUTE_HOSTNAME << "'";
198+
FAIL() << "Unable to Execute Test: "
199+
<< "Add /etc/hosts entry " << DNS_IP_ADDRESS << "\t" << DNS_ABSOLUTE_HOSTNAME;
200+
} else if (String(hostname) != String(DNS_ABSOLUTE_HOSTNAME)) {
201+
FAIL() << "Invalid /etc/hosts entry for: '" << hostname << "' != '" << DNS_ABSOLUTE_HOSTNAME
202+
<< "'";
199203
}
200204
}
201205

206+
private:
207+
void verify_dns_check() {
208+
Address verify_entry(DNS_IP_ADDRESS, 8888);
209+
uv_getnameinfo_t request;
210+
Address::SocketStorage storage;
211+
ASSERT_EQ(0,
212+
uv_getnameinfo(loop(), &request, on_request, verify_entry.to_sockaddr(&storage), 0));
213+
uv_run(loop(), UV_RUN_DEFAULT);
214+
}
215+
202216
private:
203217
mockssandra::SimpleEchoServer server_;
204218
};
@@ -217,6 +231,22 @@ TEST_F(SocketUnitTest, Simple) {
217231
EXPECT_EQ(result, "The socket is successfully connected and wrote data - Closed");
218232
}
219233

234+
TEST_F(SocketUnitTest, SimpleDns) {
235+
if (!verify_dns()) return;
236+
237+
listen(Address(DNS_IP_ADDRESS, 8888));
238+
239+
String result;
240+
SocketConnector::Ptr connector(new SocketConnector(Address(DNS_RELATIVE_HOSTNAME, 8888),
241+
bind_callback(on_socket_connected, &result)));
242+
243+
connector->connect(loop());
244+
245+
uv_run(loop(), UV_RUN_DEFAULT);
246+
247+
EXPECT_EQ(result, "The socket is successfully connected and wrote data - Closed");
248+
}
249+
220250
TEST_F(SocketUnitTest, Ssl) {
221251
SocketSettings settings(use_ssl());
222252

@@ -355,25 +385,16 @@ TEST_F(SocketUnitTest, SslVerifyIdentity) {
355385
}
356386

357387
TEST_F(SocketUnitTest, SslVerifyIdentityDns) {
358-
// Verify address can be resolved
359-
Address verify_entry(SSL_VERIFY_PEER_DNS_IP_ADDRESS, 8888);
360-
uv_getnameinfo_t request;
361-
Address::SocketStorage storage;
362-
ASSERT_EQ(0, uv_getnameinfo(loop(), &request, on_request, verify_entry.to_sockaddr(&storage), 0));
363-
uv_run(loop(), UV_RUN_DEFAULT);
364-
if (this->HasFailure()) { // Make test fail due to DNS not configured
365-
return;
366-
}
388+
if (!verify_dns()) return;
367389

368-
SocketSettings settings(use_ssl(SSL_VERIFY_PEER_DNS_RELATIVE_HOSTNAME));
390+
SocketSettings settings(use_ssl(DNS_RELATIVE_HOSTNAME));
369391

370-
listen(Address(SSL_VERIFY_PEER_DNS_IP_ADDRESS,
371-
8888)); // Ensure the echo server is listening on the correct address
392+
listen(Address(DNS_IP_ADDRESS, 8888));
372393

373394
settings.ssl_context->set_verify_flags(CASS_SSL_VERIFY_PEER_IDENTITY_DNS);
374395

375396
String result;
376-
SocketConnector::Ptr connector(new SocketConnector(Address(SSL_VERIFY_PEER_DNS_IP_ADDRESS, 8888),
397+
SocketConnector::Ptr connector(new SocketConnector(Address(DNS_RELATIVE_HOSTNAME, 8888),
377398
bind_callback(on_socket_connected, &result)));
378399

379400
connector->with_settings(settings)->connect(loop());

cpp-driver/src/connection.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,6 @@ Connection::Connection(const Socket::Ptr& socket, const Host::Ptr& host,
116116
, heartbeat_interval_secs_(heartbeat_interval_secs)
117117
, heartbeat_outstanding_(false) {
118118
inc_ref(); // For the event loop
119-
120-
assert(host_->address() == socket_->address() && "Host doesn't match socket address");
121119
host_->increment_connection_count();
122120
}
123121

cpp-driver/src/resolver.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class Resolver : public RefCounted<Resolver> {
5555
req_.data = this;
5656
}
5757

58-
~Resolver() {}
58+
uv_loop_t* loop() { return req_.loop; }
5959

6060
const String& hostname() { return hostname_; }
6161
int port() { return port_; }

cpp-driver/src/socket_connector.cpp

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,25 @@
2222
#define SSL_HANDSHAKE_MAX_BUFFER_SIZE (16 * 1024 + 5)
2323

2424
using namespace datastax;
25+
using namespace datastax::internal;
2526
using namespace datastax::internal::core;
2627

2728
namespace datastax { namespace internal { namespace core {
2829

30+
namespace {
31+
32+
// Used for debugging resolved addresses.
33+
String to_string(const AddressVec& addresses) {
34+
String result;
35+
for (AddressVec::const_iterator it = addresses.begin(), end = addresses.end(); it != end; ++it) {
36+
if (!result.empty()) result.append(", ");
37+
result.append(it->to_string());
38+
}
39+
return result;
40+
}
41+
42+
} // namespace
43+
2944
/**
3045
* A socket handler that handles the SSL handshake process.
3146
*/
@@ -85,6 +100,8 @@ SocketSettings::SocketSettings(const Config& config)
85100
, max_reusable_write_objects(config.max_reusable_write_objects())
86101
, local_address(config.local_address()) {}
87102

103+
Atomic<size_t> SocketConnector::resolved_address_offset_(0);
104+
88105
SocketConnector::SocketConnector(const Address& address, const Callback& callback)
89106
: address_(address)
90107
, callback_(callback)
@@ -99,23 +116,34 @@ SocketConnector* SocketConnector::with_settings(const SocketSettings& settings)
99116
void SocketConnector::connect(uv_loop_t* loop) {
100117
inc_ref(); // For the event loop
101118

102-
if (settings_.hostname_resolution_enabled) {
103-
// Run hostname resolution then connect.
104-
resolver_.reset(new NameResolver(address_, bind_callback(&SocketConnector::on_resolve, this)));
119+
if (!address_.is_resolved()) { // Address not resolved
120+
hostname_ = address_.to_string();
121+
122+
resolver_.reset(new Resolver(hostname_, address_.port(),
123+
bind_callback(&SocketConnector::on_resolve, this)));
105124
resolver_->resolve(loop, settings_.resolve_timeout_ms);
106125
} else {
107-
// Postpone the connection process until after this method ends because it
108-
// can call the callback (via on_error() when when the socket fails to
109-
// init/bind) and destroy its parent.
110-
no_resolve_timer_.start(loop,
111-
0, // Run connect immediately after.
112-
bind_callback(&SocketConnector::on_no_resolve, this));
126+
resolved_address_ = address_;
127+
128+
if (settings_.hostname_resolution_enabled) { // Run hostname resolution then connect.
129+
name_resolver_.reset(
130+
new NameResolver(address_, bind_callback(&SocketConnector::on_name_resolve, this)));
131+
name_resolver_->resolve(loop, settings_.resolve_timeout_ms);
132+
} else {
133+
// Postpone the connection process until after this method ends because it
134+
// can call the callback (via on_error() when when the socket fails to
135+
// init/bind) and destroy its parent.
136+
no_resolve_timer_.start(loop,
137+
0, // Run connect immediately after.
138+
bind_callback(&SocketConnector::on_no_resolve, this));
139+
}
113140
}
114141
}
115142

116143
void SocketConnector::cancel() {
117144
error_code_ = SOCKET_CANCELED;
118145
if (resolver_) resolver_->cancel();
146+
if (name_resolver_) name_resolver_->cancel();
119147
if (connector_) connector_->cancel();
120148
if (socket_) socket_->close();
121149
}
@@ -127,7 +155,7 @@ Socket::Ptr SocketConnector::release_socket() {
127155
}
128156

129157
void SocketConnector::internal_connect(uv_loop_t* loop) {
130-
Socket::Ptr socket(new Socket(address_, settings_.max_reusable_write_objects));
158+
Socket::Ptr socket(new Socket(resolved_address_, settings_.max_reusable_write_objects));
131159

132160
if (uv_tcp_init(loop, socket->handle()) != 0) {
133161
on_error(SOCKET_ERROR_INIT, "Unable to initialize TCP object");
@@ -159,11 +187,11 @@ void SocketConnector::internal_connect(uv_loop_t* loop) {
159187
}
160188

161189
if (settings_.ssl_context) {
162-
ssl_session_.reset(
163-
settings_.ssl_context->create_session(address_, hostname_, address_.server_name()));
190+
ssl_session_.reset(settings_.ssl_context->create_session(resolved_address_, hostname_,
191+
address_.server_name()));
164192
}
165193

166-
connector_.reset(new TcpConnector(address_));
194+
connector_.reset(new TcpConnector(resolved_address_));
167195
connector_->connect(socket_->handle(), bind_callback(&SocketConnector::on_connect, this));
168196
}
169197

@@ -253,7 +281,25 @@ void SocketConnector::on_connect(TcpConnector* tcp_connector) {
253281
}
254282
}
255283

256-
void SocketConnector::on_resolve(NameResolver* resolver) {
284+
void SocketConnector::on_resolve(Resolver* resolver) {
285+
if (resolver->is_success()) {
286+
const AddressVec& addresses(resolver->addresses());
287+
LOG_DEBUG("Resolved the addresses %s for hostname %s", to_string(addresses).c_str(),
288+
hostname_.c_str());
289+
resolved_address_ =
290+
addresses[resolved_address_offset_.fetch_add(MEMORY_ORDER_RELAXED) % addresses.size()];
291+
internal_connect(resolver->loop());
292+
} else if (is_canceled() || resolver->is_canceled()) {
293+
finish();
294+
} else if (resolver->is_timed_out()) {
295+
on_error(SOCKET_ERROR_RESOLVE_TIMEOUT, "Timed out attempting to resolve hostname");
296+
} else {
297+
on_error(SOCKET_ERROR_RESOLVE,
298+
"Unable to resolve hostname '" + String(uv_strerror(resolver->uv_status())) + "'");
299+
}
300+
}
301+
302+
void SocketConnector::on_name_resolve(NameResolver* resolver) {
257303
if (resolver->is_success()) {
258304
LOG_DEBUG("Resolved the hostname %s for address %s", resolver->hostname().c_str(),
259305
resolver->address().to_string().c_str());

cpp-driver/src/socket_connector.hpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
#ifndef DATASTAX_INTERNAL_SOCKET_CONNECTOR_HPP
1818
#define DATASTAX_INTERNAL_SOCKET_CONNECTOR_HPP
1919

20+
#include "atomic.hpp"
2021
#include "callback.hpp"
2122
#include "name_resolver.hpp"
23+
#include "resolver.hpp"
2224
#include "socket.hpp"
2325
#include "tcp_connector.hpp"
2426

@@ -141,17 +143,23 @@ class SocketConnector : public RefCounted<SocketConnector> {
141143

142144
void on_error(SocketError code, const String& message);
143145
void on_connect(TcpConnector* tcp_connecter);
144-
void on_resolve(NameResolver* resolver);
146+
void on_resolve(Resolver* resolver);
147+
void on_name_resolve(NameResolver* resolver);
145148
void on_no_resolve(Timer* timer);
146149

150+
private:
151+
static Atomic<size_t> resolved_address_offset_;
152+
147153
private:
148154
Address address_;
155+
Address resolved_address_;
149156
String hostname_;
150157
Callback callback_;
151158

152159
Socket::Ptr socket_;
153160
TcpConnector::Ptr connector_;
154-
NameResolver::Ptr resolver_;
161+
Resolver::Ptr resolver_;
162+
NameResolver::Ptr name_resolver_;
155163
Timer no_resolve_timer_;
156164

157165
SocketError error_code_;

0 commit comments

Comments
 (0)