Hazelcast C++ Client
Hazelcast C++ Client Library
Loading...
Searching...
No Matches
discovery.cpp
1/*
2 * Copyright (c) 2008-2025, 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
45namespace hazelcast {
46namespace client {
47namespace aws {
48namespace security {
49std::string ec2_request_signer::NEW_LINE = "\n";
50size_t ec2_request_signer::DATE_LENGTH = 8;
51
52ec2_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
61ec2_request_signer::~ec2_request_signer() = default;
62
63std::string
64ec2_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
74std::string
75ec2_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
84std::string
85ec2_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 */
94std::string
95ec2_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
106std::string
107ec2_request_signer::get_canonical_headers() const
108{
109 std::ostringstream out;
110 out << "host:" << endpoint_ << NEW_LINE;
111 return out.str();
112}
113
114std::string
115ec2_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
128std::vector<std::string>
129ec2_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
139std::string
140ec2_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 */
150std::string
151ec2_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
161std::string
162ec2_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 */
173std::vector<unsigned char>
174ec2_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
198std::string
199ec2_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
206std::string
207ec2_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
217std::string
218ec2_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
230unsigned int
231ec2_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
240unsigned int
241ec2_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
249unsigned int
250ec2_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
258unsigned int
259ec2_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
266#if OPENSSL_VERSION_NUMBER >= 0x30000000L
267 //https://www.openssl.org/docs/man3.0/man7/migration_guide.html
268 size_t len = 32;
269 EVP_MD_CTX *mdctx;
270 mdctx = EVP_MD_CTX_new();
271 EVP_PKEY *skey = NULL;
272 skey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, (const unsigned char *)key_buffer, key_len);
273 EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, skey);
274 EVP_DigestSignUpdate(mdctx, data, data_len);
275 EVP_DigestSignFinal(mdctx, hash, &len);
276 EVP_PKEY_free(skey);
277 EVP_MD_CTX_free(mdctx);
278 return static_cast<unsigned int>(len);
279#else
280#if OPENSSL_VERSION_NUMBER >= 0x10100000L
281 HMAC_CTX* hmac = HMAC_CTX_new();
282#else
283 HMAC_CTX* hmac = new HMAC_CTX;
284 HMAC_CTX_init(hmac);
285#endif
286
287 HMAC_Init_ex(hmac, key_buffer, key_len, EVP_sha256(), NULL);
288 HMAC_Update(hmac, data, data_len);
289 unsigned int len = 32;
290 HMAC_Final(hmac, hash, &len);
291
292#if OPENSSL_VERSION_NUMBER >= 0x10100000L
293 HMAC_CTX_free(hmac);
294#else
295 HMAC_CTX_cleanup(hmac);
296 delete hmac;
297#endif
298 return len;
299#endif
300}
301
302std::string
303ec2_request_signer::sha256_hashhex(const std::string& in) const
304{
305#if OPENSSL_VERSION_NUMBER >= 0x10100000L
306 EVP_MD_CTX* ctx_ptr = EVP_MD_CTX_new();
307#else
308 EVP_MD_CTX ctx;
309 EVP_MD_CTX* ctx_ptr = &ctx;
310 EVP_MD_CTX_init(ctx_ptr);
311#endif
312
313 unsigned int hash_len = 0;
314 unsigned char hash[EVP_MAX_MD_SIZE];
315
316 EVP_DigestInit_ex(ctx_ptr, EVP_sha256(), nullptr);
317 EVP_DigestUpdate(ctx_ptr, in.c_str(), in.size());
318 EVP_DigestFinal_ex(ctx_ptr, hash, &hash_len);
319
320#if OPENSSL_VERSION_NUMBER >= 0x10100000L
321 EVP_MD_CTX_free(ctx_ptr);
322#else
323 EVP_MD_CTX_cleanup(ctx_ptr);
324#endif
325
326 return convert_to_hex_string(hash, hash_len);
327}
328} // namespace security
329
330namespace impl {
331const char* Constants::DATE_FORMAT = "%Y%m%dT%H%M%SZ";
332const char* Constants::DOC_VERSION = "2016-11-15";
333const char* Constants::SIGNATURE_METHOD_V4 = "AWS4-HMAC-SHA256";
334const char* Constants::GET = "GET";
335const char* Constants::ECS_CREDENTIALS_ENV_VAR_NAME =
336 "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
337
338Filter::Filter() = default;
339
348void
349Filter::add_filter(const std::string& name, const std::string& value)
350{
351 std::stringstream out;
352 unsigned long index = filters_.size() + 1;
353 out << "Filter." << index << ".Name";
354 filters_[out.str()] = name;
355 out.str("");
356 out.clear();
357 out << "Filter." << index << ".Value.1";
358 filters_[out.str()] = value;
359}
360
361const std::unordered_map<std::string, std::string>&
362Filter::get_filters()
363{
364 return filters_;
365}
366
367const std::string DescribeInstances::QUERY_PREFIX = "/?";
368const std::string DescribeInstances::IAM_ROLE_ENDPOINT = "169.254.169.254";
369const std::string DescribeInstances::IAM_ROLE_QUERY =
370 "/latest/meta-data/iam/security-credentials/";
371const std::string DescribeInstances::IAM_TASK_ROLE_ENDPOINT = "169.254.170.2";
372
373DescribeInstances::DescribeInstances(
374 std::chrono::steady_clock::duration timeout,
375 config::client_aws_config& aws_config,
376 const std::string& endpoint,
377 logger& lg)
378 : timeout_(timeout)
379 , aws_config_(aws_config)
380 , endpoint_(endpoint)
381 , logger_(lg)
382{
383 check_keys_from_iam_roles();
384
385 std::string timeStamp = get_formatted_timestamp();
386 rs_ = std::unique_ptr<security::ec2_request_signer>(
387 new security::ec2_request_signer(aws_config, timeStamp, endpoint));
388 attributes_["Action"] = "DescribeInstances";
389 attributes_["Version"] = impl::Constants::DOC_VERSION;
390 attributes_["X-Amz-Algorithm"] = impl::Constants::SIGNATURE_METHOD_V4;
391 attributes_["X-Amz-Credential"] = rs_->create_formatted_credential();
392 attributes_["X-Amz-Date"] = timeStamp;
393 attributes_["X-Amz-SignedHeaders"] = "host";
394 attributes_["X-Amz-Expires"] = "30";
395 add_filters();
396}
397
398DescribeInstances::~DescribeInstances() = default;
399
400std::unordered_map<std::string, std::string>
401DescribeInstances::execute()
402{
403 std::string signature = rs_->sign(attributes_);
404 attributes_["X-Amz-Signature"] = signature;
405
406 std::istream& stream = call_service();
407 return utility::cloud_utility::unmarshal_the_response(stream, logger_);
408}
409
410std::string
411DescribeInstances::get_formatted_timestamp()
412{
413 using namespace boost::posix_time;
414 ptime now = second_clock::universal_time();
415
416 std::ostringstream out;
417 std::locale timeLocale(out.getloc(),
418 new time_facet(impl::Constants::DATE_FORMAT));
419 out.imbue(timeLocale);
420 out << now;
421 return out.str();
422}
423
424std::istream&
425DescribeInstances::call_service()
426{
427 std::string query = rs_->get_canonicalized_query_string(attributes_);
428 https_client_ =
429 std::unique_ptr<util::SyncHttpsClient>(new util::SyncHttpsClient(
430 endpoint_.c_str(), QUERY_PREFIX + query, timeout_));
431 return https_client_->connect_and_get_response();
432}
433
434void
435DescribeInstances::check_keys_from_iam_roles()
436{
437 if (aws_config_.get_access_key().empty() ||
438 !aws_config_.get_iam_role().empty()) {
439 try_get_default_iam_role();
440 if (!aws_config_.get_iam_role().empty()) {
441 get_keys_from_iam_role();
442 } else {
443 get_keys_from_iam_task_role();
444 }
445 }
446}
447
448void
449DescribeInstances::try_get_default_iam_role()
450{
451 // if none of the below are true
452 if (!(aws_config_.get_iam_role().empty() ||
453 aws_config_.get_iam_role() == "DEFAULT")) {
454 // stop here. No point looking up the default role.
455 return;
456 }
457 try {
458 util::SyncHttpClient httpClient(IAM_ROLE_ENDPOINT, IAM_ROLE_QUERY);
459 std::string roleName;
460 std::istream& responseStream = httpClient.open_connection();
461 responseStream >> roleName;
462 aws_config_.set_iam_role(roleName);
463 } catch (exception::io& e) {
464 BOOST_THROW_EXCEPTION(exception::invalid_configuration(
465 "tryGetDefaultIamRole",
466 std::string("Invalid Aws Configuration. ") + e.what()));
467 }
468}
469
470void
471DescribeInstances::get_keys_from_iam_task_role()
472{
473 // before giving up, attempt to discover whether we're running in an ECS
474 // Container, in which case, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI will
475 // exist as an env var.
476#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
477#pragma warning(push)
478#pragma warning( \
479 disable : 4996) // for 'getenv': This function or variable may be unsafe.
480#endif
481 const char* uri = std::getenv(Constants::ECS_CREDENTIALS_ENV_VAR_NAME);
482#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
483#pragma warning(pop)
484#endif
485 if (!uri) {
486 BOOST_THROW_EXCEPTION(exception::illegal_argument(
487 "getKeysFromIamTaskRole",
488 "Could not acquire credentials! Did not find declared AWS access key "
489 "or IAM Role, and could not discover IAM Task Role or default "
490 "role."));
491 }
492
493 util::SyncHttpClient httpClient(IAM_TASK_ROLE_ENDPOINT, uri);
494
495 try {
496 std::istream& istream = httpClient.open_connection();
497 parse_and_store_role_creds(istream);
498 } catch (exception::iexception& e) {
499 std::stringstream out;
500 out << "Unable to retrieve credentials from IAM Task Role. URI: " << uri
501 << ". \n " << e.what();
502 BOOST_THROW_EXCEPTION(exception::invalid_configuration(
503 "getKeysFromIamTaskRole", out.str()));
504 }
505}
506
507void
508DescribeInstances::get_keys_from_iam_role()
509{
510 std::string query = "/latest/meta-data/iam/security-credentials/" +
511 aws_config_.get_iam_role();
512
513 util::SyncHttpClient httpClient(IAM_ROLE_ENDPOINT, query);
514
515 try {
516 std::istream& istream = httpClient.open_connection();
517 parse_and_store_role_creds(istream);
518 } catch (exception::iexception& e) {
519 std::stringstream out;
520 out << "Unable to retrieve credentials from IAM Task Role. URI: "
521 << query << ". \n " << e.what();
522 BOOST_THROW_EXCEPTION(
523 exception::invalid_configuration("getKeysFromIamRole", out.str()));
524 }
525}
526
527void
528DescribeInstances::parse_and_store_role_creds(std::istream& in)
529{
530 utility::cloud_utility::unmarshal_json_response(
531 in, aws_config_, attributes_);
532}
533
537void
538DescribeInstances::add_filters()
539{
540 Filter filter;
541 if (!aws_config_.get_tag_key().empty()) {
542 if (!aws_config_.get_tag_value().empty()) {
543 filter.add_filter(std::string("tag:") + aws_config_.get_tag_key(),
544 aws_config_.get_tag_value());
545 } else {
546 filter.add_filter("tag-key", aws_config_.get_tag_key());
547 }
548 } else if (!aws_config_.get_tag_value().empty()) {
549 filter.add_filter("tag-value", aws_config_.get_tag_value());
550 }
551
552 if (!aws_config_.get_security_group_name().empty()) {
553 filter.add_filter("instance.group-name",
554 aws_config_.get_security_group_name());
555 }
556
557 filter.add_filter("instance-state-name", "running");
558 const std::unordered_map<std::string, std::string>& filters =
559 filter.get_filters();
560 attributes_.insert(filters.begin(), filters.end());
561}
562
563} // namespace impl
564
565namespace utility {
566std::string
567aws_url_encoder::url_encode(const std::string& value)
568{
569 std::string result = escape_encode(value);
570 boost::replace_all(result, "+", "%20");
571 return result;
572}
573
574std::string
575aws_url_encoder::escape_encode(const std::string& value)
576{
577 std::ostringstream escaped;
578 escaped.fill('0');
579 escaped << std::hex;
580
581 for (std::string::const_iterator i = value.begin(), n = value.end(); i != n;
582 ++i) {
583 std::string::value_type c = (*i);
584
585 // Keep alphanumeric and other accepted characters intact
586 if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
587 escaped << c;
588 continue;
589 }
590
591 // Any other characters are percent-encoded
592 escaped << std::uppercase;
593 escaped << '%' << std::setw(2) << int((unsigned char)c);
594 escaped << std::nouppercase;
595 }
596
597 return escaped.str();
598}
599
600std::unordered_map<std::string, std::string>
601cloud_utility::unmarshal_the_response(std::istream& stream, logger& lg)
602{
603 std::unordered_map<std::string, std::string> privatePublicPairs;
604
605 pt::ptree tree;
606 try {
607 pt::read_xml(stream, tree);
608 } catch (pt::xml_parser_error& e) {
609 HZ_LOG(
610 lg,
611 warning,
612 boost::str(boost::format("The parsed xml stream has errors: %1%") %
613 e.what()));
614 return privatePublicPairs;
615 }
616
617 // Use get_child to find the node containing the reservation set, and
618 // iterate over its children.
619 for (pt::ptree::value_type& item :
620 tree.get_child("DescribeInstancesResponse.reservationSet")) {
621 for (pt::ptree::value_type& instanceItem :
622 item.second.get_child("instancesSet")) {
623 auto privateIp =
624 instanceItem.second.get_optional<std::string>("privateIpAddress");
625 auto publicIp =
626 instanceItem.second.get_optional<std::string>("ipAddress");
627
628 auto prIp = privateIp.value_or("");
629 auto pubIp = publicIp.value_or("");
630
631 if (privateIp) {
632 privatePublicPairs[prIp] = pubIp;
633 HZ_LOG(lg,
634 finest,
635 boost::str(
636 boost::format("Accepting EC2 instance [%1%][%2%]") %
637 instanceItem.second
638 .get_optional<std::string>("tagset.item.value")
639 .value_or("") %
640 prIp));
641 }
642 }
643 }
644 return privatePublicPairs;
645}
646
647void
648cloud_utility::unmarshal_json_response(
649 std::istream& stream,
650 config::client_aws_config& aws_config,
651 std::unordered_map<std::string, std::string>& attributes)
652{
653 pt::ptree json;
654 pt::read_json(stream, json);
655 aws_config.set_access_key(
656 json.get_optional<std::string>("AccessKeyId").get_value_or(""));
657 aws_config.set_secret_key(
658 json.get_optional<std::string>("SecretAccessKey").get_value_or(""));
659 attributes["X-Amz-Security-Token"] =
660 json.get_optional<std::string>("Token").get_value_or("");
661}
662
663} // namespace utility
664
665aws_client::aws_client(std::chrono::steady_clock::duration timeout,
666 config::client_aws_config& aws_config,
667 const client_properties& client_properties,
668 logger& lg)
669 : timeout_(timeout)
670 , aws_config_(aws_config)
671 , logger_(lg)
672{
673 this->endpoint_ = aws_config.get_host_header();
674 if (!aws_config.get_region().empty() &&
675 aws_config.get_region().length() > 0) {
676 if (aws_config.get_host_header().find("ec2.") != 0) {
677 BOOST_THROW_EXCEPTION(exception::invalid_configuration(
678 "aws_client::aws_client",
679 "HostHeader should start with \"ec2.\" prefix"));
680 }
681 boost::replace_all(this->endpoint_,
682 "ec2.",
683 std::string("ec2.") + aws_config.get_region() + ".");
684 }
685
686 aws_member_port_ =
687 client_properties.get_integer(client_properties.get_aws_member_port());
688 if (aws_member_port_ < 0 || aws_member_port_ > 65535) {
689 BOOST_THROW_EXCEPTION(exception::invalid_configuration(
690 "aws_client::aws_client",
691 (boost::format(
692 "Configured aws member port %1% is not "
693 "a valid port number. It should be between 0-65535 inclusive.") %
694 aws_member_port_)
695 .str()));
696 }
697}
698
699std::unordered_map<address, address>
700aws_client::get_addresses()
701{
702 auto addr_pair_map =
703 impl::DescribeInstances(timeout_, aws_config_, endpoint_, logger_)
704 .execute();
705 std::unordered_map<address, address> addr_map;
706 addr_map.reserve(addr_pair_map.size());
707 for (const auto& addr_pair : addr_pair_map) {
708 addr_map.emplace(address{ addr_pair.first, aws_member_port_ },
709 address{ addr_pair.second, aws_member_port_ });
710 }
711 return addr_map;
712}
713} // namespace aws
714} // namespace client
715} // namespace hazelcast
716#else // HZ_BUILD_WITH_SSL
717namespace hazelcast {
718namespace client {
719namespace aws {
720aws_client::aws_client(std::chrono::steady_clock::duration /* timeout */,
721 config::client_aws_config& /* aws_config */,
722 const client_properties& /* client_properties */,
723 logger& /* lg */)
724{
725 util::Preconditions::check_ssl("aws_client::aws_client");
726}
727
728std::unordered_map<address, address>
729aws_client::get_addresses()
730{
731 util::Preconditions::check_ssl("aws_client::get_addresses");
732 return std::unordered_map<address, address>();
733}
734} // namespace aws
735} // namespace client
736} // namespace hazelcast
737#endif // HZ_BUILD_WITH_SSL