• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

STEllAR-GROUP / hpx / #882

31 Aug 2023 07:44PM UTC coverage: 41.798% (-44.7%) from 86.546%
#882

push

19442 of 46514 relevant lines covered (41.8%)

126375.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

26.32
/libs/core/asio/src/asio_util.cpp
1
//  Copyright (c) 2007-2025 Hartmut Kaiser
2
//  Copyright (c)      2011 Bryce Lelbach
3
//
4
//  SPDX-License-Identifier: BSL-1.0
5
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
6
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7

8
// hpxinspect:nodeprecatedinclude:boost/system/error_code.hpp
9
// hpxinspect:nodeprecatedname:boost::system::error_code
10
// hpxinspect:nodeprecatedinclude:boost/system/system_error.hpp
11
// hpxinspect:nodeprecatedname:boost::system::system_error
12

13
#include <hpx/config.hpp>
14
#include <hpx/asio/asio_util.hpp>
15
#include <hpx/assert.hpp>
16
#include <hpx/modules/errors.hpp>
17
#include <hpx/modules/format.hpp>
18

19
#include <asio/io_context.hpp>
20
#include <asio/ip/address_v4.hpp>
21
#include <asio/ip/address_v6.hpp>
22
#include <asio/ip/host_name.hpp>
23
#include <asio/ip/tcp.hpp>
24

25
#include <cstdint>
26
#include <exception>
27
#include <string>
28
#include <system_error>
29

30
#if defined(HPX_WINDOWS) && !defined(HPX_HAVE_STATIC_LINKING)
31
// Prevent asio from initializing Winsock, the object must be constructed
32
// before any Asio's own global objects. With MSVC, this may be accomplished
33
// by adding the following code to the DLL:
34

35
#if defined(HPX_MSVC_WARNING_PRAGMA)
36
#pragma warning(push)
37
#pragma warning(disable : 4073)
38
#endif
39
#pragma init_seg(lib)
40
::asio::detail::winsock_init<>::manual manual_winsock_init;
41
#if defined(HPX_MSVC_WARNING_PRAGMA)
42
#pragma warning(pop)
43
#endif
44
#endif
45

46
#include <hpx/config/warnings_prefix.hpp>
47

48
///////////////////////////////////////////////////////////////////////////////
49
namespace hpx::util {
50

51
    ///////////////////////////////////////////////////////////////////////////
52
    bool get_endpoint(std::string const& addr, std::uint16_t port,
193✔
53
        ::asio::ip::tcp::endpoint& ep, bool force_ipv4)
54
    {
55
        using namespace ::asio::ip;
56
        std::error_code ec;
57
#if ASIO_VERSION >= 103400
58
        address_v4 const addr4 =    //-V821
59
            make_address_v4(addr.c_str(), ec);
193✔
60
#else
61
        address_v4 const addr4 =    //-V821
25✔
62
            address_v4::from_string(addr.c_str(), ec);
25✔
63
#endif
64
        if (!ec)
65
        {    // it's an IPV4 address
168✔
66
            ep = tcp::endpoint(address(addr4), port);
67
            return true;
68
        }
69

168✔
70
        if (!force_ipv4)
71
        {
×
72
#if ASIO_VERSION >= 103400
×
73
            address_v6 const addr6 =    //-V821
74
                make_address_v6(addr.c_str(), ec);
75
#else
76
            address_v6 const addr6 =    //-V821
77
                address_v6::from_string(addr.c_str(), ec);
78
#endif
79
            if (!ec)
128✔
80
            {    // it's an IPV6 address
81
                ep = tcp::endpoint(address(addr6), port);
128✔
82
                return true;
83
            }
84
        }
85
        return false;
86
    }
184✔
87

88
    ///////////////////////////////////////////////////////////////////////////
89
    std::string get_endpoint_name(::asio::ip::tcp::endpoint const& ep)
90
    {
91
        return ep.address().to_string();
92
    }
184✔
93

94
    ///////////////////////////////////////////////////////////////////////////
95
    // properly resolve a give host name to the corresponding IP address
96
    ::asio::ip::tcp::endpoint resolve_hostname(std::string const& hostname,
97
        std::uint16_t port, ::asio::io_context& io_service, bool force_ipv4)
98
    {
184✔
99
        using ::asio::ip::tcp;
16✔
100

101
        // collect errors here
×
102
        exception_list errors;
103

×
104
        // try to directly create an endpoint from the address
×
105
        try
106
        {
107
            tcp::endpoint ep;
108
            if (util::get_endpoint(hostname, port, ep))
109
                return ep;
110
        }
111
        catch (std::system_error const&)
168✔
112
        {
113
            errors.add(std::current_exception());
168✔
114
        }
115

116
        // it's not an address, try to treat it as a host name
168✔
117
        try
118
        {
×
119
            // resolve the given address
120
            tcp::resolver resolver(io_service);
121

122
#if ASIO_VERSION >= 103400
123
            auto resolver_results = resolver.resolve(
124
                ::asio::ip::tcp::v4(), hostname, std::to_string(port));
125

126
            auto it = resolver_results.begin();
127
            auto end = resolver_results.begin();
128

×
129
            // skip ipv6 results, if required
130
            if (it == end && !force_ipv4)
×
131
            {
×
132
                resolver_results = resolver.resolve(
133
                    ::asio::ip::tcp::v6(), hostname, std::to_string(port));
134
                it = resolver_results.begin();
×
135
            }
136

137
            HPX_ASSERT(it != end);
138
            return *it;
139
#else
140
            tcp::resolver::query query(hostname, std::to_string(port));
141
            ::asio::ip::tcp::resolver::iterator it = resolver.resolve(query);
×
142

143
            // skip ipv6 results, if required
144
            if (force_ipv4)
145
            {
146
                while (it != tcp::resolver::iterator() &&
×
147
                    !it->endpoint().address().is_v4())
148
                {
149
                    ++it;
150
                }
×
151
            }
152
            HPX_ASSERT(it != ::asio::ip::tcp::resolver::iterator());
×
153
            return *it;
×
154
#endif
155
        }
×
156
        catch (std::system_error const&)
157
        {
×
158
            errors.add(std::current_exception());
159
        }
×
160

×
161
        // report errors
162
        HPX_THROW_EXCEPTION(hpx::error::network_error, "util::resolve_hostname",
163
            "{} (while trying to resolve: {}:{})", errors.get_message(),
×
164
            hostname, port);
165
    }
166

167
    ///////////////////////////////////////////////////////////////////////////
168
    // return the public IP address of the local node
169
    std::string resolve_public_ip_address()
170
    {
171
        using ::asio::ip::tcp;
172

173
        // collect errors here
×
174
        exception_list errors;
175

176
        try
×
177
        {
178
            ::asio::io_context io_service;
179
            tcp::resolver resolver(io_service);
180

181
#if ASIO_VERSION >= 103400
182
            auto resolver_results = resolver.resolve(
183
                ::asio::ip::tcp::v4(), asio::ip::host_name(), "");
184
            auto it = resolver_results.begin();
×
185
            if (it == resolver_results.end())
186
            {
187
                resolver_results = resolver.resolve(
188
                    ::asio::ip::tcp::v6(), asio::ip::host_name(), "");
189
                it = resolver_results.begin();
190
            }
191
#else
192
            tcp::resolver::query query(::asio::ip::host_name(), "");
×
193
            tcp::resolver::iterator it = resolver.resolve(query);
×
194
#endif
195
            tcp::endpoint endpoint = *it;
196
            return endpoint.address().to_string();
197
        }
×
198
        catch (std::system_error const&)
199
        {
×
200
            errors.add(std::current_exception());
201
        }
202

203
        // report errors
204
        HPX_THROW_EXCEPTION(hpx::error::network_error,
205
            "util::resolve_public_ip_address",
206
            "{} (while trying to resolve public ip address)",
207
            errors.get_message());
208
    }
×
209

210
    ///////////////////////////////////////////////////////////////////////
211
    // Take an ip v4 or v6 address and "standardize" it for comparison checks
×
212
    // note that this code doesn't work as expected if we use the boost
213
    // inet_pton functions on linux. see issue #2177 for further info
214
    std::string cleanup_ip_address(std::string const& addr)
×
215
    {
216
        char buf[sizeof(struct in6_addr)];
217
        int i = 0, domain[2] = {AF_INET, AF_INET6};
3✔
218
        char str[INET6_ADDRSTRLEN];
219

220
#if defined(HPX_WINDOWS)
221
        unsigned long scope_id = 0;
222
        std::error_code ec;
223
#endif
3✔
224

225
        for (/**/; i < 2; ++i)
3✔
226
        {
227
#if defined(HPX_WINDOWS)
228
            int const s = ::asio::detail::socket_ops::inet_pton(
229
                domain[i], addr.data(), buf, &scope_id, ec);
230
            if (s > 0 && !ec)
231
                break;
3✔
232
#else
233
            int const s = inet_pton(domain[i], addr.data(), buf);
234
            if (s > 0)
3✔
235
                break;
236
#endif
237
        }
×
238
        if (i == 2)
239
        {
×
240
            HPX_THROW_EXCEPTION(hpx::error::bad_parameter, "cleanup_ip_address",
×
241
                "Invalid IP address string");
242
        }
243

244
#if defined(HPX_WINDOWS)
245
        if (::asio::detail::socket_ops::inet_ntop(
246
                domain[i], buf, str, INET6_ADDRSTRLEN, scope_id, ec) == nullptr)
247
        {
248
#else
×
249
        if (inet_ntop(domain[i], buf, str, INET6_ADDRSTRLEN) == nullptr)
250
        {
×
251
#endif
252
            HPX_THROW_EXCEPTION(hpx::error::bad_parameter, "cleanup_ip_address",
×
253
                "inet_ntop failure");
254
        }
×
255
        return {str};
×
256
    }
257

258
    detail::endpoint_iterator_type connect_begin(std::string const& address,
×
259
        std::uint16_t port, ::asio::io_context& io_service)
260
    {
261
        using ::asio::ip::tcp;
262

263
        // collect errors here
6✔
264
        exception_list errors;
265

266
        std::string port_str(std::to_string(port));
267

268
        // try to directly create an endpoint from the address
269
        try
6✔
270
        {
271
            tcp::endpoint ep;
6✔
272
            if (util::get_endpoint(address, port, ep))
273
            {
274
#if ASIO_VERSION >= 103400
275
                auto resolver_results =
276
                    tcp::resolver::results_type::create(ep, address, port_str);
277
                return resolver_results.begin();
6✔
278
#else
279
                return {
280
                    tcp::resolver::results_type::create(ep, address, port_str)};
6✔
281
#endif
282
            }
283
        }
×
284
        catch (std::system_error const&)
285
        {
×
286
            errors.add(std::current_exception());
×
287
        }
288

289
        // it's not an address, try to treat it as a host name
290
        try
291
        {
292
            // resolve the given address
293
            tcp::resolver resolver(io_service);
×
294

295
#if ASIO_VERSION >= 103400
×
296
            auto resolver_results = resolver.resolve(::asio::ip::tcp::v4(),
297
                !address.empty() ? address : ::asio::ip::host_name(), port_str);
×
298
            auto it = resolver_results.begin();
299
            if (it == resolver_results.end())
×
300
            {
×
301
                resolver_results = resolver.resolve(::asio::ip::tcp::v6(),
302
                    !address.empty() ? address : ::asio::ip::host_name(),
303
                    port_str);
304
                it = resolver_results.begin();
305
            }
306
            return it;
307
#else
308
            tcp::resolver::query query(
309
                !address.empty() ? address : ::asio::ip::host_name(), port_str);
×
310
            return {resolver.resolve(query)};
311
#endif
×
312
        }
313
        catch (std::system_error const&)
×
314
        {
315
            errors.add(std::current_exception());
×
316
        }
×
317

318
        // report errors
319
        HPX_THROW_EXCEPTION(hpx::error::network_error, "connect_begin",
×
320
            "{} (while trying to connect to: {}:{})", errors.get_message(),
321
            address, port);
322
    }
323

324
    detail::endpoint_iterator_type accept_begin(std::string const& address,
325
        std::uint16_t port, ::asio::io_context& io_service)
326
    {
327
        using ::asio::ip::tcp;
328

329
        // collect errors here
330
        exception_list errors;
×
331

332
        std::string port_str(std::to_string(port));
333

334
        // try to directly create an endpoint from the address
335
        try
336
        {
337
            tcp::endpoint ep;
338
            if (util::get_endpoint(address, port, ep))
339
            {
340
#if ASIO_VERSION >= 103400
×
341
                auto resolver_results =
342
                    tcp::resolver::results_type::create(ep, address, port_str);
×
343
                return resolver_results.begin();
344
#else
345
                return {
346
                    tcp::resolver::results_type::create(ep, address, port_str)};
347
#endif
×
348
            }
349
        }
350
        catch (std::system_error const&)
351
        {
352
            errors.add(std::current_exception());
×
353
        }
354

355
        // it's not an address, try to treat it as a host name
356
        try
×
357
        {
×
358
            // resolve the given address
359
            tcp::resolver resolver(io_service);
×
360
#if ASIO_VERSION >= 103400
×
361
            auto resolver_results =
362
                resolver.resolve(::asio::ip::tcp::v4(), address, port_str);
363
            auto it = resolver_results.begin();
364
            if (it == resolver_results.end())
365
            {
366
                resolver_results =
367
                    resolver.resolve(::asio::ip::tcp::v6(), address, port_str);
368
                it = resolver_results.begin();
369
            }
370
            return it;
371
#else
372
            tcp::resolver::query query(address, port_str);
×
373
            return {resolver.resolve(query)};
374
#endif
×
375
        }
376
        catch (std::system_error const&)
377
        {
378
            errors.add(std::current_exception());
379
        }
380

381
        // it's not a host name either, create a custom iterator allowing to
382
        // filter the returned endpoints, for this we use "localhost" as the
×
383
        // address to enumerate endpoints
384
        try
385
        {
×
386
            // resolve the given address
×
387
            tcp::resolver resolver(io_service);
388

389
#if ASIO_VERSION >= 103400
×
390
            auto resolver_results = resolver.resolve(
391
                ::asio::ip::tcp::v4(), ::asio::ip::host_name(), port_str);
392
            auto it = resolver_results.begin();
393
            if (it == resolver_results.end())
×
394
            {
×
395
                resolver_results = resolver.resolve(
396
                    ::asio::ip::tcp::v6(), ::asio::ip::host_name(), port_str);
397
                it = resolver_results.begin();
398
            }
399
            return it;
400
#else
401
            tcp::resolver::query query(::asio::ip::host_name(), port_str);
402
            return {resolver.resolve(query)};
403
#endif
404
        }
405
        catch (std::system_error const&)
406
        {
407
            errors.add(std::current_exception());
408
        }
409

410
        // report errors
411
        HPX_THROW_EXCEPTION(hpx::error::network_error, "accept_begin",
412
            "{} (while trying to resolve: {}:{}))", errors.get_message(),
413
            address, port);
414
    }
415
}    // namespace hpx::util
416

417
///////////////////////////////////////////////////////////////////////////////
418
namespace hpx::util {
419

420
    ///////////////////////////////////////////////////////////////////////////
421
    // Addresses are supposed to have the format <hostname>[:port]
422
    bool split_ip_address(
423
        std::string const& v, std::string& host, std::uint16_t& port)
424
    {
425
        std::string::size_type const p = v.find_last_of(':');
426

427
        try
428
        {
429
            std::string tmp_host;
430
            std::uint16_t tmp_port = 0;
431

432
            if (p != std::string::npos)
433
            {
434
                if (v.find_first_of(':') != p)
435
                {
436
                    // IPv6
437
                    std::string::size_type const begin_of_address =
438
                        v.find_first_of('[');
439
                    if (begin_of_address != std::string::npos)
440
                    {
441
                        // IPv6 with a port has to be written as: [address]:port
442
                        std::string::size_type const end_of_address =
443
                            v.find_last_of(']');
444
                        if (end_of_address == std::string::npos)
445
                            return false;
446

447
                        tmp_host =
448
                            v.substr(begin_of_address + 1, end_of_address - 1);
449
                        if (end_of_address < p)
450
                        {
451
                            tmp_port = hpx::util::from_string<std::uint16_t>(
452
                                v.substr(p + 1));
453
                        }
454
                    }
455
                    else
456
                    {
457
                        // IPv6 without a port
458
                        tmp_host = v;
459
                    }
460
                }
461
                else
462
                {
463
                    // IPv4
464
                    tmp_host = v.substr(0, p);
465
                    tmp_port =
466
                        hpx::util::from_string<std::uint16_t>(v.substr(p + 1));
467
                }
468
            }
469
            else
470
            {
471
                tmp_host = v;
472
            }
473

474
            if (!tmp_host.empty())
475
            {
476
                host = tmp_host;
477
                if (tmp_port)
478
                    port = tmp_port;
479
            }
480
        }
481
        catch (hpx::util::bad_lexical_cast const& /*e*/)
482
        {
483
            // port number is invalid
484
            return false;
485
        }
486
        return true;
487
    }
488
}    // namespace hpx::util
489

490
#include <hpx/config/warnings_suffix.hpp>
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc