Hazelcast C++ Client
Hazelcast C++ Client Library
discovery.cpp
1 /*
2  * Copyright (c) 2008-2022, Hazelcast, Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "hazelcast/util/Preconditions.h"
18 #include "hazelcast/client/aws/aws_client.h"
19 #include "hazelcast/client/client_properties.h"
20 #include "hazelcast/client/config/client_aws_config.h"
21 #include "hazelcast/logger.h"
22 
23 #ifdef HZ_BUILD_WITH_SSL
24 #include <sstream>
25 #include <iomanip>
26 
27 #include <boost/algorithm/string/replace.hpp>
28 #include <boost/date_time.hpp>
29 #include <boost/property_tree/xml_parser.hpp>
30 #include <boost/property_tree/json_parser.hpp>
31 
32 #include "hazelcast/client/aws/utility/aws_url_encoder.h"
33 #include "hazelcast/client/aws/impl/Constants.h"
34 #include "hazelcast/client/aws/security/ec2_request_signer.h"
35 #include "hazelcast/client/aws/impl/Filter.h"
36 #include "hazelcast/client/aws/impl/DescribeInstances.h"
37 #include "hazelcast/client/aws/utility/cloud_utility.h"
38 #include "hazelcast/util/SyncHttpsClient.h"
39 #include "hazelcast/util/SyncHttpClient.h"
40 
41 // openssl include should be after the other so that winsock.h and winsock2.h
42 // conflict does not occur at windows
43 #include <openssl/ssl.h>
44 
45 namespace hazelcast {
46 namespace client {
47 namespace aws {
48 namespace security {
49 std::string ec2_request_signer::NEW_LINE = "\n";
50 size_t ec2_request_signer::DATE_LENGTH = 8;
51 
52 ec2_request_signer::ec2_request_signer(
53  const config::client_aws_config& aws_config,
54  const std::string& timestamp,
55  const std::string& endpoint)
56  : aws_config_(aws_config)
57  , timestamp_(timestamp)
58  , endpoint_(endpoint)
59 {}
60 
61 ec2_request_signer::~ec2_request_signer() = default;
62 
63 std::string
64 ec2_request_signer::sign(
65  const std::unordered_map<std::string, std::string>& attributes)
66 {
67  std::string canonicalRequest = get_canonicalized_request(attributes);
68  std::string stringToSign = create_string_to_sign(canonicalRequest);
69  std::vector<unsigned char> signingKey = derive_signing_key();
70 
71  return create_signature(stringToSign, signingKey);
72 }
73 
74 std::string
75 ec2_request_signer::create_formatted_credential() const
76 {
77  std::stringstream out;
78  out << aws_config_.get_access_key() << '/'
79  << timestamp_.substr(0, DATE_LENGTH) << '/' << aws_config_.get_region()
80  << '/' << "ec2/aws4_request";
81  return out.str();
82 }
83 
84 std::string
85 ec2_request_signer::get_canonicalized_query_string(
86  const std::unordered_map<std::string, std::string>& attributes) const
87 {
88  std::vector<std::string> components = get_list_of_entries(attributes);
89  std::sort(components.begin(), components.end());
90  return get_canonicalized_query_string(components);
91 }
92 
93 /* Task 1 */
94 std::string
95 ec2_request_signer::get_canonicalized_request(
96  const std::unordered_map<std::string, std::string>& attributes) const
97 {
98  std::ostringstream out;
99  out << impl::Constants::GET << NEW_LINE << '/' << NEW_LINE
100  << get_canonicalized_query_string(attributes) << NEW_LINE
101  << get_canonical_headers() << NEW_LINE << "host" << NEW_LINE
102  << sha256_hashhex("");
103  return out.str();
104 }
105 
106 std::string
107 ec2_request_signer::get_canonical_headers() const
108 {
109  std::ostringstream out;
110  out << "host:" << endpoint_ << NEW_LINE;
111  return out.str();
112 }
113 
114 std::string
115 ec2_request_signer::get_canonicalized_query_string(
116  const std::vector<std::string>& list) const
117 {
118  std::ostringstream result;
119  std::vector<std::string>::const_iterator it = list.begin();
120  result << (*it);
121  ++it;
122  for (; it != list.end(); ++it) {
123  result << "&" << *it;
124  }
125  return result.str();
126 }
127 
128 std::vector<std::string>
129 ec2_request_signer::get_list_of_entries(
130  const std::unordered_map<std::string, std::string>& entries) const
131 {
132  std::vector<std::string> components;
133  for (const auto& entry : entries) {
134  components.push_back(format_attribute(entry.first, entry.second));
135  }
136  return components;
137 }
138 
139 std::string
140 ec2_request_signer::format_attribute(const std::string& key,
141  const std::string& value)
142 {
143  std::ostringstream out;
144  out << utility::aws_url_encoder::url_encode(key) << '='
145  << utility::aws_url_encoder::url_encode(value);
146  return out.str();
147 }
148 
149 /* Task 2 */
150 std::string
151 ec2_request_signer::create_string_to_sign(
152  const std::string& canonical_request) const
153 {
154  std::ostringstream out;
155  out << impl::Constants::SIGNATURE_METHOD_V4 << NEW_LINE << timestamp_
156  << NEW_LINE << get_credential_scope() << NEW_LINE
157  << sha256_hashhex(canonical_request);
158  return out.str();
159 }
160 
161 std::string
162 ec2_request_signer::get_credential_scope() const
163 {
164  // datestamp/region/service/API_TERMINATOR
165  // dateStamp
166  std::ostringstream out;
167  out << timestamp_.substr(0, DATE_LENGTH) << "/" << aws_config_.get_region()
168  << "/ec2/aws4_request";
169  return out.str();
170 }
171 
172 /* Task 3 */
173 std::vector<unsigned char>
174 ec2_request_signer::derive_signing_key() const
175 {
176  const std::string& signKey = aws_config_.get_secret_key();
177  std::string dateStamp = timestamp_.substr(0, DATE_LENGTH);
178  // this is derived from
179  // http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python
180 
181  unsigned char kDate[32];
182  std::string key = std::string("AWS4") + signKey;
183  int kDateLen = hmac_sh_a256_bytes(key, dateStamp, kDate);
184 
185  unsigned char kRegion[32];
186  int kRegionLen =
187  hmac_sh_a256_bytes(kDate, kDateLen, aws_config_.get_region(), kRegion);
188 
189  unsigned char kService[32];
190  int kServiceLen = hmac_sh_a256_bytes(kRegion, kRegionLen, "ec2", kService);
191 
192  std::vector<unsigned char> mSigning(32);
193  hmac_sh_a256_bytes(kService, kServiceLen, "aws4_request", &mSigning[0]);
194 
195  return mSigning;
196 }
197 
198 std::string
199 ec2_request_signer::create_signature(
200  const std::string& string_to_sign,
201  const std::vector<unsigned char>& signing_key) const
202 {
203  return hmac_sh_a256_hex(signing_key, string_to_sign);
204 }
205 
206 std::string
207 ec2_request_signer::hmac_sh_a256_hex(const std::vector<unsigned char>& key,
208  const std::string& msg) const
209 {
210  unsigned char hash[32];
211 
212  unsigned int len = hmac_sh_a256_bytes(key, msg, hash);
213 
214  return convert_to_hex_string(hash, len);
215 }
216 
217 std::string
218 ec2_request_signer::convert_to_hex_string(const unsigned char* buffer,
219  unsigned int len) const
220 {
221  std::stringstream ss;
222  ss << std::hex << std::setfill('0');
223  for (unsigned int i = 0; i < len; i++) {
224  ss << std::hex << std::setw(2) << (unsigned int)buffer[i];
225  }
226 
227  return (ss.str());
228 }
229 
230 unsigned int
231 ec2_request_signer::hmac_sh_a256_bytes(const void* key,
232  int key_len,
233  const std::string& msg,
234  unsigned char* hash) const
235 {
236  return hmac_sh_a256_bytes(
237  key, key_len, (unsigned char*)&msg[0], msg.length(), hash);
238 }
239 
240 unsigned int
241 ec2_request_signer::hmac_sh_a256_bytes(const std::string& key,
242  const std::string& msg,
243  unsigned char* hash) const
244 {
245  return hmac_sh_a256_bytes(
246  &key[0], (int)key.length(), (unsigned char*)&msg[0], msg.length(), hash);
247 }
248 
249 unsigned int
250 ec2_request_signer::hmac_sh_a256_bytes(const std::vector<unsigned char>& key,
251  const std::string& msg,
252  unsigned char* hash) const
253 {
254  return hmac_sh_a256_bytes(
255  &key[0], (int)key.size(), (unsigned char*)&msg[0], msg.length(), hash);
256 }
257 
258 unsigned int
259 ec2_request_signer::hmac_sh_a256_bytes(const void* key_buffer,
260  int key_len,
261  const unsigned char* data,
262  size_t data_len,
263  unsigned char* hash) const
264 {
265 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
266  HMAC_CTX* hmac = HMAC_CTX_new();
267 #else
268  HMAC_CTX* hmac = new HMAC_CTX;
269  HMAC_CTX_init(hmac);
270 #endif
271 
272  HMAC_Init_ex(hmac, key_buffer, key_len, EVP_sha256(), NULL);
273  HMAC_Update(hmac, data, data_len);
274  unsigned int len = 32;
275  HMAC_Final(hmac, hash, &len);
276 
277 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
278  HMAC_CTX_free(hmac);
279 #else
280  HMAC_CTX_cleanup(hmac);
281  delete hmac;
282 #endif
283 
284  return len;
285 }
286 
287 std::string
288 ec2_request_signer::sha256_hashhex(const std::string& in) const
289 {
290 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
291  EVP_MD_CTX* ctx_ptr = EVP_MD_CTX_new();
292 #else
293  EVP_MD_CTX ctx;
294  EVP_MD_CTX* ctx_ptr = &ctx;
295  EVP_MD_CTX_init(ctx_ptr);
296 #endif
297 
298  unsigned int hash_len = 0;
299  unsigned char hash[EVP_MAX_MD_SIZE];
300 
301  EVP_DigestInit_ex(ctx_ptr, EVP_sha256(), nullptr);
302  EVP_DigestUpdate(ctx_ptr, in.c_str(), in.size());
303  EVP_DigestFinal_ex(ctx_ptr, hash, &hash_len);
304 
305 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
306  EVP_MD_CTX_free(ctx_ptr);
307 #else
308  EVP_MD_CTX_cleanup(ctx_ptr);
309 #endif
310 
311  return convert_to_hex_string(hash, hash_len);
312 }
313 } // namespace security
314 
315 namespace impl {
316 const char* Constants::DATE_FORMAT = "%Y%m%dT%H%M%SZ";
317 const char* Constants::DOC_VERSION = "2016-11-15";
318 const char* Constants::SIGNATURE_METHOD_V4 = "AWS4-HMAC-SHA256";
319 const char* Constants::GET = "GET";
320 const char* Constants::ECS_CREDENTIALS_ENV_VAR_NAME =
321  "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
322 
323 Filter::Filter() = default;
324 
333 void
334 Filter::add_filter(const std::string& name, const std::string& value)
335 {
336  std::stringstream out;
337  unsigned long index = filters_.size() + 1;
338  out << "Filter." << index << ".Name";
339  filters_[out.str()] = name;
340  out.str("");
341  out.clear();
342  out << "Filter." << index << ".Value.1";
343  filters_[out.str()] = value;
344 }
345 
346 const std::unordered_map<std::string, std::string>&
347 Filter::get_filters()
348 {
349  return filters_;
350 }
351 
352 const std::string DescribeInstances::QUERY_PREFIX = "/?";
353 const std::string DescribeInstances::IAM_ROLE_ENDPOINT = "169.254.169.254";
354 const std::string DescribeInstances::IAM_ROLE_QUERY =
355  "/latest/meta-data/iam/security-credentials/";
356 const std::string DescribeInstances::IAM_TASK_ROLE_ENDPOINT = "169.254.170.2";
357 
358 DescribeInstances::DescribeInstances(
359  std::chrono::steady_clock::duration timeout,
360  config::client_aws_config& aws_config,
361  const std::string& endpoint,
362  logger& lg)
363  : timeout_(timeout)
364  , aws_config_(aws_config)
365  , endpoint_(endpoint)
366  , logger_(lg)
367 {
368  check_keys_from_iam_roles();
369 
370  std::string timeStamp = get_formatted_timestamp();
371  rs_ = std::unique_ptr<security::ec2_request_signer>(
372  new security::ec2_request_signer(aws_config, timeStamp, endpoint));
373  attributes_["Action"] = "DescribeInstances";
374  attributes_["Version"] = impl::Constants::DOC_VERSION;
375  attributes_["X-Amz-Algorithm"] = impl::Constants::SIGNATURE_METHOD_V4;
376  attributes_["X-Amz-Credential"] = rs_->create_formatted_credential();
377  attributes_["X-Amz-Date"] = timeStamp;
378  attributes_["X-Amz-SignedHeaders"] = "host";
379  attributes_["X-Amz-Expires"] = "30";
380  add_filters();
381 }
382 
383 DescribeInstances::~DescribeInstances() = default;
384 
385 std::unordered_map<std::string, std::string>
386 DescribeInstances::execute()
387 {
388  std::string signature = rs_->sign(attributes_);
389  attributes_["X-Amz-Signature"] = signature;
390 
391  std::istream& stream = call_service();
392  return utility::cloud_utility::unmarshal_the_response(stream, logger_);
393 }
394 
395 std::string
396 DescribeInstances::get_formatted_timestamp()
397 {
398  using namespace boost::posix_time;
399  ptime now = second_clock::universal_time();
400 
401  std::ostringstream out;
402  std::locale timeLocale(out.getloc(),
403  new time_facet(impl::Constants::DATE_FORMAT));
404  out.imbue(timeLocale);
405  out << now;
406  return out.str();
407 }
408 
409 std::istream&
410 DescribeInstances::call_service()
411 {
412  std::string query = rs_->get_canonicalized_query_string(attributes_);
413  https_client_ =
414  std::unique_ptr<util::SyncHttpsClient>(new util::SyncHttpsClient(
415  endpoint_.c_str(), QUERY_PREFIX + query, timeout_));
416  return https_client_->connect_and_get_response();
417 }
418 
419 void
420 DescribeInstances::check_keys_from_iam_roles()
421 {
422  if (aws_config_.get_access_key().empty() ||
423  !aws_config_.get_iam_role().empty()) {
424  try_get_default_iam_role();
425  if (!aws_config_.get_iam_role().empty()) {
426  get_keys_from_iam_role();
427  } else {
428  get_keys_from_iam_task_role();
429  }
430  }
431 }
432 
433 void
434 DescribeInstances::try_get_default_iam_role()
435 {
436  // if none of the below are true
437  if (!(aws_config_.get_iam_role().empty() ||
438  aws_config_.get_iam_role() == "DEFAULT")) {
439  // stop here. No point looking up the default role.
440  return;
441  }
442  try {
443  util::SyncHttpClient httpClient(IAM_ROLE_ENDPOINT, IAM_ROLE_QUERY);
444  std::string roleName;
445  std::istream& responseStream = httpClient.open_connection();
446  responseStream >> roleName;
447  aws_config_.set_iam_role(roleName);
448  } catch (exception::io& e) {
449  BOOST_THROW_EXCEPTION(exception::invalid_configuration(
450  "tryGetDefaultIamRole",
451  std::string("Invalid Aws Configuration. ") + e.what()));
452  }
453 }
454 
455 void
456 DescribeInstances::get_keys_from_iam_task_role()
457 {
458  // before giving up, attempt to discover whether we're running in an ECS
459  // Container, in which case, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI will
460  // exist as an env var.
461 #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
462 #pragma warning(push)
463 #pragma warning( \
464  disable : 4996) // for 'getenv': This function or variable may be unsafe.
465 #endif
466  const char* uri = std::getenv(Constants::ECS_CREDENTIALS_ENV_VAR_NAME);
467 #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
468 #pragma warning(pop)
469 #endif
470  if (!uri) {
471  BOOST_THROW_EXCEPTION(exception::illegal_argument(
472  "getKeysFromIamTaskRole",
473  "Could not acquire credentials! Did not find declared AWS access key "
474  "or IAM Role, and could not discover IAM Task Role or default "
475  "role."));
476  }
477 
478  util::SyncHttpClient httpClient(IAM_TASK_ROLE_ENDPOINT, uri);
479 
480  try {
481  std::istream& istream = httpClient.open_connection();
482  parse_and_store_role_creds(istream);
483  } catch (exception::iexception& e) {
484  std::stringstream out;
485  out << "Unable to retrieve credentials from IAM Task Role. URI: " << uri
486  << ". \n " << e.what();
487  BOOST_THROW_EXCEPTION(exception::invalid_configuration(
488  "getKeysFromIamTaskRole", out.str()));
489  }
490 }
491 
492 void
493 DescribeInstances::get_keys_from_iam_role()
494 {
495  std::string query = "/latest/meta-data/iam/security-credentials/" +
496  aws_config_.get_iam_role();
497 
498  util::SyncHttpClient httpClient(IAM_ROLE_ENDPOINT, query);
499 
500  try {
501  std::istream& istream = httpClient.open_connection();
502  parse_and_store_role_creds(istream);
503  } catch (exception::iexception& e) {
504  std::stringstream out;
505  out << "Unable to retrieve credentials from IAM Task Role. URI: "
506  << query << ". \n " << e.what();
507  BOOST_THROW_EXCEPTION(
508  exception::invalid_configuration("getKeysFromIamRole", out.str()));
509  }
510 }
511 
512 void
513 DescribeInstances::parse_and_store_role_creds(std::istream& in)
514 {
515  utility::cloud_utility::unmarshal_json_response(
516  in, aws_config_, attributes_);
517 }
518 
522 void
523 DescribeInstances::add_filters()
524 {
525  Filter filter;
526  if (!aws_config_.get_tag_key().empty()) {
527  if (!aws_config_.get_tag_value().empty()) {
528  filter.add_filter(std::string("tag:") + aws_config_.get_tag_key(),
529  aws_config_.get_tag_value());
530  } else {
531  filter.add_filter("tag-key", aws_config_.get_tag_key());
532  }
533  } else if (!aws_config_.get_tag_value().empty()) {
534  filter.add_filter("tag-value", aws_config_.get_tag_value());
535  }
536 
537  if (!aws_config_.get_security_group_name().empty()) {
538  filter.add_filter("instance.group-name",
539  aws_config_.get_security_group_name());
540  }
541 
542  filter.add_filter("instance-state-name", "running");
543  const std::unordered_map<std::string, std::string>& filters =
544  filter.get_filters();
545  attributes_.insert(filters.begin(), filters.end());
546 }
547 
548 } // namespace impl
549 
550 namespace utility {
551 std::string
552 aws_url_encoder::url_encode(const std::string& value)
553 {
554  std::string result = escape_encode(value);
555  boost::replace_all(result, "+", "%20");
556  return result;
557 }
558 
559 std::string
560 aws_url_encoder::escape_encode(const std::string& value)
561 {
562  std::ostringstream escaped;
563  escaped.fill('0');
564  escaped << std::hex;
565 
566  for (std::string::const_iterator i = value.begin(), n = value.end(); i != n;
567  ++i) {
568  std::string::value_type c = (*i);
569 
570  // Keep alphanumeric and other accepted characters intact
571  if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
572  escaped << c;
573  continue;
574  }
575 
576  // Any other characters are percent-encoded
577  escaped << std::uppercase;
578  escaped << '%' << std::setw(2) << int((unsigned char)c);
579  escaped << std::nouppercase;
580  }
581 
582  return escaped.str();
583 }
584 
585 std::unordered_map<std::string, std::string>
586 cloud_utility::unmarshal_the_response(std::istream& stream, logger& lg)
587 {
588  std::unordered_map<std::string, std::string> privatePublicPairs;
589 
590  pt::ptree tree;
591  try {
592  pt::read_xml(stream, tree);
593  } catch (pt::xml_parser_error& e) {
594  HZ_LOG(
595  lg,
596  warning,
597  boost::str(boost::format("The parsed xml stream has errors: %1%") %
598  e.what()));
599  return privatePublicPairs;
600  }
601 
602  // Use get_child to find the node containing the reservation set, and
603  // iterate over its children.
604  for (pt::ptree::value_type& item :
605  tree.get_child("DescribeInstancesResponse.reservationSet")) {
606  for (pt::ptree::value_type& instanceItem :
607  item.second.get_child("instancesSet")) {
608  auto privateIp =
609  instanceItem.second.get_optional<std::string>("privateIpAddress");
610  auto publicIp =
611  instanceItem.second.get_optional<std::string>("ipAddress");
612 
613  auto prIp = privateIp.value_or("");
614  auto pubIp = publicIp.value_or("");
615 
616  if (privateIp) {
617  privatePublicPairs[prIp] = pubIp;
618  HZ_LOG(lg,
619  finest,
620  boost::str(
621  boost::format("Accepting EC2 instance [%1%][%2%]") %
622  instanceItem.second
623  .get_optional<std::string>("tagset.item.value")
624  .value_or("") %
625  prIp));
626  }
627  }
628  }
629  return privatePublicPairs;
630 }
631 
632 void
633 cloud_utility::unmarshal_json_response(
634  std::istream& stream,
635  config::client_aws_config& aws_config,
636  std::unordered_map<std::string, std::string>& attributes)
637 {
638  pt::ptree json;
639  pt::read_json(stream, json);
640  aws_config.set_access_key(
641  json.get_optional<std::string>("AccessKeyId").get_value_or(""));
642  aws_config.set_secret_key(
643  json.get_optional<std::string>("SecretAccessKey").get_value_or(""));
644  attributes["X-Amz-Security-Token"] =
645  json.get_optional<std::string>("Token").get_value_or("");
646 }
647 
648 } // namespace utility
649 
650 aws_client::aws_client(std::chrono::steady_clock::duration timeout,
651  config::client_aws_config& aws_config,
652  const client_properties& client_properties,
653  logger& lg)
654  : timeout_(timeout)
655  , aws_config_(aws_config)
656  , logger_(lg)
657 {
658  this->endpoint_ = aws_config.get_host_header();
659  if (!aws_config.get_region().empty() &&
660  aws_config.get_region().length() > 0) {
661  if (aws_config.get_host_header().find("ec2.") != 0) {
662  BOOST_THROW_EXCEPTION(exception::invalid_configuration(
663  "aws_client::aws_client",
664  "HostHeader should start with \"ec2.\" prefix"));
665  }
666  boost::replace_all(this->endpoint_,
667  "ec2.",
668  std::string("ec2.") + aws_config.get_region() + ".");
669  }
670 
671  aws_member_port_ =
672  client_properties.get_integer(client_properties.get_aws_member_port());
673  if (aws_member_port_ < 0 || aws_member_port_ > 65535) {
674  BOOST_THROW_EXCEPTION(exception::invalid_configuration(
675  "aws_client::aws_client",
676  (boost::format(
677  "Configured aws member port %1% is not "
678  "a valid port number. It should be between 0-65535 inclusive.") %
679  aws_member_port_)
680  .str()));
681  }
682 }
683 
684 std::unordered_map<address, address>
685 aws_client::get_addresses()
686 {
687  auto addr_pair_map =
688  impl::DescribeInstances(timeout_, aws_config_, endpoint_, logger_)
689  .execute();
690  std::unordered_map<address, address> addr_map;
691  addr_map.reserve(addr_pair_map.size());
692  for (const auto& addr_pair : addr_pair_map) {
693  addr_map.emplace(address{ addr_pair.first, aws_member_port_ },
694  address{ addr_pair.second, aws_member_port_ });
695  }
696  return addr_map;
697 }
698 } // namespace aws
699 } // namespace client
700 } // namespace hazelcast
701 #else // HZ_BUILD_WITH_SSL
702 namespace hazelcast {
703 namespace client {
704 namespace aws {
705 aws_client::aws_client(std::chrono::steady_clock::duration timeout,
706  config::client_aws_config& aws_config,
707  const client_properties& client_properties,
708  logger& lg)
709 {
710  util::Preconditions::check_ssl("aws_client::aws_client");
711 }
712 
713 std::unordered_map<address, address>
714 aws_client::get_addresses()
715 {
716  util::Preconditions::check_ssl("aws_client::get_addresses");
717  return std::unordered_map<address, address>();
718 }
719 } // namespace aws
720 } // namespace client
721 } // namespace hazelcast
722 #endif // HZ_BUILD_WITH_SSL